Annotation of embedaddon/mrouted/route.c, revision 1.1
1.1 ! misho 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: #include "defs.h"
! 11:
! 12: /*
! 13: * Private macros.
! 14: */
! 15: #define MAX_NUM_RT 4096
! 16:
! 17: /*
! 18: * Private types.
! 19: */
! 20: struct newrt {
! 21: u_int32 mask;
! 22: u_int32 origin;
! 23: int metric;
! 24: int pad;
! 25: };
! 26:
! 27: struct blaster_hdr {
! 28: u_int32 bh_src;
! 29: u_int32 bh_dst;
! 30: u_int32 bh_level;
! 31: int bh_datalen;
! 32: };
! 33:
! 34: /*
! 35: * Exported variables.
! 36: */
! 37: int routes_changed; /* 1=>some routes have changed */
! 38: int delay_change_reports; /* 1=>postpone change reports */
! 39: unsigned int nroutes; /* current number of route entries */
! 40: struct rtentry *routing_table; /* pointer to list of route entries */
! 41:
! 42: /*
! 43: * Private variables.
! 44: */
! 45: static struct rtentry *rtp; /* pointer to a route entry */
! 46: static struct rtentry *rt_end; /* pointer to last route entry */
! 47:
! 48: /*
! 49: * Private functions.
! 50: */
! 51: static int init_children_and_leaves (struct rtentry *r, vifi_t parent, int first);
! 52: static int find_route (u_int32 origin, u_int32 mask);
! 53: static void create_route (u_int32 origin, u_int32 mask);
! 54: static void discard_route (struct rtentry *this);
! 55: static int compare_rts (const void *rt1, const void *rt2);
! 56: static int report_chunk (int, struct rtentry *start_rt, vifi_t vifi, u_int32 dst);
! 57: static void queue_blaster_report (vifi_t vifi, u_int32 src, u_int32 dst, char *p, size_t datalen, u_int32 level);
! 58: static void process_blaster_report (void *vifip);
! 59:
! 60: #ifdef SNMP
! 61: #include <sys/types.h>
! 62: #include "snmp.h"
! 63:
! 64: /*
! 65: * Return pointer to a specific route entry. This must be a separate
! 66: * function from find_route() which modifies rtp.
! 67: */
! 68: struct rtentry *snmp_find_route(u_int32 src, u_int32 mask)
! 69: {
! 70: struct rtentry *rt;
! 71:
! 72: for (rt = routing_table; rt; rt = rt->rt_next) {
! 73: if (src == rt->rt_origin && mask == rt->rt_originmask)
! 74: return rt;
! 75: }
! 76:
! 77: return NULL;
! 78: }
! 79:
! 80: /*
! 81: * Find next route entry > specification
! 82: */
! 83: int next_route(struct rtentry **rtpp, u_int32 src, u_int32 mask)
! 84: {
! 85: struct rtentry *rt, *rbest = NULL;
! 86:
! 87: /* Among all entries > spec, find "lowest" one in order */
! 88: for (rt = routing_table; rt; rt=rt->rt_next) {
! 89: if ((ntohl(rt->rt_origin) > ntohl(src)
! 90: || (ntohl(rt->rt_origin) == ntohl(src)
! 91: && ntohl(rt->rt_originmask) > ntohl(mask)))
! 92: && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
! 93: || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
! 94: && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
! 95: rbest = rt;
! 96: }
! 97: (*rtpp) = rbest;
! 98:
! 99: return (*rtpp)!=0;
! 100: }
! 101:
! 102: /*
! 103: * Given a routing table entry, and a vifi, find the next vifi/entry
! 104: */
! 105: int next_route_child(struct rtentry **rtpp, u_int32 src, u_int32 mask, vifi_t vifi)
! 106: {
! 107: /* Get (S,M) entry */
! 108: if (!((*rtpp) = snmp_find_route(src, mask)))
! 109: if (!next_route(rtpp, src, mask))
! 110: return 0;
! 111:
! 112: /* Continue until we get one with a valid next vif */
! 113: do {
! 114: for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
! 115: if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
! 116: return 1;
! 117: *vifi = 0;
! 118: } while (next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask));
! 119:
! 120: return 0;
! 121: }
! 122: #endif /* SNMP */
! 123:
! 124: /*
! 125: * Initialize the routing table and associated variables.
! 126: */
! 127: void init_routes(void)
! 128: {
! 129: routing_table = NULL;
! 130: rt_end = NULL;
! 131: nroutes = 0;
! 132: routes_changed = FALSE;
! 133: delay_change_reports = FALSE;
! 134: }
! 135:
! 136:
! 137: /*
! 138: * Initialize the children bits for route 'r', along with the
! 139: * associated dominant and subordinate data structures.
! 140: * If first is set, initialize dominants, otherwise keep old
! 141: * dominants on non-parent interfaces.
! 142: * XXX Does this need a return value?
! 143: */
! 144: static int init_children_and_leaves(struct rtentry *r, vifi_t parent, int first)
! 145: {
! 146: vifi_t vifi;
! 147: struct uvif *v;
! 148: vifbitmap_t old_children;
! 149: nbrbitmap_t old_subords;
! 150:
! 151: VIFM_COPY(r->rt_children, old_children);
! 152: NBRM_COPY(r->rt_subordinates, old_subords);
! 153:
! 154: VIFM_CLRALL(r->rt_children);
! 155:
! 156: for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
! 157: if (first || vifi == parent)
! 158: r->rt_dominants[vifi] = 0;
! 159: if (vifi == parent || uvifs[vifi].uv_flags & VIFF_NOFLOOD ||
! 160: AVOID_TRANSIT(vifi, r) || (!first && r->rt_dominants[vifi]))
! 161: NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 162: else
! 163: NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 164:
! 165: if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED)) &&
! 166: !(!first && r->rt_dominants[vifi])) {
! 167: VIFM_SET(vifi, r->rt_children);
! 168: }
! 169: }
! 170:
! 171: return (!VIFM_SAME(r->rt_children, old_children) ||
! 172: !NBRM_SAME(r->rt_subordinates, old_subords));
! 173: }
! 174:
! 175:
! 176: /*
! 177: * A new vif has come up -- update the children bitmaps in all route
! 178: * entries to take that into account.
! 179: */
! 180: void add_vif_to_routes(vifi_t vifi)
! 181: {
! 182: struct rtentry *r;
! 183: struct uvif *v;
! 184:
! 185: v = &uvifs[vifi];
! 186: for (r = routing_table; r != NULL; r = r->rt_next) {
! 187: if (r->rt_metric != UNREACHABLE &&
! 188: !VIFM_ISSET(vifi, r->rt_children)) {
! 189: VIFM_SET(vifi, r->rt_children);
! 190: r->rt_dominants[vifi] = 0;
! 191: /*XXX isn't uv_nbrmap going to be empty?*/
! 192: NBRM_CLRMASK(r->rt_subordinates, v->uv_nbrmap);
! 193: update_table_entry(r, r->rt_gateway);
! 194: }
! 195: }
! 196: }
! 197:
! 198:
! 199: /*
! 200: * A vif has gone down -- expire all routes that have that vif as parent,
! 201: * and update the children bitmaps in all other route entries to take into
! 202: * account the failed vif.
! 203: */
! 204: void delete_vif_from_routes(vifi_t vifi)
! 205: {
! 206: struct rtentry *r;
! 207:
! 208: for (r = routing_table; r != NULL; r = r->rt_next) {
! 209: if (r->rt_metric != UNREACHABLE) {
! 210: if (vifi == r->rt_parent) {
! 211: del_table_entry(r, 0, DEL_ALL_ROUTES);
! 212: r->rt_timer = ROUTE_EXPIRE_TIME;
! 213: r->rt_metric = UNREACHABLE;
! 214: r->rt_flags |= RTF_CHANGED;
! 215: routes_changed = TRUE;
! 216: }
! 217: else if (VIFM_ISSET(vifi, r->rt_children)) {
! 218: VIFM_CLR(vifi, r->rt_children);
! 219: NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 220: update_table_entry(r, r->rt_gateway);
! 221: }
! 222: else {
! 223: r->rt_dominants[vifi] = 0;
! 224: }
! 225: }
! 226: }
! 227: }
! 228:
! 229:
! 230: /*
! 231: * A new neighbor has come up. If we're flooding on the neighbor's
! 232: * vif, mark that neighbor as subordinate for all routes whose parent
! 233: * is not this vif.
! 234: */
! 235: void add_neighbor_to_routes(vifi_t vifi, u_int index)
! 236: {
! 237: struct rtentry *r;
! 238: struct uvif *v;
! 239:
! 240: v = &uvifs[vifi];
! 241: if (v->uv_flags & VIFF_NOFLOOD)
! 242: return;
! 243: for (r = routing_table; r != NULL; r = r->rt_next) {
! 244: if (r->rt_metric != UNREACHABLE && r->rt_parent != vifi &&
! 245: !AVOID_TRANSIT(vifi, r)) {
! 246: NBRM_SET(index, r->rt_subordinates);
! 247: update_table_entry(r, r->rt_gateway);
! 248: }
! 249: }
! 250: }
! 251:
! 252:
! 253: /*
! 254: * A neighbor has failed or become unreachable. If that neighbor was
! 255: * considered a dominant or subordinate router in any route entries,
! 256: * take appropriate action. Expire all routes this neighbor advertised
! 257: * to us.
! 258: */
! 259: void delete_neighbor_from_routes(u_int32 addr, vifi_t vifi, u_int index)
! 260: {
! 261: struct rtentry *r;
! 262: struct uvif *v;
! 263:
! 264: v = &uvifs[vifi];
! 265: for (r = routing_table; r != NULL; r = r->rt_next) {
! 266: if (r->rt_metric != UNREACHABLE) {
! 267: if (r->rt_parent == vifi && r->rt_gateway == addr) {
! 268: del_table_entry(r, 0, DEL_ALL_ROUTES);
! 269: r->rt_timer = ROUTE_EXPIRE_TIME;
! 270: r->rt_metric = UNREACHABLE;
! 271: r->rt_flags |= RTF_CHANGED;
! 272: routes_changed = TRUE;
! 273: } else if (r->rt_dominants[vifi] == addr) {
! 274: VIFM_SET(vifi, r->rt_children);
! 275: r->rt_dominants[vifi] = 0;
! 276: if ((uvifs[vifi].uv_flags & VIFF_NOFLOOD) ||
! 277: AVOID_TRANSIT(vifi, r))
! 278: NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 279: else
! 280: NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 281: update_table_entry(r, r->rt_gateway);
! 282: } else if (NBRM_ISSET(index, r->rt_subordinates)) {
! 283: NBRM_CLR(index, r->rt_subordinates);
! 284: update_table_entry(r, r->rt_gateway);
! 285: }
! 286: }
! 287: }
! 288: }
! 289:
! 290:
! 291: /*
! 292: * Prepare for a sequence of ordered route updates by initializing a pointer
! 293: * to the start of the routing table. The pointer is used to remember our
! 294: * position in the routing table in order to avoid searching from the
! 295: * beginning for each update; this relies on having the route reports in
! 296: * a single message be in the same order as the route entries in the routing
! 297: * table.
! 298: */
! 299: void start_route_updates(void)
! 300: {
! 301: rtp = routing_table;
! 302: }
! 303:
! 304:
! 305: /*
! 306: * Starting at the route entry following the one to which 'rtp' points,
! 307: * look for a route entry matching the specified origin and mask. If a
! 308: * match is found, return TRUE and leave 'rtp' pointing at the found entry.
! 309: * If no match is found, return FALSE and leave 'rtp' pointing to the route
! 310: * entry preceding the point at which the new origin should be inserted.
! 311: * This code is optimized for the normal case in which the first entry to
! 312: * be examined is the matching entry.
! 313: */
! 314: static int find_route(u_int32 origin, u_int32 mask)
! 315: {
! 316: struct rtentry *r;
! 317:
! 318: r = rtp;
! 319: while (r != NULL) {
! 320: if (origin == r->rt_origin && mask == r->rt_originmask) {
! 321: rtp = r;
! 322: return TRUE;
! 323: }
! 324: if (ntohl(mask) < ntohl(r->rt_originmask) ||
! 325: (mask == r->rt_originmask &&
! 326: ntohl(origin) < ntohl(r->rt_origin))) {
! 327: rtp = r;
! 328: r = r->rt_next;
! 329: } else {
! 330: break;
! 331: }
! 332: }
! 333: return FALSE;
! 334: }
! 335:
! 336: /*
! 337: * Create a new routing table entry for the specified origin and link it into
! 338: * the routing table. The shared variable 'rtp' is assumed to point to the
! 339: * routing entry after which the new one should be inserted. It is left
! 340: * pointing to the new entry.
! 341: *
! 342: * Only the origin, originmask, originwidth and flags fields are initialized
! 343: * in the new route entry; the caller is responsible for filling in the rest.
! 344: */
! 345: static void create_route(u_int32 origin, u_int32 mask)
! 346: {
! 347: size_t len;
! 348: struct rtentry *this;
! 349:
! 350: this = (struct rtentry *)malloc(sizeof(struct rtentry));
! 351: if (!this) {
! 352: logit(LOG_ERR, errno, "route.c:create_route() - Failed allocating struct rtentry.\n");
! 353: return; /* NOTREACHED */
! 354: }
! 355: memset(this, 0, sizeof(struct rtentry));
! 356:
! 357: len = numvifs * sizeof(u_int32);
! 358: this->rt_dominants = (u_int32 *)malloc(len);
! 359: if (!this->rt_dominants) {
! 360: logit(LOG_ERR, errno, "route.c:create_route() - Failed allocating struct rtentry.\n");
! 361: free(this);
! 362: return; /* NOTREACHED */
! 363: }
! 364: memset(this->rt_dominants, 0, len);
! 365:
! 366: this->rt_origin = origin;
! 367: this->rt_originmask = mask;
! 368: if (((char *)&mask)[3] != 0) this->rt_originwidth = 4;
! 369: else if (((char *)&mask)[2] != 0) this->rt_originwidth = 3;
! 370: else if (((char *)&mask)[1] != 0) this->rt_originwidth = 2;
! 371: else this->rt_originwidth = 1;
! 372: this->rt_flags = 0;
! 373: this->rt_groups = NULL;
! 374:
! 375: VIFM_CLRALL(this->rt_children);
! 376: NBRM_CLRALL(this->rt_subordinates);
! 377: NBRM_CLRALL(this->rt_subordadv);
! 378:
! 379: /* Link in 'this', where rtp points */
! 380: if (rtp) {
! 381: this->rt_prev = rtp;
! 382: this->rt_next = rtp->rt_next;
! 383: if (this->rt_next)
! 384: (this->rt_next)->rt_prev = this;
! 385: else
! 386: rt_end = this;
! 387: rtp->rt_next = this;
! 388: } else {
! 389: routing_table = this;
! 390: rt_end = this;
! 391: }
! 392:
! 393: rtp = this;
! 394: ++nroutes;
! 395: }
! 396:
! 397:
! 398: /*
! 399: * Discard the routing table entry following the one to which 'this' points.
! 400: * [.|prev|.]--->[.|this|.]<---[.|next|.]
! 401: */
! 402: static void discard_route(struct rtentry *this)
! 403: {
! 404: struct rtentry *prev, *next;
! 405:
! 406: if (!this)
! 407: return;
! 408:
! 409: /* Find previous and next link */
! 410: prev = this->rt_prev;
! 411: next = this->rt_next;
! 412:
! 413: /* Unlink 'this' */
! 414: if (prev)
! 415: prev->rt_next = next; /* Handles case when 'this' is last link. */
! 416: else
! 417: routing_table = next; /* 'this' is first link. */
! 418: if (next)
! 419: next->rt_prev = prev;
! 420:
! 421: /* Update the books */
! 422: uvifs[this->rt_parent].uv_nroutes--;
! 423: /*???nbr???.al_nroutes--;*/
! 424: --nroutes;
! 425:
! 426: /* Update meta pointers */
! 427: if (rtp == this)
! 428: rtp = next;
! 429: if (rt_end == this)
! 430: rt_end = next;
! 431:
! 432: free(this->rt_dominants);
! 433: free(this);
! 434: }
! 435:
! 436:
! 437: /*
! 438: * Process a route report for a single origin, creating or updating the
! 439: * corresponding routing table entry if necessary. 'src' is either the
! 440: * address of a neighboring router from which the report arrived, or zero
! 441: * to indicate a change of status of one of our own interfaces.
! 442: */
! 443: void update_route(u_int32 origin, u_int32 mask, u_int metric, u_int32 src, vifi_t vifi, struct listaddr *n)
! 444: {
! 445: register struct rtentry *r;
! 446: u_int adj_metric;
! 447:
! 448: /*
! 449: * Compute an adjusted metric, taking into account the cost of the
! 450: * subnet or tunnel over which the report arrived, and normalizing
! 451: * all unreachable/poisoned metrics into a single value.
! 452: */
! 453: if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
! 454: logit(LOG_WARNING, 0, "%s reports out-of-range metric %u for origin %s",
! 455: inet_fmt(src, s1, sizeof(s1)), metric, inet_fmts(origin, mask, s2, sizeof(s2)));
! 456: return;
! 457: }
! 458: adj_metric = metric + uvifs[vifi].uv_metric;
! 459: if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
! 460:
! 461: /*
! 462: * Look up the reported origin in the routing table.
! 463: */
! 464: if (!find_route(origin, mask)) {
! 465: /*
! 466: * Not found.
! 467: * Don't create a new entry if the report says it's unreachable,
! 468: * or if the reported origin and mask are invalid.
! 469: */
! 470: if (adj_metric == UNREACHABLE) {
! 471: return;
! 472: }
! 473: if (src != 0 && !inet_valid_subnet(origin, mask)) {
! 474: logit(LOG_WARNING, 0, "%s reports an invalid origin (%s) and/or mask (%08x)",
! 475: inet_fmt(src, s1, sizeof(s1)), inet_fmt(origin, s2, sizeof(s2)), ntohl(mask));
! 476: return;
! 477: }
! 478:
! 479: IF_DEBUG(DEBUG_RTDETAIL) {
! 480: logit(LOG_DEBUG, 0, "%s advertises new route %s",
! 481: inet_fmt(src, s1, sizeof(s1)), inet_fmts(origin, mask, s2, sizeof(s2)));
! 482: }
! 483:
! 484: /*
! 485: * OK, create the new routing entry. 'rtp' will be left pointing
! 486: * to the new entry.
! 487: */
! 488: create_route(origin, mask);
! 489: uvifs[vifi].uv_nroutes++;
! 490: /*n->al_nroutes++;*/
! 491:
! 492: rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
! 493: }
! 494:
! 495: /*
! 496: * We now have a routing entry for the reported origin. Update it?
! 497: */
! 498: r = rtp;
! 499: if (r->rt_metric == UNREACHABLE) {
! 500: /*
! 501: * The routing entry is for a formerly-unreachable or new origin.
! 502: * If the report claims reachability, update the entry to use
! 503: * the reported route.
! 504: */
! 505: if (adj_metric == UNREACHABLE)
! 506: return;
! 507:
! 508: IF_DEBUG(DEBUG_RTDETAIL) {
! 509: logit(LOG_DEBUG, 0, "%s advertises %s with adj_metric %d (ours was %d)",
! 510: inet_fmt(src, s1, sizeof(s1)), inet_fmts(origin, mask, s2, sizeof(s2)),
! 511: adj_metric, r->rt_metric);
! 512: }
! 513:
! 514: /*
! 515: * Now "steal away" any sources that belong under this route
! 516: * by deleting any cache entries they might have created
! 517: * and allowing the kernel to re-request them.
! 518: *
! 519: * If we haven't performed final initialization yet and are
! 520: * just collecting the routing table, we can't have any
! 521: * sources so we don't perform this step.
! 522: */
! 523: if (did_final_init)
! 524: steal_sources(rtp);
! 525:
! 526: r->rt_parent = vifi;
! 527: r->rt_gateway = src;
! 528: init_children_and_leaves(r, vifi, 1);
! 529:
! 530: r->rt_timer = 0;
! 531: r->rt_metric = adj_metric;
! 532: r->rt_flags |= RTF_CHANGED;
! 533: routes_changed = TRUE;
! 534: update_table_entry(r, r->rt_gateway);
! 535: } else if (src == r->rt_gateway) {
! 536: /*
! 537: * The report has come either from the interface directly-connected
! 538: * to the origin subnet (src and r->rt_gateway both equal zero) or
! 539: * from the gateway we have chosen as the best first-hop gateway back
! 540: * towards the origin (src and r->rt_gateway not equal zero). Reset
! 541: * the route timer and, if the reported metric has changed, update
! 542: * our entry accordingly.
! 543: */
! 544: r->rt_timer = 0;
! 545:
! 546: IF_DEBUG(DEBUG_RTDETAIL) {
! 547: logit(LOG_DEBUG, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)",
! 548: inet_fmt(src, s1, sizeof(s1)), inet_fmts(origin, mask, s2, sizeof(s2)),
! 549: adj_metric, r->rt_metric);
! 550: }
! 551:
! 552: if (adj_metric == r->rt_metric)
! 553: return;
! 554:
! 555: if (adj_metric == UNREACHABLE) {
! 556: del_table_entry(r, 0, DEL_ALL_ROUTES);
! 557: r->rt_timer = ROUTE_EXPIRE_TIME;
! 558: }
! 559: r->rt_metric = adj_metric;
! 560: r->rt_flags |= RTF_CHANGED;
! 561: routes_changed = TRUE;
! 562: } else if (src == 0 ||
! 563: (r->rt_gateway != 0 &&
! 564: (adj_metric < r->rt_metric ||
! 565: (adj_metric == r->rt_metric &&
! 566: (ntohl(src) < ntohl(r->rt_gateway) ||
! 567: r->rt_timer >= ROUTE_SWITCH_TIME))))) {
! 568: /*
! 569: * The report is for an origin we consider reachable; the report
! 570: * comes either from one of our own interfaces or from a gateway
! 571: * other than the one we have chosen as the best first-hop gateway
! 572: * back towards the origin. If the source of the update is one of
! 573: * our own interfaces, or if the origin is not a directly-connected
! 574: * subnet and the reported metric for that origin is better than
! 575: * what our routing entry says, update the entry to use the new
! 576: * gateway and metric. We also switch gateways if the reported
! 577: * metric is the same as the one in the route entry and the gateway
! 578: * associated with the route entry has not been heard from recently,
! 579: * or if the metric is the same but the reporting gateway has a lower
! 580: * IP address than the gateway associated with the route entry.
! 581: * Did you get all that?
! 582: */
! 583: u_int32 old_gateway;
! 584: vifi_t old_parent;
! 585:
! 586: old_gateway = r->rt_gateway;
! 587: old_parent = r->rt_parent;
! 588: r->rt_gateway = src;
! 589: r->rt_parent = vifi;
! 590:
! 591: IF_DEBUG(DEBUG_RTDETAIL) {
! 592: logit(LOG_DEBUG, 0, "%s (new parent) on vif %d advertises %s with adj_metric %d (old parent was %s on vif %d, metric %d)",
! 593: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 594: adj_metric, inet_fmt(old_gateway, s3, sizeof(s3)), old_parent, r->rt_metric);
! 595: }
! 596:
! 597: if (old_parent != vifi) {
! 598: init_children_and_leaves(r, vifi, 0);
! 599: uvifs[old_parent].uv_nroutes--;
! 600: uvifs[vifi].uv_nroutes++;
! 601: }
! 602: if (old_gateway != src) {
! 603: update_table_entry(r, old_gateway);
! 604: /*???old_gateway???->al_nroutes--;*/
! 605: /*n->al_nroutes++;*/
! 606: }
! 607: r->rt_timer = 0;
! 608: r->rt_metric = adj_metric;
! 609: r->rt_flags |= RTF_CHANGED;
! 610: routes_changed = TRUE;
! 611: } else if (vifi != r->rt_parent) {
! 612: /*
! 613: * The report came from a vif other than the route's parent vif.
! 614: * Update the children info, if necessary.
! 615: */
! 616: if (AVOID_TRANSIT(vifi, r)) {
! 617: /*
! 618: * The route's parent is a vif from which we're not supposed
! 619: * to transit onto this vif. Simply ignore the update.
! 620: */
! 621: IF_DEBUG(DEBUG_RTDETAIL) {
! 622: logit(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)",
! 623: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)), metric);
! 624: }
! 625: } else if (VIFM_ISSET(vifi, r->rt_children)) {
! 626: /*
! 627: * Vif is a child vif for this route.
! 628: */
! 629: if (metric < r->rt_metric ||
! 630: (metric == r->rt_metric &&
! 631: ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
! 632: /*
! 633: * Neighbor has lower metric to origin (or has same metric
! 634: * and lower IP address) -- it becomes the dominant router,
! 635: * and vif is no longer a child for me.
! 636: */
! 637: VIFM_CLR(vifi, r->rt_children);
! 638: r->rt_dominants[vifi] = src;
! 639: /* XXX
! 640: * We don't necessarily want to forget about subordinateness
! 641: * so that we can become the dominant quickly if the current
! 642: * dominant fails.
! 643: */
! 644: NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 645: update_table_entry(r, r->rt_gateway);
! 646: IF_DEBUG(DEBUG_RTDETAIL) {
! 647: logit(LOG_DEBUG, 0, "%s on vif %d becomes dominant for %s with metric %d",
! 648: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 649: metric);
! 650: }
! 651: } else if (metric > UNREACHABLE) { /* "poisoned reverse" */
! 652: /*
! 653: * Neighbor considers this vif to be on path to route's
! 654: * origin; record this neighbor as subordinate
! 655: */
! 656: if (!NBRM_ISSET(n->al_index, r->rt_subordinates)) {
! 657: IF_DEBUG(DEBUG_RTDETAIL) {
! 658: logit(LOG_DEBUG, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d",
! 659: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 660: metric - UNREACHABLE);
! 661: }
! 662: NBRM_SET(n->al_index, r->rt_subordinates);
! 663: update_table_entry(r, r->rt_gateway);
! 664: } else {
! 665: IF_DEBUG(DEBUG_RTDETAIL) {
! 666: logit(LOG_DEBUG, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d",
! 667: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 668: metric - UNREACHABLE);
! 669: }
! 670: }
! 671: NBRM_SET(n->al_index, r->rt_subordadv);
! 672: } else if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
! 673: /*
! 674: * Current subordinate no longer considers this vif to be on
! 675: * path to route's origin; it is no longer a subordinate
! 676: * router.
! 677: */
! 678: IF_DEBUG(DEBUG_RTDETAIL) {
! 679: logit(LOG_DEBUG, 0, "%s on vif %d is no longer a subordinate for %s with metric %d",
! 680: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 681: metric);
! 682: }
! 683: NBRM_CLR(n->al_index, r->rt_subordinates);
! 684: update_table_entry(r, r->rt_gateway);
! 685: }
! 686: } else if (src == r->rt_dominants[vifi] &&
! 687: (metric > r->rt_metric ||
! 688: (metric == r->rt_metric &&
! 689: ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
! 690: /*
! 691: * Current dominant no longer has a lower metric to origin
! 692: * (or same metric and lower IP address); we adopt the vif
! 693: * as our own child.
! 694: */
! 695: IF_DEBUG(DEBUG_RTDETAIL) {
! 696: logit(LOG_DEBUG, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d",
! 697: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 698: metric);
! 699: }
! 700:
! 701: VIFM_SET(vifi, r->rt_children);
! 702: r->rt_dominants[vifi] = 0;
! 703:
! 704: if (uvifs[vifi].uv_flags & VIFF_NOFLOOD)
! 705: NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 706: else
! 707: NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
! 708:
! 709: if (metric > UNREACHABLE) {
! 710: NBRM_SET(n->al_index, r->rt_subordinates);
! 711: NBRM_SET(n->al_index, r->rt_subordadv);
! 712: }
! 713: update_table_entry(r, r->rt_gateway);
! 714: } else {
! 715: IF_DEBUG(DEBUG_RTDETAIL) {
! 716: logit(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored)",
! 717: inet_fmt(src, s1, sizeof(s1)), vifi, inet_fmts(origin, mask, s2, sizeof(s2)),
! 718: metric);
! 719: }
! 720: }
! 721: }
! 722: }
! 723:
! 724:
! 725: /*
! 726: * On every timer interrupt, advance the timer in each routing entry.
! 727: */
! 728: void age_routes(void)
! 729: {
! 730: struct rtentry *r, *next;
! 731: extern u_long virtual_time; /* from main.c */
! 732:
! 733: r = routing_table;
! 734: while (r != NULL) {
! 735: next = r->rt_next;
! 736:
! 737: if ((r->rt_timer += TIMER_INTERVAL) >= ROUTE_DISCARD_TIME) {
! 738: /*
! 739: * Time to garbage-collect the route entry.
! 740: */
! 741: del_table_entry(r, 0, DEL_ALL_ROUTES);
! 742: discard_route(r);
! 743: }
! 744: else if (r->rt_timer >= ROUTE_EXPIRE_TIME &&
! 745: r->rt_metric != UNREACHABLE) {
! 746: /*
! 747: * Time to expire the route entry. If the gateway is zero,
! 748: * i.e., it is a route to a directly-connected subnet, just
! 749: * set the timer back to zero; such routes expire only when
! 750: * the interface to the subnet goes down.
! 751: */
! 752: if (r->rt_gateway == 0) {
! 753: r->rt_timer = 0;
! 754: }
! 755: else {
! 756: del_table_entry(r, 0, DEL_ALL_ROUTES);
! 757: r->rt_metric = UNREACHABLE;
! 758: r->rt_flags |= RTF_CHANGED;
! 759: routes_changed = TRUE;
! 760: }
! 761: }
! 762: else if (virtual_time % (ROUTE_REPORT_INTERVAL * 2) == 0) {
! 763: /*
! 764: * Time out subordinateness that hasn't been reported in
! 765: * the last 2 intervals.
! 766: */
! 767: if (!NBRM_SAME(r->rt_subordinates, r->rt_subordadv)) {
! 768: IF_DEBUG(DEBUG_ROUTE) {
! 769: logit(LOG_DEBUG, 0, "rt %s sub 0x%08x%08x subadv 0x%08x%08x metric %d",
! 770: RT_FMT(r, s1), r->rt_subordinates.hi, r->rt_subordinates.lo,
! 771: r->rt_subordadv.hi, r->rt_subordadv.lo, r->rt_metric);
! 772: }
! 773: NBRM_MASK(r->rt_subordinates, r->rt_subordadv);
! 774: update_table_entry(r, r->rt_gateway);
! 775: }
! 776: NBRM_CLRALL(r->rt_subordadv);
! 777: }
! 778:
! 779: r = next;
! 780: }
! 781: }
! 782:
! 783:
! 784: /*
! 785: * Mark all routes as unreachable. This function is called only from
! 786: * hup() in preparation for informing all neighbors that we are going
! 787: * off the air. For consistency, we ought also to delete all reachable
! 788: * route entries from the kernel, but since we are about to exit we rely
! 789: * on the kernel to do its own cleanup -- no point in making all those
! 790: * expensive kernel calls now.
! 791: */
! 792: void expire_all_routes(void)
! 793: {
! 794: struct rtentry *r;
! 795:
! 796: for (r = routing_table; r != NULL; r = r->rt_next) {
! 797: r->rt_metric = UNREACHABLE;
! 798: r->rt_flags |= RTF_CHANGED;
! 799: routes_changed = TRUE;
! 800: }
! 801: }
! 802:
! 803:
! 804: /*
! 805: * Delete all the routes in the routing table.
! 806: */
! 807: void free_all_routes(void)
! 808: {
! 809: struct rtentry *r, *next;
! 810:
! 811: r = routing_table;
! 812: while (r != NULL) {
! 813: next = r->rt_next;
! 814: discard_route(r);
! 815: r = next;
! 816: }
! 817: }
! 818:
! 819:
! 820: /*
! 821: * Process an incoming neighbor probe message.
! 822: */
! 823: void accept_probe(u_int32 src, u_int32 dst, char *p, size_t datalen, u_int32 level)
! 824: {
! 825: vifi_t vifi;
! 826: static struct listaddr *unknowns = NULL;
! 827:
! 828: if ((vifi = find_vif(src, dst)) == NO_VIF) {
! 829: struct listaddr *a, **prev;
! 830: struct listaddr *match = NULL;
! 831: time_t now = time(0);
! 832:
! 833: for (prev = &unknowns, a = *prev; a; a = *prev) {
! 834: if (a->al_addr == src)
! 835: match = a;
! 836: if (a->al_ctime + 2 * a->al_timer < (u_long)now) {
! 837: /* We haven't heard from it in a long time */
! 838: *prev = a->al_next;
! 839: free(a);
! 840: } else {
! 841: prev = &a->al_next;
! 842: }
! 843: }
! 844: if (match == NULL) {
! 845: match = *prev = (struct listaddr *)malloc(sizeof(struct listaddr));
! 846: if (match == NULL) {
! 847: logit(LOG_ERR, 0, "Malloc failed in route.c:accept_probe()\n");
! 848: return; /* NOTREACHED */
! 849: }
! 850:
! 851: match->al_next = NULL;
! 852: match->al_addr = src;
! 853: match->al_timer = OLD_NEIGHBOR_EXPIRE_TIME;
! 854: match->al_ctime = now - match->al_timer;
! 855: }
! 856:
! 857: if (match->al_ctime + match->al_timer <= (u_long)now) {
! 858: logit(LOG_WARNING, 0, "Ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s",
! 859: inet_fmt(src, s1, sizeof(s1)), s1);
! 860: match->al_timer *= 2;
! 861: } else {
! 862: IF_DEBUG(DEBUG_PEER) {
! 863: logit(LOG_DEBUG, 0, "Ignoring probe from non-neighbor %s (%d seconds until next warning)",
! 864: inet_fmt(src, s1, sizeof(s1)), match->al_ctime + match->al_timer - now);
! 865: }
! 866: }
! 867:
! 868: return;
! 869: }
! 870:
! 871: update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level);
! 872: }
! 873:
! 874: static int compare_rts(const void *rt1, const void *rt2)
! 875: {
! 876: struct newrt *r1 = (struct newrt *)rt1;
! 877: struct newrt *r2 = (struct newrt *)rt2;
! 878: u_int32 m1 = ntohl(r1->mask);
! 879: u_int32 m2 = ntohl(r2->mask);
! 880: u_int32 o1, o2;
! 881:
! 882: if (m1 > m2)
! 883: return -1;
! 884: if (m1 < m2)
! 885: return 1;
! 886:
! 887: /* masks are equal */
! 888: o1 = ntohl(r1->origin);
! 889: o2 = ntohl(r2->origin);
! 890: if (o1 > o2)
! 891: return -1;
! 892: if (o1 < o2)
! 893: return 1;
! 894:
! 895: return 0;
! 896: }
! 897:
! 898: void blaster_alloc(vifi_t vifi)
! 899: {
! 900: struct uvif *v;
! 901:
! 902: v = &uvifs[vifi];
! 903: if (v->uv_blasterbuf)
! 904: free(v->uv_blasterbuf);
! 905:
! 906: v->uv_blasterlen = 64 * 1024;
! 907: v->uv_blasterbuf = malloc(v->uv_blasterlen);
! 908: v->uv_blastercur = v->uv_blasterend = v->uv_blasterbuf;
! 909: if (v->uv_blastertimer)
! 910: timer_clearTimer(v->uv_blastertimer);
! 911: v->uv_blastertimer = 0;
! 912: }
! 913:
! 914: /*
! 915: * Queue a route report from a route-blaster.
! 916: * If the timer isn't running to process these reports,
! 917: * start it.
! 918: */
! 919: static void queue_blaster_report(vifi_t vifi, u_int32 src, u_int32 dst, char *p, size_t datalen, u_int32 level)
! 920: {
! 921: struct blaster_hdr *bh;
! 922: struct uvif *v;
! 923: int bblen = sizeof(*bh) + ((datalen + 3) & ~3);
! 924:
! 925: v = &uvifs[vifi];
! 926: if (v->uv_blasterend - v->uv_blasterbuf + bblen > v->uv_blasterlen) {
! 927: int end = v->uv_blasterend - v->uv_blasterbuf;
! 928: int cur = v->uv_blastercur - v->uv_blasterbuf;
! 929:
! 930: v->uv_blasterlen *= 2;
! 931: IF_DEBUG(DEBUG_IF) {
! 932: logit(LOG_DEBUG, 0, "Increasing blasterbuf to %d bytes", v->uv_blasterlen);
! 933: }
! 934:
! 935: v->uv_blasterbuf = realloc(v->uv_blasterbuf, v->uv_blasterlen);
! 936: if (v->uv_blasterbuf == NULL) {
! 937: logit(LOG_WARNING, ENOMEM, "Turning off blaster on vif %d", vifi);
! 938: v->uv_blasterlen = 0;
! 939: v->uv_blasterend = v->uv_blastercur = NULL;
! 940: v->uv_flags &= ~VIFF_BLASTER;
! 941: return;
! 942: }
! 943: v->uv_blasterend = v->uv_blasterbuf + end;
! 944: v->uv_blastercur = v->uv_blasterbuf + cur;
! 945: }
! 946: bh = (struct blaster_hdr *)v->uv_blasterend;
! 947: bh->bh_src = src;
! 948: bh->bh_dst = dst;
! 949: bh->bh_level = level;
! 950: bh->bh_datalen = datalen;
! 951: memmove((char *)(bh + 1), p, datalen);
! 952: v->uv_blasterend += bblen;
! 953:
! 954: if (v->uv_blastertimer == 0) {
! 955: int *i;
! 956:
! 957: i = (int *)malloc(sizeof(int *));
! 958: if (i == NULL) {
! 959: logit(LOG_ERR, 0, "Malloc failed in route.c:queue_blaster_report()\n");
! 960: return; /* NOTREACHED */
! 961: }
! 962:
! 963: *i = vifi;
! 964: v->uv_blastertimer = timer_setTimer(5, process_blaster_report, i);
! 965: }
! 966: }
! 967:
! 968: /*
! 969: * Periodic process; process up to 5 of the routes in the route-blaster
! 970: * queue. If there are more routes remaining, reschedule myself to run
! 971: * in 1 second.
! 972: */
! 973: static void process_blaster_report(void *vifip)
! 974: {
! 975: vifi_t vifi = *(int *)vifip;
! 976: struct uvif *v;
! 977: struct blaster_hdr *bh;
! 978: int i;
! 979:
! 980: IF_DEBUG(DEBUG_ROUTE) {
! 981: logit(LOG_DEBUG, 0, "Processing vif %d blasted routes", vifi);
! 982: }
! 983:
! 984: v = &uvifs[vifi];
! 985: for (i = 0; i < 5; i++) {
! 986: if (v->uv_blastercur >= v->uv_blasterend)
! 987: break;
! 988:
! 989: bh = (struct blaster_hdr *)v->uv_blastercur;
! 990: v->uv_blastercur += sizeof(*bh) + ((bh->bh_datalen + 3) & ~3);
! 991:
! 992: accept_report(bh->bh_src, bh->bh_dst, (char *)(bh + 1), -bh->bh_datalen, bh->bh_level);
! 993: }
! 994:
! 995: if (v->uv_blastercur >= v->uv_blasterend) {
! 996: v->uv_blastercur = v->uv_blasterbuf;
! 997: v->uv_blasterend = v->uv_blasterbuf;
! 998: v->uv_blastertimer = 0;
! 999: free(vifip);
! 1000:
! 1001: IF_DEBUG(DEBUG_ROUTE) {
! 1002: logit(LOG_DEBUG, 0, "Finish processing vif %d blaster", vifi);
! 1003: }
! 1004: } else {
! 1005: IF_DEBUG(DEBUG_ROUTE) {
! 1006: logit(LOG_DEBUG, 0, "More blasted routes to come on vif %d", vifi);
! 1007: }
! 1008: v->uv_blastertimer = timer_setTimer(1, process_blaster_report, vifip);
! 1009: }
! 1010: }
! 1011:
! 1012: /*
! 1013: * Process an incoming route report message.
! 1014: * If the report arrived on a vif marked as a "blaster", then just
! 1015: * queue it and return; queue_blaster_report() will schedule it for
! 1016: * processing later. If datalen is negative, then this is actually
! 1017: * a queued report so actually process it instead of queueing it.
! 1018: */
! 1019: void accept_report(u_int32 src, u_int32 dst, char *p, size_t datalen, u_int32 level)
! 1020: {
! 1021: vifi_t vifi;
! 1022: size_t width, i, nrt = 0;
! 1023: int metric;
! 1024: u_int32 mask;
! 1025: u_int32 origin;
! 1026: static struct newrt rt[MAX_NUM_RT]; /* Use heap instead of stack */
! 1027: struct listaddr *nbr;
! 1028:
! 1029: /*
! 1030: * Emulate a stack variable. We use the heap insted of the stack
! 1031: * to prevent stack overflow on systems that cannot do stack realloc
! 1032: * at runtime, e.g., non-MMU Linux systems.
! 1033: */
! 1034: memset(rt, 0, MAX_NUM_RT * sizeof(rt[0]));
! 1035:
! 1036: if ((vifi = find_vif(src, dst)) == NO_VIF) {
! 1037: logit(LOG_INFO, 0, "Ignoring route report from non-neighbor %s",
! 1038: inet_fmt(src, s1, sizeof(s1)));
! 1039: return;
! 1040: }
! 1041:
! 1042: if (uvifs[vifi].uv_flags & VIFF_BLASTER) {
! 1043: if (datalen > 0) {
! 1044: queue_blaster_report(vifi, src, dst, p, datalen, level);
! 1045: return;
! 1046: } else {
! 1047: datalen = -datalen;
! 1048: }
! 1049: }
! 1050:
! 1051: nbr = update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level);
! 1052: if (!nbr)
! 1053: return;
! 1054:
! 1055: if (datalen > 2 * 4096) {
! 1056: logit(LOG_INFO, 0, "Ignoring oversized (%d bytes) route report from %s",
! 1057: datalen, inet_fmt(src, s1, sizeof(s1)));
! 1058: return;
! 1059: }
! 1060:
! 1061: while (datalen > 0 && nrt < MAX_NUM_RT) { /* Loop through per-mask lists. */
! 1062: if (datalen < 3) {
! 1063: logit(LOG_WARNING, 0, "Received truncated route report from %s",
! 1064: inet_fmt(src, s1, sizeof(s1)));
! 1065: return;
! 1066: }
! 1067:
! 1068: ((u_char *)&mask)[0] = 0xff; width = 1;
! 1069: if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
! 1070: if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
! 1071: if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
! 1072: if (!inet_valid_mask(ntohl(mask))) {
! 1073: logit(LOG_WARNING, 0, "%s reports bogus netmask 0x%08x (%s)",
! 1074: inet_fmt(src, s1, sizeof(s1)), ntohl(mask), inet_fmt(mask, s2, sizeof(s2)));
! 1075: return;
! 1076: }
! 1077: datalen -= 3;
! 1078:
! 1079: do { /* Loop through (origin, metric) pairs */
! 1080: if (datalen < width + 1) {
! 1081: logit(LOG_WARNING, 0, "Received truncated route report from %s",
! 1082: inet_fmt(src, s1, sizeof(s1)));
! 1083: return;
! 1084: }
! 1085: origin = 0;
! 1086: for (i = 0; i < width; ++i)
! 1087: ((char *)&origin)[i] = *p++;
! 1088: metric = *p++;
! 1089: datalen -= width + 1;
! 1090: rt[nrt].mask = mask;
! 1091: rt[nrt].origin = origin;
! 1092: rt[nrt].metric = (metric & 0x7f);
! 1093: ++nrt;
! 1094: } while (!(metric & 0x80) && nrt < MAX_NUM_RT);
! 1095: }
! 1096:
! 1097: qsort((char *)rt, nrt, sizeof(rt[0]), compare_rts);
! 1098: start_route_updates();
! 1099:
! 1100: /*
! 1101: * If the last entry is default, change mask from 0xff000000 to 0
! 1102: */
! 1103: if (nrt > 0 && rt[nrt - 1].origin == 0)
! 1104: rt[nrt - 1].mask = 0;
! 1105:
! 1106: IF_DEBUG(DEBUG_ROUTE) {
! 1107: logit(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
! 1108: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 1109: }
! 1110:
! 1111: for (i = 0; i < nrt; ++i) {
! 1112: if (i > 0 && rt[i].origin == rt[i - 1].origin && rt[i].mask == rt[i - 1].mask) {
! 1113: logit(LOG_WARNING, 0, "%s reports duplicate route for %s",
! 1114: inet_fmt(src, s1, sizeof(s1)), inet_fmts(rt[i].origin, rt[i].mask, s2, sizeof(s2)));
! 1115: continue;
! 1116: }
! 1117: /* Only filter non-poisoned updates. */
! 1118: if (uvifs[vifi].uv_filter && rt[i].metric < UNREACHABLE) {
! 1119: struct vf_element *vfe;
! 1120: int match = 0;
! 1121:
! 1122: for (vfe = uvifs[vifi].uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
! 1123: if (vfe->vfe_flags & VFEF_EXACT) {
! 1124: if ((vfe->vfe_addr == rt[i].origin) && (vfe->vfe_mask == rt[i].mask)) {
! 1125: match = 1;
! 1126: break;
! 1127: }
! 1128: } else {
! 1129: if ((rt[i].origin & vfe->vfe_mask) == vfe->vfe_addr) {
! 1130: match = 1;
! 1131: break;
! 1132: }
! 1133: }
! 1134: }
! 1135: if ((uvifs[vifi].uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
! 1136: (uvifs[vifi].uv_filter->vf_type == VFT_DENY && match == 1)) {
! 1137: IF_DEBUG(DEBUG_ROUTE) {
! 1138: logit(LOG_DEBUG, 0, "%s skipped on vif %d because it %s %s",
! 1139: inet_fmts(rt[i].origin, rt[i].mask, s1, sizeof(s1)),
! 1140: vifi, match ? "matches" : "doesn't match",
! 1141: match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2, sizeof(s2))
! 1142: : "the filter");
! 1143: }
! 1144: #if 0
! 1145: rt[i].metric += vfe->vfe_addmetric;
! 1146: if (rt[i].metric > UNREACHABLE)
! 1147: #endif
! 1148: rt[i].metric = UNREACHABLE;
! 1149: }
! 1150: }
! 1151: update_route(rt[i].origin, rt[i].mask, rt[i].metric, src, vifi, nbr);
! 1152: }
! 1153:
! 1154: if (routes_changed && !delay_change_reports)
! 1155: report_to_all_neighbors(CHANGED_ROUTES);
! 1156: }
! 1157:
! 1158:
! 1159: /*
! 1160: * Send a route report message to destination 'dst', via virtual interface
! 1161: * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
! 1162: */
! 1163: void report(int which_routes, vifi_t vifi, u_int32 dst)
! 1164: {
! 1165: struct rtentry *this;
! 1166: int i;
! 1167:
! 1168: this = rt_end;
! 1169: while (this && this != routing_table) {
! 1170: i = report_chunk(which_routes, this, vifi, dst);
! 1171: while (i-- > 0)
! 1172: this = this->rt_prev;
! 1173: }
! 1174: }
! 1175:
! 1176:
! 1177: /*
! 1178: * Send a route report message to all neighboring routers.
! 1179: * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
! 1180: */
! 1181: void report_to_all_neighbors(int which_routes)
! 1182: {
! 1183: vifi_t vifi;
! 1184: struct uvif *v;
! 1185: struct rtentry *r;
! 1186: int routes_changed_before;
! 1187:
! 1188: /*
! 1189: * Remember the state of the global routes_changed flag before
! 1190: * generating the reports, and clear the flag.
! 1191: */
! 1192: routes_changed_before = routes_changed;
! 1193: routes_changed = FALSE;
! 1194:
! 1195:
! 1196: for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
! 1197: if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
! 1198: report(which_routes, vifi, v->uv_dst_addr);
! 1199: }
! 1200: }
! 1201:
! 1202: /*
! 1203: * If there were changed routes before we sent the reports AND
! 1204: * if no new changes occurred while sending the reports, clear
! 1205: * the change flags in the individual route entries. If changes
! 1206: * did occur while sending the reports, new reports will be
! 1207: * generated at the next timer interrupt.
! 1208: */
! 1209: if (routes_changed_before && !routes_changed) {
! 1210: for (r = routing_table; r != NULL; r = r->rt_next) {
! 1211: r->rt_flags &= ~RTF_CHANGED;
! 1212: }
! 1213: }
! 1214:
! 1215: /*
! 1216: * Set a flag to inhibit further reports of changed routes until the
! 1217: * next timer interrupt. This is to alleviate update storms.
! 1218: */
! 1219: delay_change_reports = TRUE;
! 1220: }
! 1221:
! 1222: /*
! 1223: * Send a route report message to destination 'dst', via virtual interface
! 1224: * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
! 1225: */
! 1226: static int report_chunk(int which_routes, struct rtentry *start_rt, vifi_t vifi, u_int32 UNUSED dst)
! 1227: {
! 1228: struct rtentry *r;
! 1229: char *p;
! 1230: int i;
! 1231: size_t nrt = 0;
! 1232: struct uvif *v = &uvifs[vifi];
! 1233: int datalen = 0;
! 1234: int width = 0;
! 1235: u_int32 mask = 0;
! 1236: u_int32 src;
! 1237: int admetric = v->uv_admetric;
! 1238: int metric;
! 1239:
! 1240: src = v->uv_lcl_addr;
! 1241: p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
! 1242:
! 1243: for (r = start_rt; r != routing_table; r = r->rt_prev) {
! 1244: if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) {
! 1245: nrt++;
! 1246: continue;
! 1247: }
! 1248:
! 1249: /*
! 1250: * Do not poison-reverse a route for a directly-connected
! 1251: * subnetwork on that subnetwork. This can cause loops when
! 1252: * some router on the subnetwork is misconfigured.
! 1253: */
! 1254: if (r->rt_gateway == 0 && r->rt_parent == vifi) {
! 1255: nrt++;
! 1256: continue;
! 1257: }
! 1258:
! 1259: if (v->uv_filter && v->uv_filter->vf_flags & VFF_BIDIR) {
! 1260: struct vf_element *vfe;
! 1261: int match = 0;
! 1262:
! 1263: for (vfe = v->uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
! 1264: if (vfe->vfe_flags & VFEF_EXACT) {
! 1265: if ((vfe->vfe_addr == r->rt_origin) &&
! 1266: (vfe->vfe_mask == r->rt_originmask)) {
! 1267: match = 1;
! 1268: break;
! 1269: }
! 1270: } else {
! 1271: if ((r->rt_origin & vfe->vfe_mask) == vfe->vfe_addr) {
! 1272: match = 1;
! 1273: break;
! 1274: }
! 1275: }
! 1276: }
! 1277: if ((v->uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
! 1278: (v->uv_filter->vf_type == VFT_DENY && match == 1)) {
! 1279: IF_DEBUG(DEBUG_ROUTE) {
! 1280: logit(LOG_DEBUG, 0, "%s not reported on vif %d because it %s %s",
! 1281: RT_FMT(r, s1), vifi, match ? "matches" : "doesn't match",
! 1282: match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2, sizeof(s2))
! 1283: : "the filter");
! 1284: }
! 1285: nrt++;
! 1286: continue;
! 1287: }
! 1288: }
! 1289:
! 1290: /*
! 1291: * If there is no room for this route in the current message,
! 1292: * send it & return how many routes we sent.
! 1293: */
! 1294: if (datalen + ((r->rt_originmask == mask)
! 1295: ? (width + 1)
! 1296: : (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
! 1297: *(p-1) |= 0x80;
! 1298: send_on_vif(v, 0, DVMRP_REPORT, datalen);
! 1299:
! 1300: return nrt;
! 1301: }
! 1302:
! 1303: if (r->rt_originmask != mask || datalen == 0) {
! 1304: mask = r->rt_originmask;
! 1305: width = r->rt_originwidth;
! 1306: if (datalen != 0) *(p-1) |= 0x80;
! 1307: *p++ = ((char *)&mask)[1];
! 1308: *p++ = ((char *)&mask)[2];
! 1309: *p++ = ((char *)&mask)[3];
! 1310: datalen += 3;
! 1311: }
! 1312: for (i = 0; i < width; ++i)
! 1313: *p++ = ((char *)&(r->rt_origin))[i];
! 1314:
! 1315: metric = r->rt_metric + admetric;
! 1316: if (metric > UNREACHABLE)
! 1317: metric = UNREACHABLE;
! 1318:
! 1319: if (r->rt_parent != vifi && AVOID_TRANSIT(vifi, r))
! 1320: metric = UNREACHABLE;
! 1321:
! 1322: *p++ = (r->rt_parent == vifi && metric != UNREACHABLE)
! 1323: ? (char)(metric + UNREACHABLE) /* "poisoned reverse" */
! 1324: : (char)(metric);
! 1325: ++nrt;
! 1326: datalen += width + 1;
! 1327: }
! 1328: if (datalen != 0) {
! 1329: *(p-1) |= 0x80;
! 1330: send_on_vif(v, 0, DVMRP_REPORT, datalen);
! 1331: }
! 1332:
! 1333: return nrt;
! 1334: }
! 1335:
! 1336: /*
! 1337: * send the next chunk of our routing table to all neighbors.
! 1338: * return the length of the smallest chunk we sent out.
! 1339: */
! 1340: int report_next_chunk(void)
! 1341: {
! 1342: vifi_t vifi;
! 1343: struct uvif *v;
! 1344: struct rtentry *sr;
! 1345: int i, n = 0, min = 20000;
! 1346: static int start_rt;
! 1347:
! 1348: if (nroutes <= 0)
! 1349: return 0;
! 1350:
! 1351: /*
! 1352: * find this round's starting route.
! 1353: */
! 1354: for (sr = rt_end, i = start_rt; sr && --i >= 0; ) {
! 1355: sr = sr->rt_prev;
! 1356: if (sr == routing_table)
! 1357: sr = rt_end;
! 1358: }
! 1359:
! 1360: /*
! 1361: * send one chunk of routes starting at this round's start to
! 1362: * all our neighbors.
! 1363: */
! 1364: for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
! 1365: if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
! 1366: n = report_chunk(ALL_ROUTES, sr, vifi, v->uv_dst_addr);
! 1367: if (n < min)
! 1368: min = n;
! 1369: }
! 1370: }
! 1371: if (min == 20000)
! 1372: min = 0; /* Neighborless router didn't send any routes */
! 1373:
! 1374: n = min;
! 1375: IF_DEBUG(DEBUG_ROUTE) {
! 1376: logit(LOG_INFO, 0, "update %d starting at %d of %d",
! 1377: n, (nroutes - start_rt), nroutes);
! 1378: }
! 1379:
! 1380: start_rt = (start_rt + n) % nroutes;
! 1381:
! 1382: return n;
! 1383: }
! 1384:
! 1385:
! 1386: /*
! 1387: * Print the contents of the routing table on file 'fp'.
! 1388: */
! 1389: void dump_routes(FILE *fp)
! 1390: {
! 1391: struct rtentry *r;
! 1392: vifi_t i;
! 1393:
! 1394: fprintf(fp, "Multicast Routing Table (%u entr%s)\n", nroutes, nroutes == 1 ? "y" : "ies");
! 1395: fputs(" Origin-Subnet From-Gateway Metric Tmr Fl In-Vif Out-Vifs\n", fp);
! 1396:
! 1397: for (r = routing_table; r; r = r->rt_next) {
! 1398: fprintf(fp, " %-18s %-15s ",
! 1399: inet_fmts(r->rt_origin, r->rt_originmask, s1, sizeof(s1)),
! 1400: (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2, sizeof(s2)));
! 1401:
! 1402: fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
! 1403: r->rt_metric);
! 1404:
! 1405: fprintf(fp, " %3u %c%c %3u ", r->rt_timer,
! 1406: (r->rt_flags & RTF_CHANGED) ? 'C' : '.',
! 1407: (r->rt_flags & RTF_HOLDDOWN) ? 'H' : '.',
! 1408: r->rt_parent);
! 1409:
! 1410: for (i = 0; i < numvifs; ++i) {
! 1411: struct listaddr *n;
! 1412: char l = '[';
! 1413:
! 1414: if (VIFM_ISSET(i, r->rt_children)) {
! 1415: if ((uvifs[i].uv_flags & VIFF_TUNNEL) &&
! 1416: !NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
! 1417: /* Don't print out parenthood of a leaf tunnel. */
! 1418: continue;
! 1419:
! 1420: fprintf(fp, " %u", i);
! 1421: if (!NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
! 1422: fprintf(fp, "*");
! 1423:
! 1424: for (n = uvifs[i].uv_neighbors; n; n = n->al_next) {
! 1425: if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
! 1426: fprintf(fp, "%c%d", l, n->al_index);
! 1427: l = ',';
! 1428: }
! 1429: }
! 1430:
! 1431: if (l == ',')
! 1432: fprintf(fp, "]");
! 1433: }
! 1434: }
! 1435: fprintf(fp, "\n");
! 1436: }
! 1437: fprintf(fp, "\n");
! 1438: }
! 1439:
! 1440: struct rtentry *determine_route(u_int32 src)
! 1441: {
! 1442: struct rtentry *rt;
! 1443:
! 1444: for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
! 1445: if (rt->rt_origin == (src & rt->rt_originmask) &&
! 1446: rt->rt_metric != UNREACHABLE)
! 1447: break;
! 1448: }
! 1449:
! 1450: return rt;
! 1451: }
! 1452:
! 1453: /**
! 1454: * Local Variables:
! 1455: * version-control: t
! 1456: * indent-tabs-mode: t
! 1457: * c-file-style: "ellemtel"
! 1458: * c-basic-offset: 4
! 1459: * End:
! 1460: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>