File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mrouted / route.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:10:48 2012 UTC (12 years, 7 months ago) by misho
Branches: mrouted, MAIN
CVS tags: v3_9_5, HEAD
mrouted

    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>