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