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