rdisc.c revision 20606
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.20 $" 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=%d", 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 default: 693 msg = "Send"; 694 break; 695 696 case 1: /* broadcast */ 697 if (ifp->int_if_flags & IFF_POINTOPOINT) { 698 msg = "Send pt-to-pt"; 699 sin.sin_addr.s_addr = ifp->int_dstaddr; 700 } else { 701 msg = "Send broadcast"; 702 sin.sin_addr.s_addr = ifp->int_brdaddr; 703 } 704 break; 705 706 case 2: /* multicast */ 707 msg = "Send multicast"; 708 if (ifp->int_state & IS_DUP) { 709 trace_act("abort multicast output via %s" 710 " with duplicate address", 711 ifp->int_name); 712 return; 713 } 714 if (rdisc_sock_mcast != ifp) { 715 /* select the right interface. */ 716#ifdef MCAST_PPP_BUG 717 /* Do not specifiy the primary interface explicitly 718 * if we have the multicast point-to-point kernel 719 * bug, since the kernel will do the wrong thing 720 * if the local address of a point-to-point link 721 * is the same as the address of an ordinary 722 * interface. 723 */ 724 if (ifp->int_addr == myaddr) { 725 tgt_mcast = 0; 726 } else 727#endif 728 tgt_mcast = ifp->int_addr; 729 if (0 > setsockopt(rdisc_sock, 730 IPPROTO_IP, IP_MULTICAST_IF, 731 &tgt_mcast, sizeof(tgt_mcast))) { 732 LOGERR("setsockopt(rdisc_sock," 733 "IP_MULTICAST_IF)"); 734 rdisc_sock_mcast = 0; 735 return; 736 } 737 rdisc_sock_mcast = ifp; 738 } 739 flags = 0; 740 break; 741 } 742 743 if (rdisc_sock < 0) 744 get_rdisc_sock(); 745 746 trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, 747 p, p_size); 748 749 if (0 > sendto(rdisc_sock, p, p_size, flags, 750 (struct sockaddr *)&sin, sizeof(sin))) { 751 if (ifp == 0 || !(ifp->int_state & IS_BROKE)) 752 msglog("sendto(%s%s%s): %s", 753 ifp != 0 ? ifp->int_name : "", 754 ifp != 0 ? ", " : "", 755 inet_ntoa(sin.sin_addr), 756 strerror(errno)); 757 if (ifp != 0) 758 if_sick(ifp); 759 } 760} 761 762 763/* Send an advertisement 764 */ 765static void 766send_adv(struct interface *ifp, 767 naddr dst, /* 0 or unicast destination */ 768 int type) /* 0=unicast, 1=bcast, 2=mcast */ 769{ 770 union ad_u u; 771 n_long pref; 772 773 774 bzero(&u,sizeof(u.ad)); 775 776 u.ad.icmp_type = ICMP_ROUTERADVERT; 777 u.ad.icmp_ad_num = 1; 778 u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; 779 780 u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); 781 pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel; 782 pref = PREF(pref, ifp) ^ MIN_PreferenceLevel; 783 u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref); 784 785 u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; 786 787 u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); 788 789 send_rdisc(&u, sizeof(u.ad), ifp, dst, type); 790} 791 792 793/* Advertise for Router Discovery 794 */ 795void 796rdisc_adv(void) 797{ 798 struct interface *ifp; 799 800 if (!supplier) 801 return; 802 803 rdisc_timer.tv_sec = now.tv_sec + NEVER; 804 805 for (ifp = ifnet; ifp; ifp = ifp->int_next) { 806 if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE))) 807 continue; 808 809 if (!timercmp(&ifp->int_rdisc_timer, &now, >) 810 || stopint) { 811 send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), 812 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); 813 ifp->int_rdisc_cnt++; 814 815 intvl_random(&ifp->int_rdisc_timer, 816 (ifp->int_rdisc_int*3)/4, 817 ifp->int_rdisc_int); 818 if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS 819 && (ifp->int_rdisc_timer.tv_sec 820 > MAX_INITIAL_ADVERT_INTERVAL)) { 821 ifp->int_rdisc_timer.tv_sec 822 = MAX_INITIAL_ADVERT_INTERVAL; 823 } 824 timevaladd(&ifp->int_rdisc_timer, &now); 825 } 826 827 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 828 rdisc_timer = ifp->int_rdisc_timer; 829 } 830} 831 832 833/* Solicit for Router Discovery 834 */ 835void 836rdisc_sol(void) 837{ 838 struct interface *ifp; 839 union ad_u u; 840 841 842 if (supplier) 843 return; 844 845 rdisc_timer.tv_sec = now.tv_sec + NEVER; 846 847 for (ifp = ifnet; ifp; ifp = ifp->int_next) { 848 if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) 849 || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 850 continue; 851 852 if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { 853 bzero(&u,sizeof(u.so)); 854 u.so.icmp_type = ICMP_ROUTERSOLICIT; 855 u.so.icmp_cksum = in_cksum((u_short*)&u.so, 856 sizeof(u.so)); 857 send_rdisc(&u, sizeof(u.so), ifp, 858 htonl(INADDR_ALLROUTERS_GROUP), 859 ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); 860 861 if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 862 continue; 863 864 ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; 865 ifp->int_rdisc_timer.tv_usec = 0; 866 timevaladd(&ifp->int_rdisc_timer, &now); 867 } 868 869 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 870 rdisc_timer = ifp->int_rdisc_timer; 871 } 872} 873 874 875/* check the IP header of a possible Router Discovery ICMP packet */ 876static struct interface * /* 0 if bad */ 877ck_icmp(char *act, 878 naddr from, 879 struct interface *ifp, 880 naddr to, 881 union ad_u *p, 882 u_int len) 883{ 884 char *type; 885 886 887 if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 888 type = "advertisement"; 889 } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { 890 type = "solicitation"; 891 } else { 892 return 0; 893 } 894 895 if (p->icmp.icmp_code != 0) { 896 trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s", 897 type, p->icmp.icmp_code, 898 naddr_ntoa(from), naddr_ntoa(to)); 899 return 0; 900 } 901 902 trace_rdisc(act, from, to, ifp, p, len); 903 904 if (ifp == 0) 905 trace_pkt("unknown interface for router-discovery %s" 906 " from %s to %s", 907 type, naddr_ntoa(from), naddr_ntoa(to)); 908 909 return ifp; 910} 911 912 913/* read packets from the router discovery socket 914 */ 915void 916read_d(void) 917{ 918 static struct msg_limit bad_asize, bad_len; 919#ifdef USE_PASSIFNAME 920 static struct msg_limit bad_name; 921#endif 922 struct sockaddr_in from; 923 int n, fromlen, cc, hlen; 924 struct { 925#ifdef USE_PASSIFNAME 926 char ifname[IFNAMSIZ]; 927#endif 928 union { 929 struct ip ip; 930 u_short s[512/2]; 931 u_char b[512]; 932 } pkt; 933 } buf; 934 union ad_u *p; 935 n_long *wp; 936 struct interface *ifp; 937 938 939 for (;;) { 940 fromlen = sizeof(from); 941 cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0, 942 (struct sockaddr*)&from, 943 &fromlen); 944 if (cc <= 0) { 945 if (cc < 0 && errno != EWOULDBLOCK) 946 LOGERR("recvfrom(rdisc_sock)"); 947 break; 948 } 949 if (fromlen != sizeof(struct sockaddr_in)) 950 logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", 951 fromlen); 952#ifdef USE_PASSIFNAME 953 if ((cc -= sizeof(buf.ifname)) < 0) 954 logbad(0,"missing USE_PASSIFNAME; only %d bytes", 955 cc+sizeof(buf.ifname)); 956#endif 957 958 hlen = buf.pkt.ip.ip_hl << 2; 959 if (cc < hlen + ICMP_MINLEN) 960 continue; 961 p = (union ad_u *)&buf.pkt.b[hlen]; 962 cc -= hlen; 963 964#ifdef USE_PASSIFNAME 965 ifp = ifwithname(buf.ifname, 0); 966 if (ifp == 0) 967 msglim(&bad_name, from.sin_addr.s_addr, 968 "impossible rdisc if_ name %.*s", 969 IFNAMSIZ, buf.ifname); 970#else 971 /* If we could tell the interface on which a packet from 972 * address 0 arrived, we could deal with such solicitations. 973 */ 974 ifp = ((from.sin_addr.s_addr == 0) 975 ? 0 : iflookup(from.sin_addr.s_addr)); 976#endif 977 ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp, 978 buf.pkt.ip.ip_dst.s_addr, p, cc); 979 if (ifp == 0) 980 continue; 981 if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { 982 trace_pkt(" " 983 "discard our own Router Discovery message"); 984 continue; 985 } 986 987 switch (p->icmp.icmp_type) { 988 case ICMP_ROUTERADVERT: 989 if (p->ad.icmp_ad_asize*4 990 < sizeof(p->ad.icmp_ad_info[0])) { 991 msglim(&bad_asize, from.sin_addr.s_addr, 992 "intolerable rdisc address size=%d", 993 p->ad.icmp_ad_asize); 994 continue; 995 } 996 if (p->ad.icmp_ad_num == 0) { 997 trace_pkt(" empty?"); 998 continue; 999 } 1000 if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) 1001 + (p->ad.icmp_ad_num 1002 * sizeof(p->ad.icmp_ad_info[0])))) { 1003 msglim(&bad_len, from.sin_addr.s_addr, 1004 "rdisc length %d does not match ad_num" 1005 " %d", cc, p->ad.icmp_ad_num); 1006 continue; 1007 } 1008 if (supplier) 1009 continue; 1010 if (ifp->int_state & IS_NO_ADV_IN) 1011 continue; 1012 1013 wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 1014 for (n = 0; n < p->ad.icmp_ad_num; n++) { 1015 parse_ad(from.sin_addr.s_addr, 1016 wp[0], wp[1], 1017 ntohs(p->ad.icmp_ad_life), 1018 ifp); 1019 wp += p->ad.icmp_ad_asize; 1020 } 1021 break; 1022 1023 1024 case ICMP_ROUTERSOLICIT: 1025 if (!supplier) 1026 continue; 1027 if (ifp->int_state & IS_NO_ADV_OUT) 1028 continue; 1029 1030 /* XXX 1031 * We should handle messages from address 0. 1032 */ 1033 1034 /* Respond with a point-to-point advertisement */ 1035 send_adv(ifp, from.sin_addr.s_addr, 0); 1036 break; 1037 } 1038 } 1039 1040 rdisc_sort(); 1041} 1042