diff options
author | Bill Fenner <fenner@FreeBSD.org> | 1999-01-20 07:44:18 +0000 |
---|---|---|
committer | Bill Fenner <fenner@FreeBSD.org> | 1999-01-20 07:44:18 +0000 |
commit | 1f25327484733d0bec3b33044da7bd5d82ad007f (patch) | |
tree | 19c94eeb7ba2b59b0ad53ae0864ebf06723a8afa /usr.sbin/mrouted/route.c | |
parent | ce595def0708b60ce4dde9002ac206fae20be240 (diff) |
Notes
Diffstat (limited to 'usr.sbin/mrouted/route.c')
-rw-r--r-- | usr.sbin/mrouted/route.c | 894 |
1 files changed, 604 insertions, 290 deletions
diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c index c2b6b9e8b932a..e43dac68f1042 100644 --- a/usr.sbin/mrouted/route.c +++ b/usr.sbin/mrouted/route.c @@ -7,12 +7,16 @@ * Leland Stanford Junior University. * * - * $Id: route.c,v 3.8 1995/11/29 22:36:57 fenner Rel $ + * route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp */ #include "defs.h" +#ifndef lint +static char rcsid[] = "@(#) $Id: \ +route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp $"; +#endif /* * This define statement saves a lot of space later @@ -42,13 +46,89 @@ unsigned int nroutes; /* current number of route entries */ * Private functions. */ static int init_children_and_leaves __P((struct rtentry *r, - vifi_t parent)); + vifi_t parent, int first)); static int find_route __P((u_int32 origin, u_int32 mask)); static void create_route __P((u_int32 origin, u_int32 mask)); static void discard_route __P((struct rtentry *prev_r)); static int compare_rts __P((const void *rt1, const void *rt2)); -static int report_chunk __P((struct rtentry *start_rt, vifi_t vifi, +static int report_chunk __P((int, struct rtentry *start_rt, vifi_t vifi, u_int32 dst)); +static void queue_blaster_report __P((vifi_t, u_int32, u_int32, char *, + int, u_int32)); +static void process_blaster_report __P((void *)); + +#ifdef SNMP +#include <sys/types.h> +#include "snmp.h" + +/* + * Return pointer to a specific route entry. This must be a separate + * function from find_route() which modifies rtp. + */ +struct rtentry * +snmp_find_route(src, mask) + register u_int32 src, mask; +{ + register struct rtentry *rt; + + for (rt = routing_table; rt; rt = rt->rt_next) { + if (src == rt->rt_origin && mask == rt->rt_originmask) + return rt; + } + return NULL; +} + +/* + * Find next route entry > specification + */ +int +next_route(rtpp, src, mask) + struct rtentry **rtpp; + u_int32 src; + u_int32 mask; +{ + struct rtentry *rt, *rbest = NULL; + + /* Among all entries > spec, find "lowest" one in order */ + for (rt = routing_table; rt; rt=rt->rt_next) { + if ((ntohl(rt->rt_origin) > ntohl(src) + || (ntohl(rt->rt_origin) == ntohl(src) + && ntohl(rt->rt_originmask) > ntohl(mask))) + && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin)) + || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin) + && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask)))) + rbest = rt; + } + (*rtpp) = rbest; + return (*rtpp)!=0; +} + +/* + * Given a routing table entry, and a vifi, find the next vifi/entry + */ +int +next_route_child(rtpp, src, mask, vifi) + struct rtentry **rtpp; + u_int32 src; + u_int32 mask; + vifi_t *vifi; /* vif at which to start looking */ +{ + /* Get (S,M) entry */ + if (!((*rtpp) = snmp_find_route(src,mask))) + if (!next_route(rtpp, src, mask)) + return 0; + + /* Continue until we get one with a valid next vif */ + do { + for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++) + if (VIFM_ISSET(*vifi, (*rtpp)->rt_children)) + return 1; + *vifi = 0; + } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) ); + + return 0; +} +#endif /* * Initialize the routing table and associated variables. @@ -65,54 +145,50 @@ init_routes() /* - * Initialize the children and leaf bits for route 'r', along with the - * associated dominant, subordinate, and leaf timing data structures. - * Return TRUE if this changes the value of either the children or - * leaf bitmaps for 'r'. + * Initialize the children bits for route 'r', along with the + * associated dominant and subordinate data structures. + * If first is set, initialize dominants, otherwise keep old + * dominants on non-parent interfaces. + * XXX Does this need a return value? */ static int -init_children_and_leaves(r, parent) +init_children_and_leaves(r, parent, first) register struct rtentry *r; register vifi_t parent; + int first; { register vifi_t vifi; register struct uvif *v; - vifbitmap_t old_children, old_leaves; + vifbitmap_t old_children; + nbrbitmap_t old_subords; VIFM_COPY(r->rt_children, old_children); - VIFM_COPY(r->rt_leaves, old_leaves ); + NBRM_COPY(r->rt_subordinates, old_subords); VIFM_CLRALL(r->rt_children); - VIFM_CLRALL(r->rt_leaves); - r->rt_flags &= ~RTF_LEAF_TIMING; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - r->rt_dominants [vifi] = 0; - r->rt_subordinates[vifi] = 0; - - if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { + if (first || vifi == parent) + r->rt_dominants [vifi] = 0; + if (vifi == parent || uvifs[vifi].uv_flags & VIFF_NOFLOOD || + AVOID_TRANSIT(vifi, r) || (!first && r->rt_dominants[vifi])) + NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + else + NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + + if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED)) && + !(!first && r->rt_dominants[vifi])) { VIFM_SET(vifi, r->rt_children); - if (v->uv_neighbors == NULL) { - VIFM_SET(vifi, r->rt_leaves); - r->rt_leaf_timers[vifi] = 0; - } - else { - r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; - r->rt_flags |= RTF_LEAF_TIMING; - } - } - else { - r->rt_leaf_timers[vifi] = 0; } } return (!VIFM_SAME(r->rt_children, old_children) || - !VIFM_SAME(r->rt_leaves, old_leaves)); + !NBRM_SAME(r->rt_subordinates, old_subords)); } /* - * A new vif has come up -- update the children and leaf bitmaps in all route + * A new vif has come up -- update the children bitmaps in all route * entries to take that into account. */ void @@ -128,17 +204,9 @@ add_vif_to_routes(vifi) !VIFM_ISSET(vifi, r->rt_children)) { VIFM_SET(vifi, r->rt_children); r->rt_dominants [vifi] = 0; - r->rt_subordinates[vifi] = 0; - if (v->uv_neighbors == NULL) { - VIFM_SET(vifi, r->rt_leaves); - r->rt_leaf_timers[vifi] = 0; - } - else { - VIFM_CLR(vifi, r->rt_leaves); - r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; - r->rt_flags |= RTF_LEAF_TIMING; - } - update_table_entry(r); + /*XXX isn't uv_nbrmap going to be empty?*/ + NBRM_CLRMASK(r->rt_subordinates, v->uv_nbrmap); + update_table_entry(r, r->rt_gateway); } } } @@ -166,10 +234,8 @@ delete_vif_from_routes(vifi) } else if (VIFM_ISSET(vifi, r->rt_children)) { VIFM_CLR(vifi, r->rt_children); - VIFM_CLR(vifi, r->rt_leaves); - r->rt_subordinates[vifi] = 0; - r->rt_leaf_timers [vifi] = 0; - update_table_entry(r); + NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + update_table_entry(r, r->rt_gateway); } else { r->rt_dominants[vifi] = 0; @@ -180,14 +246,42 @@ delete_vif_from_routes(vifi) /* + * A new neighbor has come up. If we're flooding on the neighbor's + * vif, mark that neighbor as subordinate for all routes whose parent + * is not this vif. + */ +void +add_neighbor_to_routes(vifi, index) + register vifi_t vifi; + register int index; +{ + register struct rtentry *r; + register struct uvif *v; + + v = &uvifs[vifi]; + if (v->uv_flags & VIFF_NOFLOOD) + return; + for (r = routing_table; r != NULL; r = r->rt_next) { + if (r->rt_metric != UNREACHABLE && r->rt_parent != vifi && + !AVOID_TRANSIT(vifi, r)) { + NBRM_SET(index, r->rt_subordinates); + update_table_entry(r, r->rt_gateway); + } + } +} + + +/* * A neighbor has failed or become unreachable. If that neighbor was * considered a dominant or subordinate router in any route entries, - * take appropriate action. + * take appropriate action. Expire all routes this neighbor advertised + * to us. */ void -delete_neighbor_from_routes(addr, vifi) +delete_neighbor_from_routes(addr, vifi, index) register u_int32 addr; register vifi_t vifi; + int index; { register struct rtentry *r; register struct uvif *v; @@ -195,37 +289,24 @@ delete_neighbor_from_routes(addr, vifi) v = &uvifs[vifi]; for (r = routing_table; r != NULL; r = r->rt_next) { if (r->rt_metric != UNREACHABLE) { - if (r->rt_dominants[vifi] == addr) { + if (r->rt_parent == vifi && r->rt_gateway == addr) { + del_table_entry(r, 0, DEL_ALL_ROUTES); + r->rt_timer = ROUTE_EXPIRE_TIME; + r->rt_metric = UNREACHABLE; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + } else if (r->rt_dominants[vifi] == addr) { VIFM_SET(vifi, r->rt_children); - r->rt_dominants [vifi] = 0; - r->rt_subordinates[vifi] = 0; - if (v->uv_neighbors == NULL) { - VIFM_SET(vifi, r->rt_leaves); - r->rt_leaf_timers[vifi] = 0; - } - else { - VIFM_CLR(vifi, r->rt_leaves); - r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; - r->rt_flags |= RTF_LEAF_TIMING; - } - update_table_entry(r); - } - else if (r->rt_subordinates[vifi] == addr) { - r->rt_subordinates[vifi] = 0; - if (v->uv_neighbors == NULL) { - VIFM_SET(vifi, r->rt_leaves); - update_table_entry(r); - } - else { - r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; - r->rt_flags |= RTF_LEAF_TIMING; - } - } - else if (v->uv_neighbors == NULL && - r->rt_leaf_timers[vifi] != 0) { - VIFM_SET(vifi, r->rt_leaves); - r->rt_leaf_timers[vifi] = 0; - update_table_entry(r); + r->rt_dominants[vifi] = 0; + if ((uvifs[vifi].uv_flags & VIFF_NOFLOOD) || + AVOID_TRANSIT(vifi, r)) + NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + else + NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + update_table_entry(r, r->rt_gateway); + } else if (NBRM_ISSET(index, r->rt_subordinates)) { + NBRM_CLR(index, r->rt_subordinates); + update_table_entry(r, r->rt_gateway); } } } @@ -296,8 +377,7 @@ create_route(origin, mask) register struct rtentry *r; if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) + - (2 * numvifs * sizeof(u_int32)) + - (numvifs * sizeof(u_int)))) == NULL) { + (numvifs * sizeof(u_int32)))) == NULL) { log(LOG_ERR, 0, "ran out of memory"); /* fatal */ } r->rt_origin = origin; @@ -308,9 +388,11 @@ create_route(origin, mask) else r->rt_originwidth = 1; r->rt_flags = 0; r->rt_dominants = (u_int32 *)(r + 1); - r->rt_subordinates = (u_int32 *)(r->rt_dominants + numvifs); - r->rt_leaf_timers = (u_int *)(r->rt_subordinates + numvifs); + bzero(r->rt_dominants, numvifs * sizeof(u_int32)); r->rt_groups = NULL; + VIFM_CLRALL(r->rt_children); + NBRM_CLRALL(r->rt_subordinates); + NBRM_CLRALL(r->rt_subordadv); r->rt_next = rtp->rt_next; rtp->rt_next = r; @@ -334,6 +416,8 @@ discard_route(prev_r) register struct rtentry *r; r = prev_r->rt_next; + uvifs[r->rt_parent].uv_nroutes--; + /*???nbr???.al_nroutes--;*/ prev_r->rt_next = r->rt_next; if (prev_r->rt_next != NULL) (prev_r->rt_next)->rt_prev = prev_r; @@ -351,11 +435,12 @@ discard_route(prev_r) * to indicate a change of status of one of our own interfaces. */ void -update_route(origin, mask, metric, src, vifi) +update_route(origin, mask, metric, src, vifi, n) u_int32 origin, mask; u_int metric; u_int32 src; vifi_t vifi; + struct listaddr *n; { register struct rtentry *r; u_int adj_metric; @@ -393,18 +478,17 @@ update_route(origin, mask, metric, src, vifi) return; } + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s advertises new route %s", + inet_fmt(src, s1), inet_fmts(origin, mask, s2)); + /* * OK, create the new routing entry. 'rtp' will be left pointing * to the new entry. */ create_route(origin, mask); - - /* - * Now "steal away" any sources that belong under this route - * by deleting any cache entries they might have created - * and allowing the kernel to re-request them. - */ - steal_sources(rtp); + uvifs[vifi].uv_nroutes++; + /*n->al_nroutes++;*/ rtp->rt_metric = UNREACHABLE; /* temporary; updated below */ } @@ -422,15 +506,32 @@ update_route(origin, mask, metric, src, vifi) if (adj_metric == UNREACHABLE) return; - r->rt_parent = vifi; - init_children_and_leaves(r, vifi); + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s advertises %s with adj_metric %d (ours was %d)", + inet_fmt(src, s1), inet_fmts(origin, mask, s2), + adj_metric, r->rt_metric); + /* + * Now "steal away" any sources that belong under this route + * by deleting any cache entries they might have created + * and allowing the kernel to re-request them. + * + * If we haven't performed final initialization yet and are + * just collecting the routing table, we can't have any + * sources so we don't perform this step. + */ + if (did_final_init) + steal_sources(rtp); + + r->rt_parent = vifi; r->rt_gateway = src; + init_children_and_leaves(r, vifi, 1); + r->rt_timer = 0; r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; - update_table_entry(r); + update_table_entry(r, r->rt_gateway); } else if (src == r->rt_gateway) { /* @@ -442,6 +543,12 @@ update_route(origin, mask, metric, src, vifi) * our entry accordingly. */ r->rt_timer = 0; + + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)", + inet_fmt(src, s1), inet_fmts(origin, mask, s2), + adj_metric, r->rt_metric); + if (adj_metric == r->rt_metric) return; @@ -449,11 +556,6 @@ update_route(origin, mask, metric, src, vifi) del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_timer = ROUTE_EXPIRE_TIME; } - else if (adj_metric < r->rt_metric) { - if (init_children_and_leaves(r, vifi)) { - update_table_entry(r); - } - } r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; @@ -479,16 +581,29 @@ update_route(origin, mask, metric, src, vifi) * IP address than the gateway associated with the route entry. * Did you get all that? */ - if (r->rt_parent != vifi || adj_metric < r->rt_metric) { - /* - * XXX Why do we do this if we are just changing the metric? - */ - r->rt_parent = vifi; - if (init_children_and_leaves(r, vifi)) { - update_table_entry(r); - } + u_int32 old_gateway; + vifi_t old_parent; + old_gateway = r->rt_gateway; + old_parent = r->rt_parent; + r->rt_gateway = src; + r->rt_parent = vifi; + + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s (new parent) on vif %d advertises %s with adj_metric %d (old parent was %s on vif %d, metric %d)", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + adj_metric, inet_fmt(old_gateway, s3), old_parent, + r->rt_metric); + + if (old_parent != vifi) { + init_children_and_leaves(r, vifi, 0); + uvifs[old_parent].uv_nroutes--; + uvifs[vifi].uv_nroutes++; + } + if (old_gateway != src) { + update_table_entry(r, old_gateway); + /*???old_gateway???->al_nroutes--;*/ + /*n->al_nroutes++;*/ } - r->rt_gateway = src; r->rt_timer = 0; r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; @@ -497,9 +612,18 @@ update_route(origin, mask, metric, src, vifi) else if (vifi != r->rt_parent) { /* * The report came from a vif other than the route's parent vif. - * Update the children and leaf info, if necessary. + * Update the children info, if necessary. */ - if (VIFM_ISSET(vifi, r->rt_children)) { + if (AVOID_TRANSIT(vifi, r)) { + /* + * The route's parent is a vif from which we're not supposed + * to transit onto this vif. Simply ignore the update. + */ + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric); + } else if (VIFM_ISSET(vifi, r->rt_children)) { /* * Vif is a child vif for this route. */ @@ -512,42 +636,51 @@ update_route(origin, mask, metric, src, vifi) * and vif is no longer a child for me. */ VIFM_CLR(vifi, r->rt_children); - VIFM_CLR(vifi, r->rt_leaves); r->rt_dominants [vifi] = src; - r->rt_subordinates[vifi] = 0; - r->rt_leaf_timers [vifi] = 0; - update_table_entry(r); + /* XXX + * We don't necessarily want to forget about subordinateness + * so that we can become the dominant quickly if the current + * dominant fails. + */ + NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + update_table_entry(r, r->rt_gateway); + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s on vif %d becomes dominant for %s with metric %d", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric); } else if (metric > UNREACHABLE) { /* "poisoned reverse" */ /* * Neighbor considers this vif to be on path to route's - * origin; if no subordinate recorded, record this neighbor - * as subordinate and clear the leaf flag. + * origin; record this neighbor as subordinate */ - if (r->rt_subordinates[vifi] == 0) { - VIFM_CLR(vifi, r->rt_leaves); - r->rt_subordinates[vifi] = src; - r->rt_leaf_timers [vifi] = 0; - update_table_entry(r); + if (!NBRM_ISSET(n->al_index, r->rt_subordinates)) { + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric - UNREACHABLE); + NBRM_SET(n->al_index, r->rt_subordinates); + update_table_entry(r, r->rt_gateway); + } else { + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric - UNREACHABLE); } + NBRM_SET(n->al_index, r->rt_subordadv); } - else if (src == r->rt_subordinates[vifi]) { + else if (NBRM_ISSET(n->al_index, r->rt_subordinates)) { /* * Current subordinate no longer considers this vif to be on * path to route's origin; it is no longer a subordinate - * router, and we set the leaf confirmation timer to give - * us time to hear from other subordinates. + * router. */ - r->rt_subordinates[vifi] = 0; - if (uvifs[vifi].uv_neighbors == NULL || - uvifs[vifi].uv_neighbors->al_next == NULL) { - VIFM_SET(vifi, r->rt_leaves); - update_table_entry(r); - } - else { - r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME; - r->rt_flags |= RTF_LEAF_TIMING; - } + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s on vif %d is no longer a subordinate for %s with metric %d", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric); + NBRM_CLR(n->al_index, r->rt_subordinates); + update_table_entry(r, r->rt_gateway); } } @@ -560,20 +693,26 @@ update_route(origin, mask, metric, src, vifi) * (or same metric and lower IP address); we adopt the vif * as our own child. */ + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric); VIFM_SET(vifi, r->rt_children); - r->rt_dominants [vifi] = 0; + r->rt_dominants[vifi] = 0; + if (uvifs[vifi].uv_flags & VIFF_NOFLOOD) + NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); + else + NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap); if (metric > UNREACHABLE) { - r->rt_subordinates[vifi] = src; - } - else if (uvifs[vifi].uv_neighbors == NULL || - uvifs[vifi].uv_neighbors->al_next == NULL) { - VIFM_SET(vifi, r->rt_leaves); - } - else { - r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; - r->rt_flags |= RTF_LEAF_TIMING; + NBRM_SET(n->al_index, r->rt_subordinates); + NBRM_SET(n->al_index, r->rt_subordadv); } - update_table_entry(r); + update_table_entry(r, r->rt_gateway); + } else { + IF_DEBUG(DEBUG_RTDETAIL) + log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored)", + inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2), + metric); } } } @@ -587,47 +726,13 @@ age_routes() { register struct rtentry *r; register struct rtentry *prev_r; - register vifi_t vifi; + extern u_long virtual_time; /* from main.c */ for (prev_r = RT_ADDR, r = routing_table; r != NULL; prev_r = r, r = r->rt_next) { - if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) { - /* - * Route is still good; see if any leaf timers need to be - * advanced. - */ - if (r->rt_flags & RTF_LEAF_TIMING) { - r->rt_flags &= ~RTF_LEAF_TIMING; - for (vifi = 0; vifi < numvifs; ++vifi) { - if (r->rt_leaf_timers[vifi] != 0) { - /* - * Unlike other timers, leaf timers decrement. - */ - if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){ -#ifdef NOTYET - /* If the vif is a physical leaf but has neighbors, - * it is not a tree leaf. If I am a leaf, then no - * interface with neighbors is a tree leaf. */ - if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) || - (vifs_with_neighbors == 1)) && - (uvifs[vifi].uv_neighbors != NULL))) { -#endif - VIFM_SET(vifi, r->rt_leaves); - update_table_entry(r); -#ifdef NOTYET - } -#endif - } - else { - r->rt_flags |= RTF_LEAF_TIMING; - } - } - } - } - } - else if (r->rt_timer >= ROUTE_DISCARD_TIME) { + if ((r->rt_timer += TIMER_INTERVAL) >= ROUTE_DISCARD_TIME) { /* * Time to garbage-collect the route entry. */ @@ -635,7 +740,8 @@ age_routes() discard_route(prev_r); r = prev_r; } - else if (r->rt_metric != UNREACHABLE) { + else if (r->rt_timer >= ROUTE_EXPIRE_TIME && + r->rt_metric != UNREACHABLE) { /* * Time to expire the route entry. If the gateway is zero, * i.e., it is a route to a directly-connected subnet, just @@ -652,6 +758,22 @@ age_routes() routes_changed = TRUE; } } + else if (virtual_time % (ROUTE_REPORT_INTERVAL * 2) == 0) { + /* + * Time out subordinateness that hasn't been reported in + * the last 2 intervals. + */ + if (!NBRM_SAME(r->rt_subordinates, r->rt_subordadv)) { + IF_DEBUG(DEBUG_ROUTE) + log(LOG_DEBUG, 0, "rt %s sub 0x%08x%08x subadv 0x%08x%08x metric %d", + RT_FMT(r, s1), + r->rt_subordinates.hi, r->rt_subordinates.lo, + r->rt_subordadv.hi, r->rt_subordadv.lo, r->rt_metric); + NBRM_MASK(r->rt_subordinates, r->rt_subordadv); + update_table_entry(r, r->rt_gateway); + } + NBRM_CLRALL(r->rt_subordadv); + } } } @@ -704,10 +826,41 @@ accept_probe(src, dst, p, datalen, level) u_int32 level; { vifi_t vifi; + static struct listaddr *unknowns = NULL; if ((vifi = find_vif(src, dst)) == NO_VIF) { - log(LOG_INFO, 0, - "ignoring probe from non-neighbor %s", inet_fmt(src, s1)); + struct listaddr *a, **prev; + struct listaddr *match = NULL; + time_t now = time(0); + + for (prev = &unknowns, a = *prev; a; a = *prev) { + if (a->al_addr == src) + match = a; + if (a->al_ctime + 2 * a->al_timer < now) { + /* We haven't heard from it in a long time */ + *prev = a->al_next; + free(a); + } else { + prev = &a->al_next; + } + } + if (match == NULL) { + match = *prev = (struct listaddr *)malloc(sizeof(struct listaddr)); + match->al_next = NULL; + match->al_addr = src; + match->al_timer = OLD_NEIGHBOR_EXPIRE_TIME; + match->al_ctime = now - match->al_timer; + } + + if (match->al_ctime + match->al_timer <= now) { + log(LOG_WARNING, 0, + "ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s", + inet_fmt(src, s1), s1); + match->al_timer *= 2; + } else + IF_DEBUG(DEBUG_PEER) + log(LOG_DEBUG, 0, + "ignoring probe from non-neighbor %s (%d seconds until next warning)", inet_fmt(src, s1), match->al_ctime + match->al_timer - now); return; } @@ -747,8 +900,137 @@ compare_rts(rt1, rt2) return (0); } +void +blaster_alloc(vifi) + vifi_t vifi; +{ + register struct uvif *v; + + v = &uvifs[vifi]; + if (v->uv_blasterbuf) + free(v->uv_blasterbuf); + + v->uv_blasterlen = 64*1024; + v->uv_blasterbuf = malloc(v->uv_blasterlen); + v->uv_blastercur = v->uv_blasterend = v->uv_blasterbuf; + if (v->uv_blastertimer) + timer_clearTimer(v->uv_blastertimer); + v->uv_blastertimer = 0; +} + +struct blaster_hdr { + u_int32 bh_src; + u_int32 bh_dst; + u_int32 bh_level; + int bh_datalen; +}; + +/* + * Queue a route report from a route-blaster. + * If the timer isn't running to process these reports, + * start it. + */ +static void +queue_blaster_report(vifi, src, dst, p, datalen, level) + vifi_t vifi; + u_int32 src, dst, level; + register char *p; + register int datalen; +{ + register struct blaster_hdr *bh; + register struct uvif *v; + int bblen = sizeof(*bh) + ((datalen + 3) & ~3); + + v = &uvifs[vifi]; + if (v->uv_blasterend - v->uv_blasterbuf + + bblen > v->uv_blasterlen) { + int end = v->uv_blasterend - v->uv_blasterbuf; + int cur = v->uv_blastercur - v->uv_blasterbuf; + + v->uv_blasterlen *= 2; + IF_DEBUG(DEBUG_IF) + log(LOG_DEBUG, 0, "increasing blasterbuf to %d bytes", + v->uv_blasterlen); + v->uv_blasterbuf = realloc(v->uv_blasterbuf, + v->uv_blasterlen); + if (v->uv_blasterbuf == NULL) { + log(LOG_WARNING, ENOMEM, "turning off blaster on vif %d", vifi); + v->uv_blasterlen = 0; + v->uv_blasterend = v->uv_blastercur = NULL; + v->uv_flags &= ~VIFF_BLASTER; + return; + } + v->uv_blasterend = v->uv_blasterbuf + end; + v->uv_blastercur = v->uv_blasterbuf + cur; + } + bh = (struct blaster_hdr *)v->uv_blasterend; + bh->bh_src = src; + bh->bh_dst = dst; + bh->bh_level = level; + bh->bh_datalen = datalen; + bcopy(p, (char *)(bh + 1), datalen); + v->uv_blasterend += bblen; + + if (v->uv_blastertimer == 0) { + int *i = (int *)malloc(sizeof(int *)); + + if (i == NULL) + log(LOG_ERR, 0, "out of memory"); + + *i = vifi; + + v->uv_blastertimer = timer_setTimer(5, + process_blaster_report, i); + } +} + +/* + * Periodic process; process up to 5 of the routes in the route-blaster + * queue. If there are more routes remaining, reschedule myself to run + * in 1 second. + */ +static void +process_blaster_report(vifip) + void *vifip; +{ + vifi_t vifi = *(int *)vifip; + register struct uvif *v; + register struct blaster_hdr *bh; + int i; + + IF_DEBUG(DEBUG_ROUTE) + log(LOG_DEBUG, 0, "processing vif %d blasted routes", vifi); + v = &uvifs[vifi]; + for (i = 0; i < 5; i++) { + if (v->uv_blastercur >= v->uv_blasterend) + break; + bh = (struct blaster_hdr *)v->uv_blastercur; + v->uv_blastercur += sizeof(*bh) + ((bh->bh_datalen + 3) & ~3); + accept_report(bh->bh_src, bh->bh_dst, (char *)(bh + 1), + -bh->bh_datalen, bh->bh_level); + } + + if (v->uv_blastercur >= v->uv_blasterend) { + v->uv_blastercur = v->uv_blasterbuf; + v->uv_blasterend = v->uv_blasterbuf; + v->uv_blastertimer = 0; + free(vifip); + IF_DEBUG(DEBUG_ROUTE) + log(LOG_DEBUG, 0, "finish processing vif %d blaster", vifi); + } else { + IF_DEBUG(DEBUG_ROUTE) + log(LOG_DEBUG, 0, "more blasted routes to come on vif %d", vifi); + v->uv_blastertimer = timer_setTimer(1, + process_blaster_report, vifip); + } +} + /* * Process an incoming route report message. + * If the report arrived on a vif marked as a "blaster", then just + * queue it and return; queue_blaster_report() will schedule it for + * processing later. If datalen is negative, then this is actually + * a queued report so actually process it instead of queueing it. */ void accept_report(src, dst, p, datalen, level) @@ -762,6 +1044,7 @@ accept_report(src, dst, p, datalen, level) u_int32 mask; u_int32 origin; struct newrt rt[4096]; + struct listaddr *nbr; if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, @@ -769,7 +1052,15 @@ accept_report(src, dst, p, datalen, level) return; } - if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)) + if (uvifs[vifi].uv_flags & VIFF_BLASTER) + if (datalen > 0) { + queue_blaster_report(vifi, src, dst, p, datalen, level); + return; + } else { + datalen = -datalen; + } + + if (!(nbr = update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level))) return; if (datalen > 2*4096) { @@ -826,6 +1117,7 @@ accept_report(src, dst, p, datalen, level) if (rt[nrt-1].origin == 0) rt[nrt-1].mask = 0; + IF_DEBUG(DEBUG_ROUTE) log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt, inet_fmt(src, s1), inet_fmt(dst, s2)); for (i = 0; i < nrt; ++i) { @@ -835,8 +1127,43 @@ accept_report(src, dst, p, datalen, level) inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2)); continue; } + /* Only filter non-poisoned updates. */ + if (uvifs[vifi].uv_filter && rt[i].metric < UNREACHABLE) { + struct vf_element *vfe; + int match = 0; + + for (vfe = uvifs[vifi].uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) { + if (vfe->vfe_flags & VFEF_EXACT) { + if ((vfe->vfe_addr == rt[i].origin) && + (vfe->vfe_mask == rt[i].mask)) { + match = 1; + break; + } + } else { + if ((rt[i].origin & vfe->vfe_mask) == vfe->vfe_addr) { + match = 1; + break; + } + } + } + if ((uvifs[vifi].uv_filter->vf_type == VFT_ACCEPT && match == 0) || + (uvifs[vifi].uv_filter->vf_type == VFT_DENY && match == 1)) { + IF_DEBUG(DEBUG_ROUTE) + log(LOG_DEBUG, 0, "%s skipped on vif %d because it %s %s", + inet_fmts(rt[i].origin, rt[i].mask, s1), + vifi, + match ? "matches" : "doesn't match", + match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) : + "the filter"); +#if 0 + rt[i].metric += vfe->vfe_addmetric; + if (rt[i].metric > UNREACHABLE) +#endif + rt[i].metric = UNREACHABLE; + } + } update_route(rt[i].origin, rt[i].mask, rt[i].metric, - src, vifi); + src, vifi, nbr); } if (routes_changed && !delay_change_reports) @@ -855,80 +1182,13 @@ report(which_routes, vifi, dst) u_int32 dst; { register struct rtentry *r; - register char *p; register int i; - int datalen = 0; - int width = 0; - u_int32 mask = 0; - u_int32 src; - u_int32 nflags; - - src = uvifs[vifi].uv_lcl_addr; - - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; -#ifdef NOTYET - /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */ - if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) { - *p++ = 0; /* 0xff000000 mask */ - *p++ = 0; - *p++ = 0; - *p++ = 0; /* class A net 0.0.0.0 == default */ - *p++ = 0x81; /*XXX metric 1, is this safe? */ - datalen += 5; - send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL), datalen); - return; - } -#endif - - nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS; - - for (r = rt_end; r != RT_ADDR; r = r->rt_prev) { - - if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) - continue; - - /* - * If there is no room for this route in the current message, - * send the message and start a new one. - */ - if (datalen + ((r->rt_originmask == mask) ? - (width + 1) : - (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { - *(p-1) |= 0x80; - send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL | nflags), datalen); - - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - mask = 0; - } - - if (r->rt_originmask != mask || datalen == 0) { - mask = r->rt_originmask; - width = r->rt_originwidth; - if (datalen != 0) *(p-1) |= 0x80; - *p++ = ((char *)&mask)[1]; - *p++ = ((char *)&mask)[2]; - *p++ = ((char *)&mask)[3]; - datalen += 3; - } - - for (i = 0; i < width; ++i) - *p++ = ((char *)&(r->rt_origin))[i]; - - *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? - (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ - (char)(r->rt_metric); - - datalen += width + 1; - } - - if (datalen != 0) { - *(p-1) |= 0x80; - send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL | nflags), datalen); + r = rt_end; + while (r != RT_ADDR) { + i = report_chunk(which_routes, r, vifi, dst); + while (i-- > 0) + r = r->rt_prev; } } @@ -955,10 +1215,8 @@ report_to_all_neighbors(which_routes) for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - if (v->uv_neighbors != NULL) { - report(which_routes, vifi, - (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr - : dvmrp_group); + if (!NBRM_ISEMPTY(v->uv_nbrmap)) { + report(which_routes, vifi, v->uv_dst_addr); } } @@ -987,7 +1245,8 @@ report_to_all_neighbors(which_routes) * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. */ static int -report_chunk(start_rt, vifi, dst) +report_chunk(which_routes, start_rt, vifi, dst) + int which_routes; register struct rtentry *start_rt; vifi_t vifi; u_int32 dst; @@ -996,27 +1255,63 @@ report_chunk(start_rt, vifi, dst) register char *p; register int i; register int nrt = 0; + struct uvif *v = &uvifs[vifi]; int datalen = 0; int width = 0; u_int32 mask = 0; u_int32 src; - u_int32 nflags; + int admetric = v->uv_admetric; + int metric; - src = uvifs[vifi].uv_lcl_addr; + src = v->uv_lcl_addr; p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS; - for (r = start_rt; r != RT_ADDR; r = r->rt_prev) { + if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) { + nrt++; + continue; + } -#ifdef NOTYET - /* Don't send poisoned routes back to parents if I am a leaf */ - if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi) - && (r->rt_metric > 1)) { - ++nrt; + /* + * Do not poison-reverse a route for a directly-connected + * subnetwork on that subnetwork. This can cause loops when + * some router on the subnetwork is misconfigured. + */ + if (r->rt_gateway == 0 && r->rt_parent == vifi) { + nrt++; continue; } -#endif + + if (v->uv_filter && v->uv_filter->vf_flags & VFF_BIDIR) { + struct vf_element *vfe; + int match = 0; + + for (vfe = v->uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) { + if (vfe->vfe_flags & VFEF_EXACT) { + if ((vfe->vfe_addr == r->rt_origin) && + (vfe->vfe_mask == r->rt_originmask)) { + match = 1; + break; + } + } else { + if ((r->rt_origin & vfe->vfe_mask) == vfe->vfe_addr) { + match = 1; + break; + } + } + } + if ((v->uv_filter->vf_type == VFT_ACCEPT && match == 0) || + (v->uv_filter->vf_type == VFT_DENY && match == 1)) { + IF_DEBUG(DEBUG_ROUTE) + log(LOG_DEBUG, 0, "%s not reported on vif %d because it %s %s", + RT_FMT(r, s1), vifi, + match ? "matches" : "doesn't match", + match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) : + "the filter"); + nrt++; + continue; + } + } /* * If there is no room for this route in the current message, @@ -1026,10 +1321,10 @@ report_chunk(start_rt, vifi, dst) (width + 1) : (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { *(p-1) |= 0x80; - send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL | nflags), datalen); + send_on_vif(v, 0, DVMRP_REPORT, datalen); return (nrt); } + if (r->rt_originmask != mask || datalen == 0) { mask = r->rt_originmask; width = r->rt_originwidth; @@ -1042,16 +1337,20 @@ report_chunk(start_rt, vifi, dst) for (i = 0; i < width; ++i) *p++ = ((char *)&(r->rt_origin))[i]; - *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? - (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ - (char)(r->rt_metric); + metric = r->rt_metric + admetric; + if (metric > UNREACHABLE) + metric = UNREACHABLE; + if (r->rt_parent != vifi && AVOID_TRANSIT(vifi, r)) + metric = UNREACHABLE; + *p++ = (r->rt_parent == vifi && metric != UNREACHABLE) ? + (char)(metric + UNREACHABLE) : /* "poisoned reverse" */ + (char)(metric); ++nrt; datalen += width + 1; } if (datalen != 0) { *(p-1) |= 0x80; - send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL | nflags), datalen); + send_on_vif(v, 0, DVMRP_REPORT, datalen); } return (nrt); } @@ -1086,14 +1385,8 @@ report_next_chunk() * all our neighbors. */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - if ((v->uv_neighbors != NULL) -#ifdef NOTYET - && !(v->uv_flags & VIFF_LEAF) -#endif - ) { - n = report_chunk(sr, vifi, - (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr - : dvmrp_group); + if (!NBRM_ISEMPTY(v->uv_nbrmap)) { + n = report_chunk(ALL_ROUTES, sr, vifi, v->uv_dst_addr); if (n < min) min = n; } @@ -1102,6 +1395,7 @@ report_next_chunk() min = 0; /* Neighborless router didn't send any routes */ n = min; + IF_DEBUG(DEBUG_ROUTE) log(LOG_INFO, 0, "update %d starting at %d of %d", n, (nroutes - start_rt), nroutes); @@ -1124,7 +1418,7 @@ dump_routes(fp) fprintf(fp, "Multicast Routing Table (%u %s)\n%s\n", nroutes, (nroutes == 1) ? "entry" : "entries", - " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs"); + " Origin-Subnet From-Gateway Metric Tmr Fl In-Vif Out-Vifs"); for (r = routing_table; r != NULL; r = r->rt_next) { @@ -1135,12 +1429,31 @@ dump_routes(fp) fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ", r->rt_metric); - fprintf(fp, " %3u %3u ", r->rt_timer, r->rt_parent); + fprintf(fp, " %3u %c%c %3u ", r->rt_timer, + (r->rt_flags & RTF_CHANGED) ? 'C' : '.', + (r->rt_flags & RTF_HOLDDOWN) ? 'H' : '.', + r->rt_parent); for (i = 0; i < numvifs; ++i) { + struct listaddr *n; + char l = '['; + if (VIFM_ISSET(i, r->rt_children)) { - fprintf(fp, " %u%c", - i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' '); + if ((uvifs[i].uv_flags & VIFF_TUNNEL) && + !NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates)) + /* Don't print out parenthood of a leaf tunnel. */ + continue; + fprintf(fp, " %u", i); + if (!NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates)) + fprintf(fp, "*"); + for (n = uvifs[i].uv_neighbors; n; n = n->al_next) { + if (NBRM_ISSET(n->al_index, r->rt_subordinates)) { + fprintf(fp, "%c%d", l, n->al_index); + l = ','; + } + } + if (l == ',') + fprintf(fp, "]"); } } fprintf(fp, "\n"); @@ -1155,7 +1468,8 @@ determine_route(src) struct rtentry *rt; for (rt = routing_table; rt != NULL; rt = rt->rt_next) { - if (rt->rt_origin == (src & rt->rt_originmask)) + if (rt->rt_origin == (src & rt->rt_originmask) && + rt->rt_metric != UNREACHABLE) break; } return rt; |