1 /* 2 * The mrouted program is covered by the license in the accompanying file 3 * named "LICENSE". Use of the mrouted program represents acceptance of 4 * the terms and conditions listed in that file. 5 * 6 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of 7 * Leland Stanford Junior University. 8 * 9 * 10 * from: Id: route.c,v 1.5 1993/06/24 05:11:16 deering Exp 11 * $Id: route.c,v 1.2 1994/05/08 15:08:56 brezak Exp $ 12 */ 13 14 #ifndef lint 15 static char rcsid[] = "$Id: route.c,v 1.2 1994/05/08 15:08:56 brezak Exp $"; 16 #endif 17 18 #include "defs.h" 19 20 21 /* 22 * Exported variables. 23 */ 24 int routes_changed; /* 1=>some routes have changed */ 25 int delay_change_reports; /* 1=>postpone change reports */ 26 27 28 /* 29 * Private variables. 30 */ 31 static struct rtentry *routing_table; /* pointer to list of route entries */ 32 static struct rtentry *rtp; /* pointer to a route entry */ 33 unsigned nroutes; /* current number of route entries */ 34 35 36 /* 37 * Initialize the routing table and associated variables. 38 */ 39 void init_routes() 40 { 41 routing_table = NULL; 42 nroutes = 0; 43 routes_changed = FALSE; 44 delay_change_reports = FALSE; 45 } 46 47 48 /* 49 * Initialize the children and leaf bits for route 'r', along with the 50 * associated dominant, subordinate, and leaf timing data structures. 51 * Return TRUE if this changes the value of either the children or 52 * leaf bitmaps for 'r'. 53 */ 54 static int init_children_and_leaves(r, parent) 55 register struct rtentry *r; 56 register vifi_t parent; 57 { 58 register vifi_t vifi; 59 register struct uvif *v; 60 vifbitmap_t old_children, old_leaves; 61 62 VIFM_COPY(r->rt_children, old_children); 63 VIFM_COPY(r->rt_leaves, old_leaves ); 64 65 VIFM_CLRALL(r->rt_children); 66 VIFM_CLRALL(r->rt_leaves); 67 r->rt_flags &= ~RTF_LEAF_TIMING; 68 69 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { 70 r->rt_dominants [vifi] = 0; 71 r->rt_subordinates[vifi] = 0; 72 73 if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { 74 VIFM_SET(vifi, r->rt_children); 75 if (v->uv_neighbors == NULL) { 76 VIFM_SET(vifi, r->rt_leaves); 77 r->rt_leaf_timers[vifi] = 0; 78 } 79 else { 80 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; 81 r->rt_flags |= RTF_LEAF_TIMING; 82 } 83 } 84 else { 85 r->rt_leaf_timers[vifi] = 0; 86 } 87 } 88 89 return (!VIFM_SAME(r->rt_children, old_children) || 90 !VIFM_SAME(r->rt_leaves, old_leaves)); 91 } 92 93 94 /* 95 * A new vif has come up -- update the children and leaf bitmaps in all route 96 * entries to take that into account. 97 */ 98 void add_vif_to_routes(vifi) 99 register vifi_t vifi; 100 { 101 register struct rtentry *r; 102 register struct uvif *v; 103 104 v = &uvifs[vifi]; 105 for (r = routing_table; r != NULL; r = r->rt_next) { 106 if (r->rt_metric != UNREACHABLE && 107 !VIFM_ISSET(vifi, r->rt_children)) { 108 VIFM_SET(vifi, r->rt_children); 109 r->rt_dominants [vifi] = 0; 110 r->rt_subordinates[vifi] = 0; 111 if (v->uv_neighbors == NULL) { 112 VIFM_SET(vifi, r->rt_leaves); 113 r->rt_leaf_timers[vifi] = 0; 114 } 115 else { 116 VIFM_CLR(vifi, r->rt_leaves); 117 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; 118 r->rt_flags |= RTF_LEAF_TIMING; 119 } 120 k_update_route(r); 121 } 122 } 123 } 124 125 126 /* 127 * A vif has gone down -- expire all routes that have that vif as parent, 128 * and update the children bitmaps in all other route entries to take into 129 * account the failed vif. 130 */ 131 void delete_vif_from_routes(vifi) 132 register vifi_t vifi; 133 { 134 register struct rtentry *r; 135 136 for (r = routing_table; r != NULL; r = r->rt_next) { 137 if (r->rt_metric != UNREACHABLE) { 138 if (vifi == r->rt_parent) { 139 k_del_route(r); 140 r->rt_timer = ROUTE_EXPIRE_TIME; 141 r->rt_metric = UNREACHABLE; 142 r->rt_flags |= RTF_CHANGED; 143 routes_changed = TRUE; 144 } 145 else if (VIFM_ISSET(vifi, r->rt_children)) { 146 VIFM_CLR(vifi, r->rt_children); 147 VIFM_CLR(vifi, r->rt_leaves); 148 r->rt_subordinates[vifi] = 0; 149 r->rt_leaf_timers [vifi] = 0; 150 k_update_route(r); 151 } 152 else { 153 r->rt_dominants[vifi] = 0; 154 } 155 } 156 } 157 } 158 159 160 /* 161 * A neighbor has failed or become unreachable. If that neighbor was 162 * considered a dominant or subordinate router in any route entries, 163 * take appropriate action. 164 */ 165 void delete_neighbor_from_routes(addr, vifi) 166 register u_long addr; 167 register vifi_t vifi; 168 { 169 register struct rtentry *r; 170 register struct uvif *v; 171 172 v = &uvifs[vifi]; 173 for (r = routing_table; r != NULL; r = r->rt_next) { 174 if (r->rt_metric != UNREACHABLE) { 175 if (r->rt_dominants[vifi] == addr) { 176 VIFM_SET(vifi, r->rt_children); 177 r->rt_dominants [vifi] = 0; 178 r->rt_subordinates[vifi] = 0; 179 if (v->uv_neighbors == NULL) { 180 VIFM_SET(vifi, r->rt_leaves); 181 r->rt_leaf_timers[vifi] = 0; 182 } 183 else { 184 VIFM_CLR(vifi, r->rt_leaves); 185 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; 186 r->rt_flags |= RTF_LEAF_TIMING; 187 } 188 k_update_route(r); 189 } 190 else if (r->rt_subordinates[vifi] == addr) { 191 r->rt_subordinates[vifi] = 0; 192 if (v->uv_neighbors == NULL) { 193 VIFM_SET(vifi, r->rt_leaves); 194 k_update_route(r); 195 } 196 else { 197 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; 198 r->rt_flags |= RTF_LEAF_TIMING; 199 } 200 } 201 else if (v->uv_neighbors == NULL && 202 r->rt_leaf_timers[vifi] != 0) { 203 VIFM_SET(vifi, r->rt_leaves); 204 r->rt_leaf_timers[vifi] = 0; 205 k_update_route(r); 206 } 207 } 208 } 209 } 210 211 212 /* 213 * Prepare for a sequence of ordered route updates by initializing a pointer 214 * to the start of the routing table. The pointer is used to remember our 215 * position in the routing table in order to avoid searching from the 216 * beginning for each update; this relies on having the route reports in 217 * a single message be in the same order as the route entries in the routing 218 * table. 219 */ 220 void start_route_updates() 221 { 222 rtp = (struct rtentry *)&routing_table; 223 } 224 225 226 /* 227 * Starting at the route entry following the one to which 'rtp' points, 228 * look for a route entry matching the specified origin and mask. If a 229 * match is found, return TRUE and leave 'rtp' pointing at the found entry. 230 * If no match is found, return FALSE and leave 'rtp' pointing to the route 231 * entry preceding the point at which the new origin should be inserted. 232 * This code is optimized for the normal case in which the first entry to 233 * be examined is the matching entry. 234 */ 235 static int find_route(origin, mask) 236 register u_long origin, mask; 237 { 238 register struct rtentry *r; 239 240 r = rtp->rt_next; 241 while (r != NULL) { 242 if (origin == r->rt_origin && mask == r->rt_originmask) { 243 rtp = r; 244 return (TRUE); 245 } 246 if (ntohl(mask) > ntohl(r->rt_originmask) || 247 (mask == r->rt_originmask && 248 ntohl(origin) > ntohl(r->rt_origin))) { 249 rtp = r; 250 r = r->rt_next; 251 } 252 else break; 253 } 254 return (FALSE); 255 } 256 257 258 /* 259 * Search the entire routing table, looking for an entry which conflicts 260 * with the given origin and mask, for example, an entry which has the same 261 * origin under a different mask. If a conflicting entry is found, return 262 * a pointer to the entry preceding it (to facilitate deletion); if no 263 * conflict is found, return NULL. 264 */ 265 static struct rtentry *find_conflicting_route(origin, mask) 266 register u_long origin, mask; 267 { 268 register struct rtentry *r, *prev_r; 269 270 for (prev_r = (struct rtentry *)&routing_table, r = routing_table; 271 r != NULL; 272 prev_r = r, r = r->rt_next ) { 273 if ((origin & r->rt_originmask) == r->rt_origin || 274 (r->rt_origin & mask) == origin) { 275 return (prev_r); 276 } 277 } 278 return (NULL); 279 } 280 281 282 /* 283 * Create a new routing table entry for the specified origin and link it into 284 * the routing table. The shared variable 'rtp' is assumed to point to the 285 * routing entry after which the new one should be inserted. It is left 286 * pointing to the new entry. 287 * 288 * Only the origin, originmask, originwidth and flags fields are initialized 289 * in the new route entry; the caller is responsible for filling in the the 290 * rest. 291 */ 292 static void create_route(origin, mask) 293 u_long origin, mask; 294 { 295 register struct rtentry *r; 296 297 if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) 298 + (3 * numvifs * sizeof(u_long)))) == NULL) { 299 log(LOG_ERR, 0, "ran out of memory"); /* fatal */ 300 } 301 r->rt_origin = origin; 302 r->rt_originmask = mask; 303 if (((char *)&mask)[3] != 0) r->rt_originwidth = 4; 304 else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3; 305 else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2; 306 else r->rt_originwidth = 1; 307 r->rt_flags = 0; 308 r->rt_dominants = (u_long *)(r + 1); 309 r->rt_subordinates = (u_long *)(r->rt_dominants + numvifs); 310 r->rt_leaf_timers = (u_long *)(r->rt_subordinates + numvifs); 311 312 r->rt_next = rtp->rt_next; 313 rtp->rt_next = r; 314 rtp = r; 315 ++nroutes; 316 } 317 318 319 /* 320 * Discard the routing table entry following the one to which 'prev_r' points. 321 */ 322 static void discard_route(prev_r) 323 register struct rtentry *prev_r; 324 { 325 register struct rtentry *r; 326 327 r = prev_r->rt_next; 328 prev_r->rt_next = r->rt_next; 329 free((char *)r); 330 --nroutes; 331 } 332 333 334 /* 335 * Process a route report for a single origin, creating or updating the 336 * corresponding routing table entry if necessary. 'src' is either the 337 * address of a neighboring router from which the report arrived, or zero 338 * to indicate a change of status of one of our own interfaces. 339 */ 340 void update_route(origin, mask, metric, src, vifi) 341 u_long origin, mask; 342 int metric; 343 u_long src; 344 vifi_t vifi; 345 { 346 register struct rtentry *r; 347 struct rtentry *prev_r; 348 int adj_metric; 349 350 /* 351 * Compute an adjusted metric, taking into account the cost of the 352 * subnet or tunnel over which the report arrived, and normalizing 353 * all unreachable/poisoned metrics into a single value. 354 */ 355 if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) { 356 log(LOG_WARNING, 0, 357 "%s reports out-of-range metric %u for origin %s", 358 inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2)); 359 return; 360 } 361 adj_metric = metric + uvifs[vifi].uv_metric; 362 if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE; 363 364 /* 365 * Look up the reported origin in the routing table. 366 */ 367 if (!find_route(origin, mask)) { 368 /* 369 * Not found. 370 * Don't create a new entry if the report says it's unreachable, 371 * or if the reported origin and mask are invalid. 372 */ 373 if (adj_metric == UNREACHABLE) { 374 return; 375 } 376 if (src != 0 && !inet_valid_subnet(origin, mask)) { 377 log(LOG_WARNING, 0, 378 "%s reports an invalid origin (%s) and/or mask (%08x)", 379 inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask)); 380 return; 381 } 382 383 /* 384 * If the new origin and mask are inconsistent with an entry 385 * already in the routing table, either ignore this update 386 * (if it came from another router), or delete the conflicting 387 * entry (if the update is for a directly-connected subnet). 388 */ 389 if ((prev_r = find_conflicting_route(origin, mask)) != NULL ) { 390 if (src != 0) { 391 log(LOG_INFO, 0, 392 "%s reports a conflicting origin (%s) and mask (%08x)", 393 inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask)); 394 return; 395 } 396 else { 397 r = prev_r->rt_next; 398 log(LOG_INFO, 0, 399 "deleting route with conflicting origin (%s), mask (%08x)", 400 inet_fmt(r->rt_origin, s1), ntohl(r->rt_originmask)); 401 402 if (r->rt_metric != UNREACHABLE) { 403 k_del_route(r); 404 } 405 discard_route(prev_r); 406 if (rtp == r) rtp = prev_r; 407 } 408 } 409 410 /* 411 * OK, create the new routing entry. 'rtp' will be left pointing 412 * to the new entry. 413 */ 414 create_route(origin, mask); 415 416 rtp->rt_metric = UNREACHABLE; /* temporary; updated below */ 417 } 418 419 /* 420 * We now have a routing entry for the reported origin. Update it? 421 */ 422 r = rtp; 423 if (r->rt_metric == UNREACHABLE) { 424 /* 425 * The routing entry is for a formerly-unreachable or new origin. 426 * If the report claims reachability, update the entry to use 427 * the reported route. 428 */ 429 if (adj_metric == UNREACHABLE) 430 return; 431 432 r->rt_parent = vifi; 433 init_children_and_leaves(r, vifi); 434 k_add_route(r); 435 r->rt_gateway = src; 436 r->rt_timer = 0; 437 r->rt_metric = adj_metric; 438 r->rt_flags |= RTF_CHANGED; 439 routes_changed = TRUE; 440 } 441 else if (src == r->rt_gateway) { 442 /* 443 * The report has come either from the interface directly-connected 444 * to the origin subnet (src and r->rt_gateway both equal zero) or 445 * from the gateway we have chosen as the best first-hop gateway back 446 * towards the origin (src and r->rt_gateway not equal zero). Reset 447 * the route timer and, if the reported metric has changed, update 448 * our entry accordingly. 449 */ 450 r->rt_timer = 0; 451 if (adj_metric == r->rt_metric) 452 return; 453 454 if (adj_metric == UNREACHABLE) { 455 k_del_route(r); 456 r->rt_timer = ROUTE_EXPIRE_TIME; 457 } 458 else if (adj_metric < r->rt_metric) { 459 if (init_children_and_leaves(r, vifi)) { 460 k_update_route(r); 461 } 462 } 463 r->rt_metric = adj_metric; 464 r->rt_flags |= RTF_CHANGED; 465 routes_changed = TRUE; 466 } 467 else if (src == 0 || 468 (r->rt_gateway != 0 && 469 (adj_metric < r->rt_metric || 470 (adj_metric == r->rt_metric && 471 r->rt_timer >= ROUTE_SWITCH_TIME)))) { 472 /* 473 * The report is for an origin we consider reachable; the report 474 * comes either from one of our own interfaces or from a gateway 475 * other than the one we have chosen as the best first-hop gateway 476 * back towards the origin. If the source of the update is one of 477 * our own interfaces, or if the origin is not a directly-connected 478 * subnet and the reported metric for that origin is better than 479 * what our routing entry says, update the entry to use the new 480 * gateway and metric. We also switch gateways if the reported 481 * metric is the same as the one in the route entry and the gateway 482 * associated with the route entry has not been heard from recently. 483 * Did you get all that? 484 */ 485 if (r->rt_parent != vifi || adj_metric < r->rt_metric) { 486 r->rt_parent = vifi; 487 if (init_children_and_leaves(r, vifi)) { 488 k_update_route(r); 489 } 490 } 491 r->rt_gateway = src; 492 r->rt_timer = 0; 493 r->rt_metric = adj_metric; 494 r->rt_flags |= RTF_CHANGED; 495 routes_changed = TRUE; 496 } 497 else if (vifi != r->rt_parent) { 498 /* 499 * The report came from a vif other than the route's parent vif. 500 * Update the children and leaf info, if necessary. 501 */ 502 if (VIFM_ISSET(vifi, r->rt_children)) { 503 /* 504 * Vif is a child vif for this route. 505 */ 506 if (metric < r->rt_metric || 507 (metric == r->rt_metric && 508 ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) { 509 /* 510 * Neighbor has lower metric to origin (or has same metric 511 * and lower IP address) -- it becomes the dominant router, 512 * and vif is no longer a child for me. 513 */ 514 VIFM_CLR(vifi, r->rt_children); 515 VIFM_CLR(vifi, r->rt_leaves); 516 r->rt_dominants [vifi] = src; 517 r->rt_subordinates[vifi] = 0; 518 r->rt_leaf_timers [vifi] = 0; 519 k_update_route(r); 520 } 521 else if (metric > UNREACHABLE) { /* "poisoned reverse" */ 522 /* 523 * Neighbor considers this vif to be on path to route's 524 * origin; if no subordinate recorded, record this neighbor 525 * as subordinate and clear the leaf flag. 526 */ 527 if (r->rt_subordinates[vifi] == 0) { 528 VIFM_CLR(vifi, r->rt_leaves); 529 r->rt_subordinates[vifi] = src; 530 r->rt_leaf_timers [vifi] = 0; 531 k_update_route(r); 532 } 533 } 534 else if (src == r->rt_subordinates[vifi]) { 535 /* 536 * Current subordinate no longer considers this vif to be on 537 * path to route's origin; it is no longer a subordinate 538 * router, and we set the leaf confirmation timer to give 539 * us time to hear from other subordinates. 540 */ 541 r->rt_subordinates[vifi] = 0; 542 if (uvifs[vifi].uv_neighbors == NULL || 543 uvifs[vifi].uv_neighbors->al_next == NULL) { 544 VIFM_SET(vifi, r->rt_leaves); 545 k_update_route(r); 546 } 547 else { 548 r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME; 549 r->rt_flags |= RTF_LEAF_TIMING; 550 } 551 } 552 553 } 554 else if (src == r->rt_dominants[vifi] && 555 (metric > r->rt_metric || 556 (metric == r->rt_metric && 557 ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) { 558 /* 559 * Current dominant no longer has a lower metric to origin 560 * (or same metric and lower IP address); we adopt the vif 561 * as our own child. 562 */ 563 VIFM_SET(vifi, r->rt_children); 564 r->rt_dominants [vifi] = 0; 565 if (metric > UNREACHABLE) { 566 r->rt_subordinates[vifi] = src; 567 } 568 else if (uvifs[vifi].uv_neighbors == NULL || 569 uvifs[vifi].uv_neighbors->al_next == NULL) { 570 VIFM_SET(vifi, r->rt_leaves); 571 } 572 else { 573 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; 574 r->rt_flags |= RTF_LEAF_TIMING; 575 } 576 k_update_route(r); 577 } 578 } 579 } 580 581 582 /* 583 * On every timer interrupt, advance the timer in each routing entry. 584 */ 585 void age_routes() 586 { 587 register struct rtentry *r; 588 register struct rtentry *prev_r; 589 register vifi_t vifi; 590 591 for (prev_r = (struct rtentry *)&routing_table, r = routing_table; 592 r != NULL; 593 prev_r = r, r = r->rt_next) { 594 595 if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) { 596 /* 597 * Route is still good; see if any leaf timers need to be 598 * advanced. 599 */ 600 if (r->rt_flags & RTF_LEAF_TIMING) { 601 r->rt_flags &= ~RTF_LEAF_TIMING; 602 for (vifi = 0; vifi < numvifs; ++vifi) { 603 if (r->rt_leaf_timers[vifi] != 0) { 604 /* 605 * Unlike other timers, leaf timers decrement. 606 */ 607 if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){ 608 VIFM_SET(vifi, r->rt_leaves); 609 k_update_route(r); 610 } 611 else { 612 r->rt_flags |= RTF_LEAF_TIMING; 613 } 614 } 615 } 616 } 617 } 618 else if (r->rt_timer >= ROUTE_DISCARD_TIME) { 619 /* 620 * Time to garbage-collect the route entry. 621 */ 622 discard_route(prev_r); 623 r = prev_r; 624 } 625 else if (r->rt_metric != UNREACHABLE) { 626 /* 627 * Time to expire the route entry. If the gateway is zero, 628 * i.e., it is a route to a directly-connected subnet, just 629 * set the timer back to zero; such routes expire only when 630 * the interface to the subnet goes down. 631 */ 632 if (r->rt_gateway == 0) { 633 r->rt_timer = 0; 634 } 635 else { 636 k_del_route(r); 637 r->rt_metric = UNREACHABLE; 638 r->rt_flags |= RTF_CHANGED; 639 routes_changed = TRUE; 640 } 641 } 642 } 643 } 644 645 646 /* 647 * Mark all routes as unreachable. This function is called only from 648 * hup() in preparation for informing all neighbors that we are going 649 * off the air. For consistency, we ought also to delete all reachable 650 * route entries from the kernel, but since we are about to exit we rely 651 * on the kernel to do its own cleanup -- no point in making all those 652 * expensive kernel calls now. 653 */ 654 void expire_all_routes() 655 { 656 register struct rtentry *r; 657 658 for (r = routing_table; r != NULL; r = r->rt_next) { 659 r->rt_metric = UNREACHABLE; 660 r->rt_flags |= RTF_CHANGED; 661 routes_changed = TRUE; 662 } 663 } 664 665 666 /* 667 * Process an incoming neighbor probe message. 668 */ 669 void accept_probe(src, dst) 670 u_long src, dst; 671 { 672 vifi_t vifi; 673 674 if ((vifi = find_vif(src, dst)) == NO_VIF) { 675 log(LOG_INFO, 0, 676 "ignoring probe from non-neighbor %s", inet_fmt(src, s1)); 677 return; 678 } 679 680 if (!update_neighbor(vifi, src, DVMRP_PROBE)) 681 return; 682 683 report(ALL_ROUTES, vifi, src); 684 } 685 686 struct newrt { 687 u_long mask; 688 u_long origin; 689 int metric; 690 int pad; 691 }; 692 693 int compare_rts(r1, r2) 694 register struct newrt *r1; 695 register struct newrt *r2; 696 { 697 register unsigned long m1 = ntohl(r1->mask); 698 register unsigned long m2 = ntohl(r2->mask); 699 register unsigned long o1, o2; 700 701 if (m1 > m2) 702 return (1); 703 if (m1 < m2) 704 return (-1); 705 706 /* masks are equal */ 707 o1 = ntohl(r1->origin); 708 o2 = ntohl(r2->origin); 709 if (o1 > o2) 710 return (1); 711 if (o1 < o2) 712 return (-1); 713 return (0); 714 } 715 716 /* 717 * Process an incoming route report message. 718 */ 719 void accept_report(src, dst, p, datalen) 720 u_long src, dst; 721 register char *p; 722 register int datalen; 723 { 724 vifi_t vifi; 725 register int width, i, nrt = 0; 726 int metric; 727 u_long mask; 728 u_long origin; 729 struct newrt rt[4096]; 730 731 if ((vifi = find_vif(src, dst)) == NO_VIF) { 732 log(LOG_INFO, 0, 733 "ignoring route report from non-neighbor %s", inet_fmt(src, s1)); 734 return; 735 } 736 737 if (!update_neighbor(vifi, src, DVMRP_REPORT)) 738 return; 739 740 if (datalen > 2*4096) { 741 log(LOG_INFO, 0, 742 "ignoring oversize (%d bytes) route report from %s", 743 datalen, inet_fmt(src, s1)); 744 return; 745 } 746 747 while (datalen > 0) { /* Loop through per-mask lists. */ 748 if (datalen < 3) { 749 log(LOG_WARNING, 0, 750 "received truncated route report from %s", inet_fmt(src, s1)); 751 return; 752 } 753 ((char *)&mask)[0] = 0xff; width = 1; 754 if ((((char *)&mask)[1] = *p++) != 0) width = 2; 755 if ((((char *)&mask)[2] = *p++) != 0) width = 3; 756 if ((((char *)&mask)[3] = *p++) != 0) width = 4; 757 datalen -= 3; 758 759 do { /* Loop through (origin, metric) pairs */ 760 if (datalen < width + 1) { 761 log(LOG_WARNING, 0, 762 "received truncated route report from %s", inet_fmt(src, s1)); 763 return; 764 } 765 origin = 0; 766 for (i = 0; i < width; ++i) 767 ((char *)&origin)[i] = *p++; 768 metric = *p++; 769 datalen -= width + 1; 770 rt[nrt].mask = mask; 771 rt[nrt].origin = origin; 772 rt[nrt].metric = metric; 773 ++nrt; 774 } while (!(metric & 0x80)); 775 } 776 qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts); 777 start_route_updates(); 778 for (i = 0; i < nrt; ++i) 779 update_route(rt[i].origin, rt[i].mask, (rt[i].metric & 0x7f), src, vifi); 780 781 if (routes_changed && !delay_change_reports) 782 report_to_all_neighbors(CHANGED_ROUTES); 783 } 784 785 786 /* 787 * Send a route report message to destination 'dst', via virtual interface 788 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. 789 */ 790 void report(which_routes, vifi, dst) 791 int which_routes; 792 vifi_t vifi; 793 u_long dst; 794 { 795 register struct rtentry *r; 796 register char *p; 797 register int i; 798 int datalen; 799 int width; 800 u_long mask; 801 u_long src; 802 803 src = uvifs[vifi].uv_lcl_addr; 804 805 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; 806 datalen = 0; 807 mask = 0; 808 809 for (r = routing_table; r != NULL; r = r->rt_next) { 810 811 if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) 812 continue; 813 814 /* 815 * If there is no room for this route in the current message, 816 * send the message and start a new one. 817 */ 818 if (datalen + ((r->rt_originmask == mask) ? 819 (width + 1) : 820 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { 821 *(p-1) |= 0x80; 822 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, 823 htonl(MROUTED_LEVEL), datalen); 824 825 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; 826 datalen = 0; 827 mask = 0; 828 } 829 830 if(r->rt_originmask != mask) { 831 mask = r->rt_originmask; 832 width = r->rt_originwidth; 833 if (datalen != 0) *(p-1) |= 0x80; 834 *p++ = ((char *)&mask)[1]; 835 *p++ = ((char *)&mask)[2]; 836 *p++ = ((char *)&mask)[3]; 837 datalen += 3; 838 } 839 840 for (i = 0; i < width; ++i) 841 *p++ = ((char *)&(r->rt_origin))[i]; 842 843 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? 844 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ 845 (char)(r->rt_metric); 846 847 datalen += width + 1; 848 } 849 850 if (datalen != 0) { 851 *(p-1) |= 0x80; 852 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, 853 htonl(MROUTED_LEVEL), datalen); 854 } 855 } 856 857 858 /* 859 * Send a route report message to all neighboring routers. 860 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. 861 */ 862 void report_to_all_neighbors(which_routes) 863 int which_routes; 864 { 865 register vifi_t vifi; 866 register struct uvif *v; 867 register struct rtentry *r; 868 int routes_changed_before; 869 870 /* 871 * Remember the state of the global routes_changed flag before 872 * generating the reports, and clear the flag. 873 */ 874 routes_changed_before = routes_changed; 875 routes_changed = FALSE; 876 877 878 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { 879 if (v->uv_neighbors != NULL) { 880 report(which_routes, vifi, 881 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr 882 : dvmrp_group); 883 } 884 } 885 886 /* 887 * If there were changed routes before we sent the reports AND 888 * if no new changes occurred while sending the reports, clear 889 * the change flags in the individual route entries. If changes 890 * did occur while sending the reports, new reports will be 891 * generated at the next timer interrupt. 892 */ 893 if (routes_changed_before && !routes_changed) { 894 for (r = routing_table; r != NULL; r = r->rt_next) { 895 r->rt_flags &= ~RTF_CHANGED; 896 } 897 } 898 899 /* 900 * Set a flag to inhibit further reports of changed routes until the 901 * next timer interrupt. This is to alleviate update storms. 902 */ 903 delay_change_reports = TRUE; 904 } 905 906 /* 907 * Send a route report message to destination 'dst', via virtual interface 908 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. 909 */ 910 int report_chunk(start_rt, vifi, dst) 911 register struct rtentry *start_rt; 912 vifi_t vifi; 913 u_long dst; 914 { 915 register struct rtentry *r; 916 register char *p; 917 register int i; 918 register int nrt = 0; 919 int datalen; 920 int width; 921 u_long mask; 922 u_long src; 923 924 src = uvifs[vifi].uv_lcl_addr; 925 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; 926 datalen = 0; 927 mask = 0; 928 929 for (r = start_rt; r != NULL; r = r->rt_next) { 930 /* 931 * If there is no room for this route in the current message, 932 * send it & return how many routes we sent. 933 */ 934 if (datalen + ((r->rt_originmask == mask) ? 935 (width + 1) : 936 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { 937 *(p-1) |= 0x80; 938 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, 939 htonl(MROUTED_LEVEL), datalen); 940 return (nrt); 941 } 942 if(r->rt_originmask != mask) { 943 mask = r->rt_originmask; 944 width = r->rt_originwidth; 945 if (datalen != 0) *(p-1) |= 0x80; 946 *p++ = ((char *)&mask)[1]; 947 *p++ = ((char *)&mask)[2]; 948 *p++ = ((char *)&mask)[3]; 949 datalen += 3; 950 } 951 for (i = 0; i < width; ++i) 952 *p++ = ((char *)&(r->rt_origin))[i]; 953 954 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? 955 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ 956 (char)(r->rt_metric); 957 ++nrt; 958 datalen += width + 1; 959 } 960 if (datalen != 0) { 961 *(p-1) |= 0x80; 962 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, 963 htonl(MROUTED_LEVEL), datalen); 964 } 965 return (nrt); 966 } 967 968 /* 969 * send the next chunk of our routing table to all neighbors. 970 */ 971 int report_next_chunk() 972 { 973 register vifi_t vifi; 974 register struct uvif *v; 975 register struct rtentry *r; 976 register struct rtentry *sr; 977 register int i, n = 0; 978 static int start_rt; 979 980 if (nroutes <= 0) 981 return (0); 982 983 /* 984 * find this round's starting route. 985 */ 986 for (sr = routing_table, i = start_rt; --i >= 0; ) { 987 sr = sr->rt_next; 988 if (sr == NULL) 989 sr = routing_table; 990 } 991 /* 992 * send one chunk of routes starting at this round's start to 993 * all our neighbors. 994 */ 995 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { 996 if (v->uv_neighbors != NULL) { 997 n = report_chunk(sr, vifi, 998 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr 999 : dvmrp_group); 1000 } 1001 } 1002 if (debug) 1003 printf("update %d starting at %d of %d\n", n, start_rt, nroutes); 1004 start_rt = (start_rt + n) % nroutes; 1005 return (n); 1006 } 1007 1008 1009 /* 1010 * Print the contents of the routing table on file 'fp'. 1011 */ 1012 void dump_routes(fp) 1013 FILE *fp; 1014 { 1015 register struct rtentry *r; 1016 register int i; 1017 1018 fprintf(fp, 1019 "Multicast Routing Table (%u %s)\n%s", 1020 nroutes, (nroutes == 1) ? "entry" : "entries", 1021 " Origin-Subnet From-Gateway Metric In-Vif Out-Vifs\n"); 1022 1023 for (r = routing_table; r != NULL; r = r->rt_next) { 1024 1025 fprintf(fp, " %-15s %-15s ", 1026 inet_fmts(r->rt_origin, r->rt_originmask, s1), 1027 (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2)); 1028 1029 fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ", 1030 r->rt_metric); 1031 1032 fprintf(fp, "%7u ", 1033 r->rt_parent); 1034 1035 for (i = 0; i < numvifs; ++i) { 1036 if (VIFM_ISSET(i, r->rt_children)) { 1037 fprintf(fp, " %u%c", 1038 i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' '); 1039 } 1040 } 1041 fprintf(fp, "\n"); 1042 } 1043 fprintf(fp, "\n"); 1044 } 1045