rdisc.c revision 19880
1/* 2 * Copyright (c) 1995 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) 35static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; 36#elif defined(__NetBSD__) 37static char rcsid[] = "$NetBSD$"; 38#endif 39#ident "$Revision: 1.17 $" 40 41#include "defs.h" 42#include <netinet/in_systm.h> 43#include <netinet/ip.h> 44#include <netinet/ip_icmp.h> 45 46/* router advertisement ICMP packet */ 47struct icmp_ad { 48 u_int8_t icmp_type; /* type of message */ 49 u_int8_t icmp_code; /* type sub code */ 50 u_int16_t icmp_cksum; /* ones complement cksum of struct */ 51 u_int8_t icmp_ad_num; /* # of following router addresses */ 52 u_int8_t icmp_ad_asize; /* 2--words in each advertisement */ 53 u_int16_t icmp_ad_life; /* seconds of validity */ 54 struct icmp_ad_info { 55 n_long icmp_ad_addr; 56 n_long icmp_ad_pref; 57 } icmp_ad_info[1]; 58}; 59 60/* router solicitation ICMP packet */ 61struct icmp_so { 62 u_int8_t icmp_type; /* type of message */ 63 u_int8_t icmp_code; /* type sub code */ 64 u_int16_t icmp_cksum; /* ones complement cksum of struct */ 65 n_long icmp_so_rsvd; 66}; 67 68union ad_u { 69 struct icmp icmp; 70 struct icmp_ad ad; 71 struct icmp_so so; 72}; 73 74 75int rdisc_sock = -1; /* router-discovery raw socket */ 76struct interface *rdisc_sock_mcast; /* current multicast interface */ 77 78struct timeval rdisc_timer; 79int rdisc_ok; /* using solicited route */ 80 81 82#define MAX_ADS 5 83struct dr { /* accumulated advertisements */ 84 struct interface *dr_ifp; 85 naddr dr_gate; /* gateway */ 86 time_t dr_ts; /* when received */ 87 time_t dr_life; /* lifetime */ 88 n_long dr_recv_pref; /* received but biased preference */ 89 n_long dr_pref; /* preference adjusted by metric */ 90} *cur_drp, drs[MAX_ADS]; 91 92/* adjust preference by interface metric without driving it to infinity */ 93#define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \ 94 : (p) - ((ifp)->int_metric)) 95 96static void rdisc_sort(void); 97 98 99/* dump an ICMP Router Discovery Advertisement Message 100 */ 101static void 102trace_rdisc(char *act, 103 naddr from, 104 naddr to, 105 struct interface *ifp, 106 union ad_u *p, 107 u_int len) 108{ 109 int i; 110 n_long *wp, *lim; 111 112 113 if (!TRACEPACKETS || ftrace == 0) 114 return; 115 116 lastlog(); 117 118 if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 119 (void)fprintf(ftrace, "%s Router Ad" 120 " from %s to %s via %s life=%d\n", 121 act, naddr_ntoa(from), naddr_ntoa(to), 122 ifp ? ifp->int_name : "?", 123 ntohs(p->ad.icmp_ad_life)); 124 if (!TRACECONTENTS) 125 return; 126 127 wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 128 lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)]; 129 for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { 130 (void)fprintf(ftrace, "\t%s preference=%#x", 131 naddr_ntoa(wp[0]), (int)ntohl(wp[1])); 132 wp += p->ad.icmp_ad_asize; 133 } 134 (void)fputc('\n',ftrace); 135 136 } else { 137 trace_act("%s Router Solic. from %s to %s via %s value=%#x", 138 act, naddr_ntoa(from), naddr_ntoa(to), 139 ifp ? ifp->int_name : "?", 140 ntohl(p->so.icmp_so_rsvd)); 141 } 142} 143 144/* prepare Router Discovery socket. 145 */ 146static void 147get_rdisc_sock(void) 148{ 149 if (rdisc_sock < 0) { 150 rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 151 if (rdisc_sock < 0) 152 BADERR(1,"rdisc_sock = socket()"); 153 fix_sock(rdisc_sock,"rdisc_sock"); 154 fix_select(); 155 } 156} 157 158 159/* Pick multicast group for router-discovery socket 160 */ 161void 162set_rdisc_mg(struct interface *ifp, 163 int on) /* 0=turn it off */ 164{ 165 struct ip_mreq m; 166 167 if (rdisc_sock < 0) { 168 /* Create the raw socket so that we can hear at least 169 * broadcast router discovery packets. 170 */ 171 if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC 172 || !on) 173 return; 174 get_rdisc_sock(); 175 } 176 177 if (!(ifp->int_if_flags & IFF_MULTICAST)) { 178 ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); 179 return; 180 } 181 182#ifdef MCAST_PPP_BUG 183 if (ifp->int_if_flags & IFF_POINTOPOINT) 184 return; 185#endif 186 bzero(&m, sizeof(m)); 187 m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) 188 ? ifp->int_dstaddr 189 : ifp->int_addr); 190 if (supplier 191 || (ifp->int_state & IS_NO_ADV_IN) 192 || !on) { 193 /* stop listening to advertisements 194 */ 195 if (ifp->int_state & IS_ALL_HOSTS) { 196 m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 197 if (setsockopt(rdisc_sock, IPPROTO_IP, 198 IP_DROP_MEMBERSHIP, 199 &m, sizeof(m)) < 0) 200 LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS"); 201 ifp->int_state &= ~IS_ALL_HOSTS; 202 } 203 204 } else if (!(ifp->int_state & IS_ALL_HOSTS)) { 205 /* start listening to advertisements 206 */ 207 m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 208 if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 209 &m, sizeof(m)) < 0) { 210 LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS"); 211 } else { 212 ifp->int_state |= IS_ALL_HOSTS; 213 } 214 } 215 216 if (!supplier 217 || (ifp->int_state & IS_NO_ADV_OUT) 218 || !on) { 219 /* stop listening to solicitations 220 */ 221 if (ifp->int_state & IS_ALL_ROUTERS) { 222 m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); 223 if (setsockopt(rdisc_sock, IPPROTO_IP, 224 IP_DROP_MEMBERSHIP, 225 &m, sizeof(m)) < 0) 226 LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS"); 227 ifp->int_state &= ~IS_ALL_ROUTERS; 228 } 229 230 } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { 231 /* start hearing solicitations 232 */ 233 m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); 234 if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 235 &m, sizeof(m)) < 0) { 236 LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS"); 237 } else { 238 ifp->int_state |= IS_ALL_ROUTERS; 239 } 240 } 241} 242 243 244/* start supplying routes 245 */ 246void 247set_supplier(void) 248{ 249 struct interface *ifp; 250 struct dr *drp; 251 252 if (supplier_set) 253 return; 254 255 trace_act("start suppying routes"); 256 257 /* Forget discovered routes. 258 */ 259 for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 260 drp->dr_recv_pref = 0; 261 drp->dr_life = 0; 262 } 263 rdisc_age(0); 264 265 supplier_set = 1; 266 supplier = 1; 267 268 /* Do not start advertising until we have heard some RIP routes */ 269 LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); 270 271 /* Switch router discovery multicast groups from soliciting 272 * to advertising. 273 */ 274 for (ifp = ifnet; ifp; ifp = ifp->int_next) { 275 if (ifp->int_state & IS_BROKE) 276 continue; 277 ifp->int_rdisc_cnt = 0; 278 ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; 279 ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; 280 set_rdisc_mg(ifp, 1); 281 } 282 283 /* get rid of any redirects */ 284 del_redirects(0,0); 285} 286 287 288/* age discovered routes and find the best one 289 */ 290void 291rdisc_age(naddr bad_gate) 292{ 293 time_t sec; 294 struct dr *drp; 295 296 297 /* If only adverising, then do only that. */ 298 if (supplier) { 299 /* if switching from client to server, get rid of old 300 * default routes. 301 */ 302 if (cur_drp != 0) 303 rdisc_sort(); 304 rdisc_adv(); 305 return; 306 } 307 308 /* If we are being told about a bad router, 309 * then age the discovered default route, and if there is 310 * no alternative, solicite a replacement. 311 */ 312 if (bad_gate != 0) { 313 /* Look for the bad discovered default route. 314 * Age it and note its interface. 315 */ 316 for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 317 if (drp->dr_ts == 0) 318 continue; 319 320 /* When we find the bad router, then age the route 321 * to at most SUPPLY_INTERVAL. 322 * This is contrary to RFC 1256, but defends against 323 * black holes. 324 */ 325 if (drp->dr_gate == bad_gate) { 326 sec = (now.tv_sec - drp->dr_life 327 + SUPPLY_INTERVAL); 328 if (drp->dr_ts > sec) { 329 trace_act("age 0.0.0.0 --> %s via %s", 330 naddr_ntoa(drp->dr_gate), 331 drp->dr_ifp->int_name); 332 drp->dr_ts = sec; 333 } 334 break; 335 } 336 } 337 } 338 339 /* delete old redirected routes to keep the kernel table small 340 */ 341 sec = (cur_drp == 0) ? MaxMaxAdvertiseInterval : cur_drp->dr_life; 342 del_redirects(bad_gate, now.tv_sec-sec); 343 344 rdisc_sol(); 345 346 rdisc_sort(); 347} 348 349 350/* Zap all routes discovered via an interface that has gone bad 351 * This should only be called when !(ifp->int_state & IS_ALIAS) 352 */ 353void 354if_bad_rdisc(struct interface *ifp) 355{ 356 struct dr *drp; 357 358 for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 359 if (drp->dr_ifp != ifp) 360 continue; 361 drp->dr_recv_pref = 0; 362 drp->dr_life = 0; 363 } 364 365 rdisc_sort(); 366} 367 368 369/* mark an interface ok for router discovering. 370 */ 371void 372if_ok_rdisc(struct interface *ifp) 373{ 374 set_rdisc_mg(ifp, 1); 375 376 ifp->int_rdisc_cnt = 0; 377 ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier 378 ? MIN_WAITTIME 379 : MAX_SOLICITATION_DELAY); 380 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 381 rdisc_timer = ifp->int_rdisc_timer; 382} 383 384 385/* get rid of a dead discovered router 386 */ 387static void 388del_rdisc(struct dr *drp) 389{ 390 struct interface *ifp; 391 int i; 392 393 394 del_redirects(drp->dr_gate, 0); 395 drp->dr_ts = 0; 396 drp->dr_life = 0; 397 398 399 /* Count the other discovered routes on the interface. 400 */ 401 i = 0; 402 ifp = drp->dr_ifp; 403 for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 404 if (drp->dr_ts != 0 405 && drp->dr_ifp == ifp) 406 i++; 407 } 408 409 /* If that was the last good discovered router on the interface, 410 * then solicit a new one. 411 * This is contrary to RFC 1256, but defends against black holes. 412 */ 413 if (i == 0 414 && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { 415 trace_act("discovered route is bad--re-solicit routers via %s", 416 ifp->int_name); 417 ifp->int_rdisc_cnt = 0; 418 ifp->int_rdisc_timer.tv_sec = 0; 419 rdisc_sol(); 420 } 421} 422 423 424/* Find the best discovered route, 425 * and discard stale routers. 426 */ 427static void 428rdisc_sort(void) 429{ 430 struct dr *drp, *new_drp; 431 struct rt_entry *rt; 432 struct interface *ifp; 433 u_int new_st; 434 n_long new_pref; 435 436 437 /* Find the best discovered route. 438 */ 439 new_drp = 0; 440 for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 441 if (drp->dr_ts == 0) 442 continue; 443 ifp = drp->dr_ifp; 444 445 /* Get rid of expired discovered routers. 446 */ 447 if (drp->dr_ts + drp->dr_life <= now.tv_sec) { 448 del_rdisc(drp); 449 continue; 450 } 451 452 LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); 453 454 /* Update preference with possibly changed interface 455 * metric. 456 */ 457 drp->dr_pref = PREF(drp->dr_recv_pref, ifp); 458 459 /* Prefer the current route to prevent thrashing. 460 * Prefer shorter lifetimes to speed the detection of 461 * bad routers. 462 * Avoid sick interfaces. 463 */ 464 if (new_drp == 0 465 || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) 466 && (new_pref < drp->dr_pref 467 || (new_pref == drp->dr_pref 468 && (drp == cur_drp 469 || (new_drp != cur_drp 470 && new_drp->dr_life > drp->dr_life))))) 471 || ((new_st & IS_SICK) 472 && !(drp->dr_ifp->int_state & IS_SICK))) { 473 new_drp = drp; 474 new_st = drp->dr_ifp->int_state; 475 new_pref = drp->dr_pref; 476 } 477 } 478 479 /* switch to a better default route 480 */ 481 if (new_drp != cur_drp) { 482 rt = rtget(RIP_DEFAULT, 0); 483 484 /* Stop using discovered routes if they are all bad 485 */ 486 if (new_drp == 0) { 487 trace_act("turn off Router Discovery client"); 488 rdisc_ok = 0; 489 490 if (rt != 0 491 && (rt->rt_state & RS_RDISC)) { 492 rtchange(rt, rt->rt_state & ~RS_RDISC, 493 rt->rt_gate, rt->rt_router, 494 HOPCNT_INFINITY, 0, rt->rt_ifp, 495 now.tv_sec - GARBAGE_TIME, 0); 496 rtswitch(rt, 0); 497 } 498 499 /* turn on RIP if permitted */ 500 rip_on(0); 501 502 } else { 503 if (cur_drp == 0) { 504 trace_act("turn on Router Discovery client" 505 " using %s via %s", 506 naddr_ntoa(new_drp->dr_gate), 507 new_drp->dr_ifp->int_name); 508 509 rdisc_ok = 1; 510 511 } else { 512 trace_act("switch Router Discovery from" 513 " %s via %s to %s via %s", 514 naddr_ntoa(cur_drp->dr_gate), 515 cur_drp->dr_ifp->int_name, 516 naddr_ntoa(new_drp->dr_gate), 517 new_drp->dr_ifp->int_name); 518 } 519 520 if (rt != 0) { 521 rtchange(rt, rt->rt_state | RS_RDISC, 522 new_drp->dr_gate, new_drp->dr_gate, 523 0,0, new_drp->dr_ifp, 524 now.tv_sec, 0); 525 } else { 526 rtadd(RIP_DEFAULT, 0, 527 new_drp->dr_gate, new_drp->dr_gate, 528 HOPCNT_INFINITY-1, 0, 529 RS_RDISC, new_drp->dr_ifp); 530 } 531 532 /* Now turn off RIP and delete RIP routes, 533 * which might otherwise include the default 534 * we just modified. 535 */ 536 rip_off(); 537 } 538 539 cur_drp = new_drp; 540 } 541} 542 543 544/* handle a single address in an advertisement 545 */ 546static void 547parse_ad(naddr from, 548 naddr gate, 549 n_long pref, 550 u_short life, 551 struct interface *ifp) 552{ 553 static struct msg_limit bad_gate; 554 struct dr *drp, *new_drp; 555 556 557 if (gate == RIP_DEFAULT 558 || !check_dst(gate)) { 559 msglim(&bad_gate, from,"router %s advertising bad gateway %s", 560 naddr_ntoa(from), 561 naddr_ntoa(gate)); 562 return; 563 } 564 565 /* ignore pointers to ourself and routes via unreachable networks 566 */ 567 if (ifwithaddr(gate, 1, 0) != 0) { 568 trace_pkt(" discard Router Discovery Ad pointing at us"); 569 return; 570 } 571 if (!on_net(gate, ifp->int_net, ifp->int_mask)) { 572 trace_pkt(" discard Router Discovery Ad" 573 " toward unreachable net"); 574 return; 575 } 576 577 /* Convert preference to an unsigned value 578 * and later bias it by the metric of the interface. 579 */ 580 pref = ntohl(pref) ^ MIN_PreferenceLevel; 581 582 if (pref == 0 || life == 0) { 583 pref = 0; 584 life = 0; 585 } 586 587 for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { 588 /* accept new info for a familiar entry 589 */ 590 if (drp->dr_gate == gate) { 591 new_drp = drp; 592 break; 593 } 594 595 if (life == 0) 596 continue; /* do not worry about dead ads */ 597 598 if (drp->dr_ts == 0) { 599 new_drp = drp; /* use unused entry */ 600 601 } else if (new_drp == 0) { 602 /* look for an entry worse than the new one to 603 * reuse. 604 */ 605 if ((!(ifp->int_state & IS_SICK) 606 && (drp->dr_ifp->int_state & IS_SICK)) 607 || (pref > drp->dr_pref 608 && !((ifp->int_state ^ drp->dr_ifp->int_state) 609 & IS_SICK))) 610 new_drp = drp; 611 612 } else if (new_drp->dr_ts != 0) { 613 /* look for the least valueable entry to reuse 614 */ 615 if ((!(new_drp->dr_ifp->int_state & IS_SICK) 616 && (drp->dr_ifp->int_state & IS_SICK)) 617 || (new_drp->dr_pref > drp->dr_pref 618 && !((new_drp->dr_ifp->int_state 619 ^ drp->dr_ifp->int_state) 620 & IS_SICK))) 621 new_drp = drp; 622 } 623 } 624 625 /* forget it if all of the current entries are better */ 626 if (new_drp == 0) 627 return; 628 629 new_drp->dr_ifp = ifp; 630 new_drp->dr_gate = gate; 631 new_drp->dr_ts = now.tv_sec; 632 new_drp->dr_life = ntohs(life); 633 new_drp->dr_recv_pref = pref; 634 /* bias functional preference by metric of the interface */ 635 new_drp->dr_pref = PREF(pref,ifp); 636 637 /* after hearing a good advertisement, stop asking 638 */ 639 if (!(ifp->int_state & IS_SICK)) 640 ifp->int_rdisc_cnt = MAX_SOLICITATIONS; 641} 642 643 644/* Compute the IP checksum 645 * This assumes the packet is less than 32K long. 646 */ 647static u_short 648in_cksum(u_short *p, 649 u_int len) 650{ 651 u_int sum = 0; 652 int nwords = len >> 1; 653 654 while (nwords-- != 0) 655 sum += *p++; 656 657 if (len & 1) 658 sum += *(u_char *)p; 659 660 /* end-around-carry */ 661 sum = (sum >> 16) + (sum & 0xffff); 662 sum += (sum >> 16); 663 return (~sum); 664} 665 666 667/* Send a router discovery advertisement or solicitation ICMP packet. 668 */ 669static void 670send_rdisc(union ad_u *p, 671 int p_size, 672 struct interface *ifp, 673 naddr dst, /* 0 or unicast destination */ 674 int type) /* 0=unicast, 1=bcast, 2=mcast */ 675{ 676 struct sockaddr_in sin; 677 int flags; 678 char *msg; 679 naddr tgt_mcast; 680 681 682 bzero(&sin, sizeof(sin)); 683 sin.sin_addr.s_addr = dst; 684 sin.sin_family = AF_INET; 685#ifdef _HAVE_SIN_LEN 686 sin.sin_len = sizeof(sin); 687#endif 688 flags = MSG_DONTROUTE; 689 690 switch (type) { 691 case 0: /* unicast */ 692 msg = "Send"; 693 break; 694 695 case 1: /* broadcast */ 696 if (ifp->int_if_flags & IFF_POINTOPOINT) { 697 msg = "Send pt-to-pt"; 698 sin.sin_addr.s_addr = ifp->int_dstaddr; 699 } else { 700 msg = "Send broadcast"; 701 sin.sin_addr.s_addr = ifp->int_brdaddr; 702 } 703 break; 704 705 case 2: /* multicast */ 706 msg = "Send multicast"; 707 if (ifp->int_state & IS_DUP) { 708 trace_act("abort multicast output via %s" 709 " with duplicate address", 710 ifp->int_name); 711 return; 712 } 713 if (rdisc_sock_mcast != ifp) { 714 /* select the right interface. */ 715#ifdef MCAST_PPP_BUG 716 /* Do not specifiy the primary interface explicitly 717 * if we have the multicast point-to-point kernel 718 * bug, since the kernel will do the wrong thing 719 * if the local address of a point-to-point link 720 * is the same as the address of an ordinary 721 * interface. 722 */ 723 if (ifp->int_addr == myaddr) { 724 tgt_mcast = 0; 725 } else 726#endif 727 tgt_mcast = ifp->int_addr; 728 if (0 > setsockopt(rdisc_sock, 729 IPPROTO_IP, IP_MULTICAST_IF, 730 &tgt_mcast, sizeof(tgt_mcast))) { 731 LOGERR("setsockopt(rdisc_sock," 732 "IP_MULTICAST_IF)"); 733 rdisc_sock_mcast = 0; 734 return; 735 } 736 rdisc_sock_mcast = ifp; 737 } 738 flags = 0; 739 break; 740 } 741 742 if (rdisc_sock < 0) 743 get_rdisc_sock(); 744 745 trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, 746 p, p_size); 747 748 if (0 > sendto(rdisc_sock, p, p_size, flags, 749 (struct sockaddr *)&sin, sizeof(sin))) { 750 if (ifp == 0 || !(ifp->int_state & IS_BROKE)) 751 msglog("sendto(%s%s%s): %s", 752 ifp != 0 ? ifp->int_name : "", 753 ifp != 0 ? ", " : "", 754 inet_ntoa(sin.sin_addr), 755 strerror(errno)); 756 if (ifp != 0) 757 if_sick(ifp); 758 } 759} 760 761 762/* Send an advertisement 763 */ 764static void 765send_adv(struct interface *ifp, 766 naddr dst, /* 0 or unicast destination */ 767 int type) /* 0=unicast, 1=bcast, 2=mcast */ 768{ 769 union ad_u u; 770 n_long pref; 771 772 773 bzero(&u,sizeof(u.ad)); 774 775 u.ad.icmp_type = ICMP_ROUTERADVERT; 776 u.ad.icmp_ad_num = 1; 777 u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; 778 779 u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); 780 pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel; 781 pref = PREF(pref, ifp) ^ MIN_PreferenceLevel; 782 u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref); 783 784 u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; 785 786 u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); 787 788 send_rdisc(&u, sizeof(u.ad), ifp, dst, type); 789} 790 791 792/* Advertise for Router Discovery 793 */ 794void 795rdisc_adv(void) 796{ 797 struct interface *ifp; 798 799 if (!supplier) 800 return; 801 802 rdisc_timer.tv_sec = now.tv_sec + NEVER; 803 804 for (ifp = ifnet; ifp; ifp = ifp->int_next) { 805 if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE))) 806 continue; 807 808 if (!timercmp(&ifp->int_rdisc_timer, &now, >) 809 || stopint) { 810 send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), 811 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); 812 ifp->int_rdisc_cnt++; 813 814 intvl_random(&ifp->int_rdisc_timer, 815 (ifp->int_rdisc_int*3)/4, 816 ifp->int_rdisc_int); 817 if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS 818 && (ifp->int_rdisc_timer.tv_sec 819 > MAX_INITIAL_ADVERT_INTERVAL)) { 820 ifp->int_rdisc_timer.tv_sec 821 = MAX_INITIAL_ADVERT_INTERVAL; 822 } 823 timevaladd(&ifp->int_rdisc_timer, &now); 824 } 825 826 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 827 rdisc_timer = ifp->int_rdisc_timer; 828 } 829} 830 831 832/* Solicit for Router Discovery 833 */ 834void 835rdisc_sol(void) 836{ 837 struct interface *ifp; 838 union ad_u u; 839 840 841 if (supplier) 842 return; 843 844 rdisc_timer.tv_sec = now.tv_sec + NEVER; 845 846 for (ifp = ifnet; ifp; ifp = ifp->int_next) { 847 if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) 848 || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 849 continue; 850 851 if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { 852 bzero(&u,sizeof(u.so)); 853 u.so.icmp_type = ICMP_ROUTERSOLICIT; 854 u.so.icmp_cksum = in_cksum((u_short*)&u.so, 855 sizeof(u.so)); 856 send_rdisc(&u, sizeof(u.so), ifp, 857 htonl(INADDR_ALLROUTERS_GROUP), 858 ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); 859 860 if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 861 continue; 862 863 ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; 864 ifp->int_rdisc_timer.tv_usec = 0; 865 timevaladd(&ifp->int_rdisc_timer, &now); 866 } 867 868 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 869 rdisc_timer = ifp->int_rdisc_timer; 870 } 871} 872 873 874/* check the IP header of a possible Router Discovery ICMP packet */ 875static struct interface * /* 0 if bad */ 876ck_icmp(char *act, 877 naddr from, 878 struct interface *ifp, 879 naddr to, 880 union ad_u *p, 881 u_int len) 882{ 883 char *type; 884 885 886 if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 887 type = "advertisement"; 888 } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { 889 type = "solicitation"; 890 } else { 891 return 0; 892 } 893 894 if (p->icmp.icmp_code != 0) { 895 trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s", 896 type, p->icmp.icmp_code, 897 naddr_ntoa(from), naddr_ntoa(to)); 898 return 0; 899 } 900 901 trace_rdisc(act, from, to, ifp, p, len); 902 903 if (ifp == 0) 904 trace_pkt("unknown interface for router-discovery %s" 905 " from %s to %s", 906 type, naddr_ntoa(from), naddr_ntoa(to)); 907 908 return ifp; 909} 910 911 912/* read packets from the router discovery socket 913 */ 914void 915read_d(void) 916{ 917 static struct msg_limit bad_asize, bad_len; 918 struct sockaddr_in from; 919 int n, fromlen, cc, hlen; 920 struct { 921#undef USE_PASSIFNAME /* it is too bad it does not work on raw sockets */ 922#ifdef USE_PASSIFNAME 923 char ifname[IFNAMSIZ]; 924#endif 925 union { 926 struct ip ip; 927 u_short s[512/2]; 928 u_char b[512]; 929 } pkt; 930 } buf; 931 union ad_u *p; 932 n_long *wp; 933 struct interface *ifp; 934 935 936 for (;;) { 937 fromlen = sizeof(from); 938 cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0, 939 (struct sockaddr*)&from, 940 &fromlen); 941 if (cc <= 0) { 942 if (cc < 0 && errno != EWOULDBLOCK) 943 LOGERR("recvfrom(rdisc_sock)"); 944 break; 945 } 946 if (fromlen != sizeof(struct sockaddr_in)) 947 logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", 948 fromlen); 949#ifdef USE_PASSIFNAME 950 if ((cc -= sizeof(buf.ifname)) < 0) 951 logbad(0,"missing USE_PASSIFNAME; only %d bytes", 952 cc+sizeof(buf.ifname)); 953#endif 954 955 hlen = buf.pkt.ip.ip_hl << 2; 956 if (cc < hlen + ICMP_MINLEN) 957 continue; 958 p = (union ad_u *)&buf.pkt.b[hlen]; 959 cc -= hlen; 960 961#ifdef USE_PASSIFNAME 962 ifp = ifwithname(buf.ifname, 0); 963 if (ifp == 0) { 964 /* maybe it is a new interface */ 965 ifinit(); 966 ifp = ifwithname(buf.ifname, 0); 967 if (ifp == 0) { 968 msglim(&bad_name, from.sin_addr.s_addr, 969 "impossible rdisc if_ name %.*s", 970 IFNAMSIZ, buf.ifname); 971 } 972 } 973#else 974 /* If we could tell the interface on which a packet from 975 * address 0 arrived, we could deal with such solicitations. 976 */ 977 ifp = ((from.sin_addr.s_addr == 0) 978 ? 0 : iflookup(from.sin_addr.s_addr)); 979#endif 980 ifp = ck_icmp("Recv", 981 from.sin_addr.s_addr, ifp, 982 buf.pkt.ip.ip_dst.s_addr, 983 p, cc); 984 if (ifp == 0) 985 continue; 986 if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { 987 trace_pkt(" discard our own Router Discovery" 988 " message"); 989 continue; 990 } 991 992 switch (p->icmp.icmp_type) { 993 case ICMP_ROUTERADVERT: 994 if (p->ad.icmp_ad_asize*4 995 < sizeof(p->ad.icmp_ad_info[0])) { 996 msglim(&bad_asize, from.sin_addr.s_addr, 997 "intolerable rdisc address size=%d", 998 p->ad.icmp_ad_asize); 999 continue; 1000 } 1001 if (p->ad.icmp_ad_num == 0) { 1002 trace_pkt(" empty?"); 1003 continue; 1004 } 1005 if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) 1006 + (p->ad.icmp_ad_num 1007 * sizeof(p->ad.icmp_ad_info[0])))) { 1008 msglim(&bad_len, from.sin_addr.s_addr, 1009 "rdisc length %d does not match ad_num" 1010 " %d", cc, p->ad.icmp_ad_num); 1011 continue; 1012 } 1013 if (supplier) 1014 continue; 1015 if (ifp->int_state & IS_NO_ADV_IN) 1016 continue; 1017 1018 wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 1019 for (n = 0; n < p->ad.icmp_ad_num; n++) { 1020 parse_ad(from.sin_addr.s_addr, 1021 wp[0], wp[1], 1022 ntohs(p->ad.icmp_ad_life), 1023 ifp); 1024 wp += p->ad.icmp_ad_asize; 1025 } 1026 break; 1027 1028 1029 case ICMP_ROUTERSOLICIT: 1030 if (!supplier) 1031 continue; 1032 if (ifp->int_state & IS_NO_ADV_OUT) 1033 continue; 1034 1035 /* XXX 1036 * We should handle messages from address 0. 1037 */ 1038 1039 /* Respond with a point-to-point advertisement */ 1040 send_adv(ifp, from.sin_addr.s_addr, 0); 1041 break; 1042 } 1043 } 1044 1045 rdisc_sort(); 1046} 1047