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