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