usched: Allow process to change self cpu affinity
[dragonfly.git] / usr.sbin / mrouted / route.c
blob4bd1ecfa8f57eeec93f3c196fb3c3b69e8442052
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.
6 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7 * Leland Stanford Junior University.
10 * route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp
12 * $FreeBSD: src/usr.sbin/mrouted/route.c,v 1.12 1999/08/28 01:17:08 peter Exp $
15 #include "defs.h"
18 * This define statement saves a lot of space later
20 #define RT_ADDR (struct rtentry *)&routing_table
23 * Exported variables.
25 int routes_changed; /* 1=>some routes have changed */
26 int delay_change_reports; /* 1=>postpone change reports */
30 * The routing table is shared with prune.c , so must not be static.
32 struct rtentry *routing_table; /* pointer to list of route entries */
35 * Private variables.
37 static struct rtentry *rtp; /* pointer to a route entry */
38 static struct rtentry *rt_end; /* pointer to last route entry */
39 unsigned int nroutes; /* current number of route entries */
42 * Private functions.
44 static int init_children_and_leaves(struct rtentry *r,
45 vifi_t parent, int first);
46 static int find_route (u_int32 origin, u_int32 mask);
47 static void create_route(u_int32 origin, u_int32 mask);
48 static void discard_route(struct rtentry *prev_r);
49 static int compare_rts (const void *rt1, const void *rt2);
50 static int report_chunk (int, struct rtentry *start_rt, vifi_t vifi,
51 u_int32 dst);
52 static void queue_blaster_report(vifi_t, u_int32, u_int32, char *,
53 int, u_int32);
54 static void process_blaster_report(void *);
56 #ifdef SNMP
57 #include <sys/types.h>
58 #include "snmp.h"
61 * Return pointer to a specific route entry. This must be a separate
62 * function from find_route() which modifies rtp.
64 struct rtentry *
65 snmp_find_route(u_int32 src, u_int32 mask)
67 struct rtentry *rt;
69 for (rt = routing_table; rt; rt = rt->rt_next) {
70 if (src == rt->rt_origin && mask == rt->rt_originmask)
71 return rt;
73 return NULL;
77 * Find next route entry > specification
79 int
80 next_route(struct rtentry **rtpp, u_int32 src, u_int32 mask)
82 struct rtentry *rt, *rbest = NULL;
84 /* Among all entries > spec, find "lowest" one in order */
85 for (rt = routing_table; rt; rt=rt->rt_next) {
86 if ((ntohl(rt->rt_origin) > ntohl(src)
87 || (ntohl(rt->rt_origin) == ntohl(src)
88 && ntohl(rt->rt_originmask) > ntohl(mask)))
89 && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
90 || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
91 && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
92 rbest = rt;
94 (*rtpp) = rbest;
95 return (*rtpp)!=NULL;
99 * Given a routing table entry, and a vifi, find the next vifi/entry
102 next_route_child(struct rtentry **rtpp, u_int32 src, u_int32 mask, vifi_t *vifi)
104 /* Get (S,M) entry */
105 if (!((*rtpp) = snmp_find_route(src,mask)))
106 if (!next_route(rtpp, src, mask))
107 return 0;
109 /* Continue until we get one with a valid next vif */
110 do {
111 for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
112 if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
113 return 1;
114 *vifi = 0;
115 } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) );
117 return 0;
119 #endif
122 * Initialize the routing table and associated variables.
124 void
125 init_routes(void)
127 routing_table = NULL;
128 rt_end = RT_ADDR;
129 nroutes = 0;
130 routes_changed = FALSE;
131 delay_change_reports = FALSE;
136 * Initialize the children bits for route 'r', along with the
137 * associated dominant and subordinate data structures.
138 * If first is set, initialize dominants, otherwise keep old
139 * dominants on non-parent interfaces.
140 * XXX Does this need a return value?
142 static int
143 init_children_and_leaves(struct rtentry *r, vifi_t parent, int first)
145 vifi_t vifi;
146 struct uvif *v;
147 vifbitmap_t old_children;
148 nbrbitmap_t old_subords;
150 VIFM_COPY(r->rt_children, old_children);
151 NBRM_COPY(r->rt_subordinates, old_subords);
153 VIFM_CLRALL(r->rt_children);
155 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
156 if (first || vifi == parent)
157 r->rt_dominants [vifi] = 0;
158 if (vifi == parent || uvifs[vifi].uv_flags & VIFF_NOFLOOD ||
159 AVOID_TRANSIT(vifi, r) || (!first && r->rt_dominants[vifi]))
160 NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
161 else
162 NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
164 if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED)) &&
165 !(!first && r->rt_dominants[vifi])) {
166 VIFM_SET(vifi, r->rt_children);
170 return (!VIFM_SAME(r->rt_children, old_children) ||
171 !NBRM_SAME(r->rt_subordinates, old_subords));
176 * A new vif has come up -- update the children bitmaps in all route
177 * entries to take that into account.
179 void
180 add_vif_to_routes(vifi_t vifi)
182 struct rtentry *r;
183 struct uvif *v;
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);
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.
204 void
205 delete_vif_from_routes(vifi_t vifi)
207 struct rtentry *r;
209 for (r = routing_table; r != NULL; r = r->rt_next) {
210 if (r->rt_metric != UNREACHABLE) {
211 if (vifi == r->rt_parent) {
212 del_table_entry(r, 0, DEL_ALL_ROUTES);
213 r->rt_timer = ROUTE_EXPIRE_TIME;
214 r->rt_metric = UNREACHABLE;
215 r->rt_flags |= RTF_CHANGED;
216 routes_changed = TRUE;
218 else if (VIFM_ISSET(vifi, r->rt_children)) {
219 VIFM_CLR(vifi, r->rt_children);
220 NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
221 update_table_entry(r, r->rt_gateway);
223 else {
224 r->rt_dominants[vifi] = 0;
232 * A new neighbor has come up. If we're flooding on the neighbor's
233 * vif, mark that neighbor as subordinate for all routes whose parent
234 * is not this vif.
236 void
237 add_neighbor_to_routes(vifi_t vifi, int index)
239 struct rtentry *r;
240 struct uvif *v;
242 v = &uvifs[vifi];
243 if (v->uv_flags & VIFF_NOFLOOD)
244 return;
245 for (r = routing_table; r != NULL; r = r->rt_next) {
246 if (r->rt_metric != UNREACHABLE && r->rt_parent != vifi &&
247 !AVOID_TRANSIT(vifi, r)) {
248 NBRM_SET(index, r->rt_subordinates);
249 update_table_entry(r, r->rt_gateway);
256 * A neighbor has failed or become unreachable. If that neighbor was
257 * considered a dominant or subordinate router in any route entries,
258 * take appropriate action. Expire all routes this neighbor advertised
259 * to us.
261 void
262 delete_neighbor_from_routes(u_int32 addr, vifi_t vifi, int index)
264 struct rtentry *r;
265 struct uvif *v;
267 v = &uvifs[vifi];
268 for (r = routing_table; r != NULL; r = r->rt_next) {
269 if (r->rt_metric != UNREACHABLE) {
270 if (r->rt_parent == vifi && r->rt_gateway == addr) {
271 del_table_entry(r, 0, DEL_ALL_ROUTES);
272 r->rt_timer = ROUTE_EXPIRE_TIME;
273 r->rt_metric = UNREACHABLE;
274 r->rt_flags |= RTF_CHANGED;
275 routes_changed = TRUE;
276 } else if (r->rt_dominants[vifi] == addr) {
277 VIFM_SET(vifi, r->rt_children);
278 r->rt_dominants[vifi] = 0;
279 if ((uvifs[vifi].uv_flags & VIFF_NOFLOOD) ||
280 AVOID_TRANSIT(vifi, r))
281 NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
282 else
283 NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
284 update_table_entry(r, r->rt_gateway);
285 } else if (NBRM_ISSET(index, r->rt_subordinates)) {
286 NBRM_CLR(index, r->rt_subordinates);
287 update_table_entry(r, r->rt_gateway);
295 * Prepare for a sequence of ordered route updates by initializing a pointer
296 * to the start of the routing table. The pointer is used to remember our
297 * position in the routing table in order to avoid searching from the
298 * beginning for each update; this relies on having the route reports in
299 * a single message be in the same order as the route entries in the routing
300 * table.
302 void
303 start_route_updates(void)
305 rtp = RT_ADDR;
310 * Starting at the route entry following the one to which 'rtp' points,
311 * look for a route entry matching the specified origin and mask. If a
312 * match is found, return TRUE and leave 'rtp' pointing at the found entry.
313 * If no match is found, return FALSE and leave 'rtp' pointing to the route
314 * entry preceding the point at which the new origin should be inserted.
315 * This code is optimized for the normal case in which the first entry to
316 * be examined is the matching entry.
318 static int
319 find_route(u_int32 origin, u_int32 mask)
321 struct rtentry *r;
323 r = rtp->rt_next;
324 while (r != NULL) {
325 if (origin == r->rt_origin && mask == r->rt_originmask) {
326 rtp = r;
327 return (TRUE);
329 if (ntohl(mask) < ntohl(r->rt_originmask) ||
330 (mask == r->rt_originmask &&
331 ntohl(origin) < ntohl(r->rt_origin))) {
332 rtp = r;
333 r = r->rt_next;
335 else break;
337 return (FALSE);
341 * Create a new routing table entry for the specified origin and link it into
342 * the routing table. The shared variable 'rtp' is assumed to point to the
343 * routing entry after which the new one should be inserted. It is left
344 * pointing to the new entry.
346 * Only the origin, originmask, originwidth and flags fields are initialized
347 * in the new route entry; the caller is responsible for filling in the the
348 * rest.
350 static void
351 create_route(u_int32 origin, u_int32 mask)
353 struct rtentry *r;
355 if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) +
356 (numvifs * sizeof(u_int32)))) == NULL) {
357 dolog(LOG_ERR, 0, "ran out of memory"); /* fatal */
359 r->rt_origin = origin;
360 r->rt_originmask = mask;
361 if (((char *)&mask)[3] != 0) r->rt_originwidth = 4;
362 else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3;
363 else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2;
364 else r->rt_originwidth = 1;
365 r->rt_flags = 0;
366 r->rt_dominants = (u_int32 *)(r + 1);
367 bzero(r->rt_dominants, numvifs * sizeof(u_int32));
368 r->rt_groups = NULL;
369 VIFM_CLRALL(r->rt_children);
370 NBRM_CLRALL(r->rt_subordinates);
371 NBRM_CLRALL(r->rt_subordadv);
373 r->rt_next = rtp->rt_next;
374 rtp->rt_next = r;
375 r->rt_prev = rtp;
376 if (r->rt_next != NULL)
377 (r->rt_next)->rt_prev = r;
378 else
379 rt_end = r;
380 rtp = r;
381 ++nroutes;
386 * Discard the routing table entry following the one to which 'prev_r' points.
388 static void
389 discard_route(struct rtentry *prev_r)
391 struct rtentry *r;
393 r = prev_r->rt_next;
394 uvifs[r->rt_parent].uv_nroutes--;
395 /*???nbr???.al_nroutes--;*/
396 prev_r->rt_next = r->rt_next;
397 if (prev_r->rt_next != NULL)
398 (prev_r->rt_next)->rt_prev = prev_r;
399 else
400 rt_end = prev_r;
401 free((char *)r);
402 --nroutes;
407 * Process a route report for a single origin, creating or updating the
408 * corresponding routing table entry if necessary. 'src' is either the
409 * address of a neighboring router from which the report arrived, or zero
410 * to indicate a change of status of one of our own interfaces.
412 void
413 update_route(u_int32 origin, u_int32 mask, u_int metric, u_int32 src,
414 vifi_t vifi, struct listaddr *n)
416 struct rtentry *r;
417 u_int adj_metric;
420 * Compute an adjusted metric, taking into account the cost of the
421 * subnet or tunnel over which the report arrived, and normalizing
422 * all unreachable/poisoned metrics into a single value.
424 if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
425 dolog(LOG_WARNING, 0,
426 "%s reports out-of-range metric %u for origin %s",
427 inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2));
428 return;
430 adj_metric = metric + uvifs[vifi].uv_metric;
431 if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
434 * Look up the reported origin in the routing table.
436 if (!find_route(origin, mask)) {
438 * Not found.
439 * Don't create a new entry if the report says it's unreachable,
440 * or if the reported origin and mask are invalid.
442 if (adj_metric == UNREACHABLE) {
443 return;
445 if (src != 0 && !inet_valid_subnet(origin, mask)) {
446 dolog(LOG_WARNING, 0,
447 "%s reports an invalid origin (%s) and/or mask (%08x)",
448 inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
449 return;
452 IF_DEBUG(DEBUG_RTDETAIL)
453 dolog(LOG_DEBUG, 0, "%s advertises new route %s",
454 inet_fmt(src, s1), inet_fmts(origin, mask, s2));
457 * OK, create the new routing entry. 'rtp' will be left pointing
458 * to the new entry.
460 create_route(origin, mask);
461 uvifs[vifi].uv_nroutes++;
462 /*n->al_nroutes++;*/
464 rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
468 * We now have a routing entry for the reported origin. Update it?
470 r = rtp;
471 if (r->rt_metric == UNREACHABLE) {
473 * The routing entry is for a formerly-unreachable or new origin.
474 * If the report claims reachability, update the entry to use
475 * the reported route.
477 if (adj_metric == UNREACHABLE)
478 return;
480 IF_DEBUG(DEBUG_RTDETAIL)
481 dolog(LOG_DEBUG, 0, "%s advertises %s with adj_metric %d (ours was %d)",
482 inet_fmt(src, s1), inet_fmts(origin, mask, s2),
483 adj_metric, r->rt_metric);
486 * Now "steal away" any sources that belong under this route
487 * by deleting any cache entries they might have created
488 * and allowing the kernel to re-request them.
490 * If we haven't performed final initialization yet and are
491 * just collecting the routing table, we can't have any
492 * sources so we don't perform this step.
494 if (did_final_init)
495 steal_sources(rtp);
497 r->rt_parent = vifi;
498 r->rt_gateway = src;
499 init_children_and_leaves(r, vifi, 1);
501 r->rt_timer = 0;
502 r->rt_metric = adj_metric;
503 r->rt_flags |= RTF_CHANGED;
504 routes_changed = TRUE;
505 update_table_entry(r, r->rt_gateway);
507 else if (src == r->rt_gateway) {
509 * The report has come either from the interface directly-connected
510 * to the origin subnet (src and r->rt_gateway both equal zero) or
511 * from the gateway we have chosen as the best first-hop gateway back
512 * towards the origin (src and r->rt_gateway not equal zero). Reset
513 * the route timer and, if the reported metric has changed, update
514 * our entry accordingly.
516 r->rt_timer = 0;
518 IF_DEBUG(DEBUG_RTDETAIL)
519 dolog(LOG_DEBUG, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)",
520 inet_fmt(src, s1), inet_fmts(origin, mask, s2),
521 adj_metric, r->rt_metric);
523 if (adj_metric == r->rt_metric)
524 return;
526 if (adj_metric == UNREACHABLE) {
527 del_table_entry(r, 0, DEL_ALL_ROUTES);
528 r->rt_timer = ROUTE_EXPIRE_TIME;
530 r->rt_metric = adj_metric;
531 r->rt_flags |= RTF_CHANGED;
532 routes_changed = TRUE;
534 else if (src == 0 ||
535 (r->rt_gateway != 0 &&
536 (adj_metric < r->rt_metric ||
537 (adj_metric == r->rt_metric &&
538 (ntohl(src) < ntohl(r->rt_gateway) ||
539 r->rt_timer >= ROUTE_SWITCH_TIME))))) {
541 * The report is for an origin we consider reachable; the report
542 * comes either from one of our own interfaces or from a gateway
543 * other than the one we have chosen as the best first-hop gateway
544 * back towards the origin. If the source of the update is one of
545 * our own interfaces, or if the origin is not a directly-connected
546 * subnet and the reported metric for that origin is better than
547 * what our routing entry says, update the entry to use the new
548 * gateway and metric. We also switch gateways if the reported
549 * metric is the same as the one in the route entry and the gateway
550 * associated with the route entry has not been heard from recently,
551 * or if the metric is the same but the reporting gateway has a lower
552 * IP address than the gateway associated with the route entry.
553 * Did you get all that?
555 u_int32 old_gateway;
556 vifi_t old_parent;
557 old_gateway = r->rt_gateway;
558 old_parent = r->rt_parent;
559 r->rt_gateway = src;
560 r->rt_parent = vifi;
562 IF_DEBUG(DEBUG_RTDETAIL)
563 dolog(LOG_DEBUG, 0, "%s (new parent) on vif %d advertises %s with adj_metric %d (old parent was %s on vif %d, metric %d)",
564 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
565 adj_metric, inet_fmt(old_gateway, s3), old_parent,
566 r->rt_metric);
568 if (old_parent != vifi) {
569 init_children_and_leaves(r, vifi, 0);
570 uvifs[old_parent].uv_nroutes--;
571 uvifs[vifi].uv_nroutes++;
573 if (old_gateway != src) {
574 update_table_entry(r, old_gateway);
575 /*???old_gateway???->al_nroutes--;*/
576 /*n->al_nroutes++;*/
578 r->rt_timer = 0;
579 r->rt_metric = adj_metric;
580 r->rt_flags |= RTF_CHANGED;
581 routes_changed = TRUE;
583 else if (vifi != r->rt_parent) {
585 * The report came from a vif other than the route's parent vif.
586 * Update the children info, if necessary.
588 if (AVOID_TRANSIT(vifi, r)) {
590 * The route's parent is a vif from which we're not supposed
591 * to transit onto this vif. Simply ignore the update.
593 IF_DEBUG(DEBUG_RTDETAIL)
594 dolog(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)",
595 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
596 metric);
597 } else if (VIFM_ISSET(vifi, r->rt_children)) {
599 * Vif is a child vif for this route.
601 if (metric < r->rt_metric ||
602 (metric == r->rt_metric &&
603 ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
605 * Neighbor has lower metric to origin (or has same metric
606 * and lower IP address) -- it becomes the dominant router,
607 * and vif is no longer a child for me.
609 VIFM_CLR(vifi, r->rt_children);
610 r->rt_dominants [vifi] = src;
611 /* XXX
612 * We don't necessarily want to forget about subordinateness
613 * so that we can become the dominant quickly if the current
614 * dominant fails.
616 NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
617 update_table_entry(r, r->rt_gateway);
618 IF_DEBUG(DEBUG_RTDETAIL)
619 dolog(LOG_DEBUG, 0, "%s on vif %d becomes dominant for %s with metric %d",
620 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
621 metric);
623 else if (metric > UNREACHABLE) { /* "poisoned reverse" */
625 * Neighbor considers this vif to be on path to route's
626 * origin; record this neighbor as subordinate
628 if (!NBRM_ISSET(n->al_index, r->rt_subordinates)) {
629 IF_DEBUG(DEBUG_RTDETAIL)
630 dolog(LOG_DEBUG, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d",
631 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
632 metric - UNREACHABLE);
633 NBRM_SET(n->al_index, r->rt_subordinates);
634 update_table_entry(r, r->rt_gateway);
635 } else {
636 IF_DEBUG(DEBUG_RTDETAIL)
637 dolog(LOG_DEBUG, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d",
638 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
639 metric - UNREACHABLE);
641 NBRM_SET(n->al_index, r->rt_subordadv);
643 else if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
645 * Current subordinate no longer considers this vif to be on
646 * path to route's origin; it is no longer a subordinate
647 * router.
649 IF_DEBUG(DEBUG_RTDETAIL)
650 dolog(LOG_DEBUG, 0, "%s on vif %d is no longer a subordinate for %s with metric %d",
651 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
652 metric);
653 NBRM_CLR(n->al_index, r->rt_subordinates);
654 update_table_entry(r, r->rt_gateway);
658 else if (src == r->rt_dominants[vifi] &&
659 (metric > r->rt_metric ||
660 (metric == r->rt_metric &&
661 ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
663 * Current dominant no longer has a lower metric to origin
664 * (or same metric and lower IP address); we adopt the vif
665 * as our own child.
667 IF_DEBUG(DEBUG_RTDETAIL)
668 dolog(LOG_DEBUG, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d",
669 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
670 metric);
671 VIFM_SET(vifi, r->rt_children);
672 r->rt_dominants[vifi] = 0;
673 if (uvifs[vifi].uv_flags & VIFF_NOFLOOD)
674 NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
675 else
676 NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
677 if (metric > UNREACHABLE) {
678 NBRM_SET(n->al_index, r->rt_subordinates);
679 NBRM_SET(n->al_index, r->rt_subordadv);
681 update_table_entry(r, r->rt_gateway);
682 } else {
683 IF_DEBUG(DEBUG_RTDETAIL)
684 dolog(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored)",
685 inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
686 metric);
693 * On every timer interrupt, advance the timer in each routing entry.
695 void
696 age_routes(void)
698 struct rtentry *r;
699 struct rtentry *prev_r;
700 extern u_long virtual_time; /* from main.c */
702 for (prev_r = RT_ADDR, r = routing_table;
703 r != NULL;
704 prev_r = r, r = r->rt_next) {
706 if ((r->rt_timer += TIMER_INTERVAL) >= ROUTE_DISCARD_TIME) {
708 * Time to garbage-collect the route entry.
710 del_table_entry(r, 0, DEL_ALL_ROUTES);
711 discard_route(prev_r);
712 r = prev_r;
714 else if (r->rt_timer >= ROUTE_EXPIRE_TIME &&
715 r->rt_metric != UNREACHABLE) {
717 * Time to expire the route entry. If the gateway is zero,
718 * i.e., it is a route to a directly-connected subnet, just
719 * set the timer back to zero; such routes expire only when
720 * the interface to the subnet goes down.
722 if (r->rt_gateway == 0) {
723 r->rt_timer = 0;
725 else {
726 del_table_entry(r, 0, DEL_ALL_ROUTES);
727 r->rt_metric = UNREACHABLE;
728 r->rt_flags |= RTF_CHANGED;
729 routes_changed = TRUE;
732 else if (virtual_time % (ROUTE_REPORT_INTERVAL * 2) == 0) {
734 * Time out subordinateness that hasn't been reported in
735 * the last 2 intervals.
737 if (!NBRM_SAME(r->rt_subordinates, r->rt_subordadv)) {
738 IF_DEBUG(DEBUG_ROUTE)
739 dolog(LOG_DEBUG, 0, "rt %s sub 0x%08lx%08lx subadv 0x%08lx%08lx metric %d",
740 RT_FMT(r, s1),
741 r->rt_subordinates.hi, r->rt_subordinates.lo,
742 r->rt_subordadv.hi, r->rt_subordadv.lo, r->rt_metric);
743 NBRM_MASK(r->rt_subordinates, r->rt_subordadv);
744 update_table_entry(r, r->rt_gateway);
746 NBRM_CLRALL(r->rt_subordadv);
753 * Mark all routes as unreachable. This function is called only from
754 * hup() in preparation for informing all neighbors that we are going
755 * off the air. For consistency, we ought also to delete all reachable
756 * route entries from the kernel, but since we are about to exit we rely
757 * on the kernel to do its own cleanup -- no point in making all those
758 * expensive kernel calls now.
760 void
761 expire_all_routes(void)
763 struct rtentry *r;
765 for (r = routing_table; r != NULL; r = r->rt_next) {
766 r->rt_metric = UNREACHABLE;
767 r->rt_flags |= RTF_CHANGED;
768 routes_changed = TRUE;
774 * Delete all the routes in the routing table.
776 void
777 free_all_routes(void)
779 struct rtentry *r;
781 r = RT_ADDR;
783 while (r->rt_next)
784 discard_route(r);
789 * Process an incoming neighbor probe message.
791 void
792 accept_probe(u_int32 src, u_int32 dst, char *p, int datalen, u_int32 level)
794 vifi_t vifi;
795 static struct listaddr *unknowns = NULL;
797 if ((vifi = find_vif(src, dst)) == NO_VIF) {
798 struct listaddr *a, **prev;
799 struct listaddr *match = NULL;
800 time_t now = time(0);
802 for (prev = &unknowns, a = *prev; a; a = *prev) {
803 if (a->al_addr == src)
804 match = a;
805 if (a->al_ctime + 2 * a->al_timer < now) {
806 /* We haven't heard from it in a long time */
807 *prev = a->al_next;
808 free(a);
809 } else {
810 prev = &a->al_next;
813 if (match == NULL) {
814 match = *prev = (struct listaddr *)malloc(sizeof(struct listaddr));
815 match->al_next = NULL;
816 match->al_addr = src;
817 match->al_timer = OLD_NEIGHBOR_EXPIRE_TIME;
818 match->al_ctime = now - match->al_timer;
821 if (match->al_ctime + match->al_timer <= now) {
822 dolog(LOG_WARNING, 0,
823 "ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s",
824 inet_fmt(src, s1), s1);
825 match->al_timer *= 2;
826 } else
827 IF_DEBUG(DEBUG_PEER)
828 dolog(LOG_DEBUG, 0,
829 "ignoring probe from non-neighbor %s (%lu seconds until next warning)", inet_fmt(src, s1), match->al_ctime + match->al_timer - now);
830 return;
833 update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level);
836 struct newrt {
837 u_int32 mask;
838 u_int32 origin;
839 int metric;
840 int pad;
843 static int
844 compare_rts(const void *rt1, const void *rt2)
846 struct newrt *r1 = (struct newrt *)rt1;
847 struct newrt *r2 = (struct newrt *)rt2;
848 u_int32 m1 = ntohl(r1->mask);
849 u_int32 m2 = ntohl(r2->mask);
850 u_int32 o1, o2;
852 if (m1 > m2)
853 return (-1);
854 if (m1 < m2)
855 return (1);
857 /* masks are equal */
858 o1 = ntohl(r1->origin);
859 o2 = ntohl(r2->origin);
860 if (o1 > o2)
861 return (-1);
862 if (o1 < o2)
863 return (1);
864 return (0);
867 void
868 blaster_alloc(vifi_t vifi)
870 struct uvif *v;
872 v = &uvifs[vifi];
873 if (v->uv_blasterbuf)
874 free(v->uv_blasterbuf);
876 v->uv_blasterlen = 64*1024;
877 v->uv_blasterbuf = malloc(v->uv_blasterlen);
878 v->uv_blastercur = v->uv_blasterend = v->uv_blasterbuf;
879 if (v->uv_blastertimer)
880 timer_clearTimer(v->uv_blastertimer);
881 v->uv_blastertimer = 0;
884 struct blaster_hdr {
885 u_int32 bh_src;
886 u_int32 bh_dst;
887 u_int32 bh_level;
888 int bh_datalen;
892 * Queue a route report from a route-blaster.
893 * If the timer isn't running to process these reports,
894 * start it.
896 static void
897 queue_blaster_report(vifi_t vifi, u_int32 src, u_int32 dst, char *p,
898 int datalen, u_int32 level)
900 struct blaster_hdr *bh;
901 struct uvif *v;
902 int bblen = sizeof(*bh) + ((datalen + 3) & ~3);
904 v = &uvifs[vifi];
905 if (v->uv_blasterend - v->uv_blasterbuf +
906 bblen > v->uv_blasterlen) {
907 int end = v->uv_blasterend - v->uv_blasterbuf;
908 int cur = v->uv_blastercur - v->uv_blasterbuf;
910 v->uv_blasterlen *= 2;
911 IF_DEBUG(DEBUG_IF)
912 dolog(LOG_DEBUG, 0, "increasing blasterbuf to %d bytes",
913 v->uv_blasterlen);
914 v->uv_blasterbuf = realloc(v->uv_blasterbuf,
915 v->uv_blasterlen);
916 if (v->uv_blasterbuf == NULL) {
917 dolog(LOG_WARNING, ENOMEM, "turning off blaster on vif %d", vifi);
918 v->uv_blasterlen = 0;
919 v->uv_blasterend = v->uv_blastercur = NULL;
920 v->uv_flags &= ~VIFF_BLASTER;
921 return;
923 v->uv_blasterend = v->uv_blasterbuf + end;
924 v->uv_blastercur = v->uv_blasterbuf + cur;
926 bh = (struct blaster_hdr *)v->uv_blasterend;
927 bh->bh_src = src;
928 bh->bh_dst = dst;
929 bh->bh_level = level;
930 bh->bh_datalen = datalen;
931 bcopy(p, (char *)(bh + 1), datalen);
932 v->uv_blasterend += bblen;
934 if (v->uv_blastertimer == 0) {
935 int *i = (int *)malloc(sizeof(int *));
937 if (i == NULL)
938 dolog(LOG_ERR, 0, "out of memory");
940 *i = vifi;
942 v->uv_blastertimer = timer_setTimer(5,
943 process_blaster_report, i);
948 * Periodic process; process up to 5 of the routes in the route-blaster
949 * queue. If there are more routes remaining, reschedule myself to run
950 * in 1 second.
952 static void
953 process_blaster_report(void *vifip)
955 vifi_t vifi = *(int *)vifip;
956 struct uvif *v;
957 struct blaster_hdr *bh;
958 int i;
960 IF_DEBUG(DEBUG_ROUTE)
961 dolog(LOG_DEBUG, 0, "processing vif %d blasted routes", vifi);
962 v = &uvifs[vifi];
963 for (i = 0; i < 5; i++) {
964 if (v->uv_blastercur >= v->uv_blasterend)
965 break;
966 bh = (struct blaster_hdr *)v->uv_blastercur;
967 v->uv_blastercur += sizeof(*bh) + ((bh->bh_datalen + 3) & ~3);
968 accept_report(bh->bh_src, bh->bh_dst, (char *)(bh + 1),
969 -bh->bh_datalen, bh->bh_level);
972 if (v->uv_blastercur >= v->uv_blasterend) {
973 v->uv_blastercur = v->uv_blasterbuf;
974 v->uv_blasterend = v->uv_blasterbuf;
975 v->uv_blastertimer = 0;
976 free(vifip);
977 IF_DEBUG(DEBUG_ROUTE)
978 dolog(LOG_DEBUG, 0, "finish processing vif %d blaster", vifi);
979 } else {
980 IF_DEBUG(DEBUG_ROUTE)
981 dolog(LOG_DEBUG, 0, "more blasted routes to come on vif %d", vifi);
982 v->uv_blastertimer = timer_setTimer(1,
983 process_blaster_report, vifip);
988 * Process an incoming route report message.
989 * If the report arrived on a vif marked as a "blaster", then just
990 * queue it and return; queue_blaster_report() will schedule it for
991 * processing later. If datalen is negative, then this is actually
992 * a queued report so actually process it instead of queueing it.
994 void
995 accept_report(u_int32 src, u_int32 dst, char *p, int datalen, u_int32 level)
997 vifi_t vifi;
998 int width, i, nrt = 0;
999 int metric;
1000 u_int32 mask;
1001 u_int32 origin;
1002 struct newrt rt[4096];
1003 struct listaddr *nbr;
1005 if ((vifi = find_vif(src, dst)) == NO_VIF) {
1006 dolog(LOG_INFO, 0,
1007 "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
1008 return;
1011 if (uvifs[vifi].uv_flags & VIFF_BLASTER) {
1012 if (datalen > 0) {
1013 queue_blaster_report(vifi, src, dst, p, datalen, level);
1014 return;
1015 } else {
1016 datalen = -datalen;
1020 if (!(nbr = update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)))
1021 return;
1023 if (datalen > 2*4096) {
1024 dolog(LOG_INFO, 0,
1025 "ignoring oversize (%d bytes) route report from %s",
1026 datalen, inet_fmt(src, s1));
1027 return;
1030 while (datalen > 0) { /* Loop through per-mask lists. */
1032 if (datalen < 3) {
1033 dolog(LOG_WARNING, 0,
1034 "received truncated route report from %s",
1035 inet_fmt(src, s1));
1036 return;
1038 ((u_char *)&mask)[0] = 0xff; width = 1;
1039 if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
1040 if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
1041 if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
1042 if (!inet_valid_mask(ntohl(mask))) {
1043 dolog(LOG_WARNING, 0,
1044 "%s reports bogus netmask 0x%08x (%s)",
1045 inet_fmt(src, s1), ntohl(mask), inet_fmt(mask, s2));
1046 return;
1048 datalen -= 3;
1050 do { /* Loop through (origin, metric) pairs */
1051 if (datalen < width + 1) {
1052 dolog(LOG_WARNING, 0,
1053 "received truncated route report from %s",
1054 inet_fmt(src, s1));
1055 return;
1057 origin = 0;
1058 for (i = 0; i < width; ++i)
1059 ((char *)&origin)[i] = *p++;
1060 metric = *p++;
1061 datalen -= width + 1;
1062 rt[nrt].mask = mask;
1063 rt[nrt].origin = origin;
1064 rt[nrt].metric = (metric & 0x7f);
1065 ++nrt;
1066 } while (!(metric & 0x80));
1069 qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
1070 start_route_updates();
1072 * If the last entry is default, change mask from 0xff000000 to 0
1074 if (rt[nrt-1].origin == 0)
1075 rt[nrt-1].mask = 0;
1077 IF_DEBUG(DEBUG_ROUTE)
1078 dolog(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
1079 inet_fmt(src, s1), inet_fmt(dst, s2));
1080 for (i = 0; i < nrt; ++i) {
1081 if (i != 0 && rt[i].origin == rt[i-1].origin &&
1082 rt[i].mask == rt[i-1].mask) {
1083 dolog(LOG_WARNING, 0, "%s reports duplicate route for %s",
1084 inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2));
1085 continue;
1087 /* Only filter non-poisoned updates. */
1088 if (uvifs[vifi].uv_filter && rt[i].metric < UNREACHABLE) {
1089 struct vf_element *vfe;
1090 int match = 0;
1092 for (vfe = uvifs[vifi].uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
1093 if (vfe->vfe_flags & VFEF_EXACT) {
1094 if ((vfe->vfe_addr == rt[i].origin) &&
1095 (vfe->vfe_mask == rt[i].mask)) {
1096 match = 1;
1097 break;
1099 } else {
1100 if ((rt[i].origin & vfe->vfe_mask) == vfe->vfe_addr) {
1101 match = 1;
1102 break;
1106 if ((uvifs[vifi].uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
1107 (uvifs[vifi].uv_filter->vf_type == VFT_DENY && match == 1)) {
1108 IF_DEBUG(DEBUG_ROUTE)
1109 dolog(LOG_DEBUG, 0, "%s skipped on vif %d because it %s %s",
1110 inet_fmts(rt[i].origin, rt[i].mask, s1),
1111 vifi,
1112 match ? "matches" : "doesn't match",
1113 match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
1114 "the filter");
1115 #if 0
1116 rt[i].metric += vfe->vfe_addmetric;
1117 if (rt[i].metric > UNREACHABLE)
1118 #endif
1119 rt[i].metric = UNREACHABLE;
1122 update_route(rt[i].origin, rt[i].mask, rt[i].metric,
1123 src, vifi, nbr);
1126 if (routes_changed && !delay_change_reports)
1127 report_to_all_neighbors(CHANGED_ROUTES);
1132 * Send a route report message to destination 'dst', via virtual interface
1133 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
1135 void
1136 report(int which_routes, vifi_t vifi, u_int32 dst)
1138 struct rtentry *r;
1139 int i;
1141 r = rt_end;
1142 while (r != RT_ADDR) {
1143 i = report_chunk(which_routes, r, vifi, dst);
1144 while (i-- > 0)
1145 r = r->rt_prev;
1151 * Send a route report message to all neighboring routers.
1152 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
1154 void
1155 report_to_all_neighbors(int which_routes)
1157 vifi_t vifi;
1158 struct uvif *v;
1159 struct rtentry *r;
1160 int routes_changed_before;
1163 * Remember the state of the global routes_changed flag before
1164 * generating the reports, and clear the flag.
1166 routes_changed_before = routes_changed;
1167 routes_changed = FALSE;
1170 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
1171 if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
1172 report(which_routes, vifi, v->uv_dst_addr);
1177 * If there were changed routes before we sent the reports AND
1178 * if no new changes occurred while sending the reports, clear
1179 * the change flags in the individual route entries. If changes
1180 * did occur while sending the reports, new reports will be
1181 * generated at the next timer interrupt.
1183 if (routes_changed_before && !routes_changed) {
1184 for (r = routing_table; r != NULL; r = r->rt_next) {
1185 r->rt_flags &= ~RTF_CHANGED;
1190 * Set a flag to inhibit further reports of changed routes until the
1191 * next timer interrupt. This is to alleviate update storms.
1193 delay_change_reports = TRUE;
1197 * Send a route report message to destination 'dst', via virtual interface
1198 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
1200 static int
1201 report_chunk(int which_routes, struct rtentry *start_rt, vifi_t vifi,
1202 u_int32 dst)
1204 struct rtentry *r;
1205 char *p;
1206 int i;
1207 int nrt = 0;
1208 struct uvif *v = &uvifs[vifi];
1209 int datalen = 0;
1210 int width = 0;
1211 u_int32 mask = 0;
1212 u_int32 src;
1213 int admetric = v->uv_admetric;
1214 int metric;
1216 src = v->uv_lcl_addr;
1217 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
1219 for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
1220 if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) {
1221 nrt++;
1222 continue;
1226 * Do not poison-reverse a route for a directly-connected
1227 * subnetwork on that subnetwork. This can cause loops when
1228 * some router on the subnetwork is misconfigured.
1230 if (r->rt_gateway == 0 && r->rt_parent == vifi) {
1231 nrt++;
1232 continue;
1235 if (v->uv_filter && v->uv_filter->vf_flags & VFF_BIDIR) {
1236 struct vf_element *vfe;
1237 int match = 0;
1239 for (vfe = v->uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
1240 if (vfe->vfe_flags & VFEF_EXACT) {
1241 if ((vfe->vfe_addr == r->rt_origin) &&
1242 (vfe->vfe_mask == r->rt_originmask)) {
1243 match = 1;
1244 break;
1246 } else {
1247 if ((r->rt_origin & vfe->vfe_mask) == vfe->vfe_addr) {
1248 match = 1;
1249 break;
1253 if ((v->uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
1254 (v->uv_filter->vf_type == VFT_DENY && match == 1)) {
1255 IF_DEBUG(DEBUG_ROUTE)
1256 dolog(LOG_DEBUG, 0, "%s not reported on vif %d because it %s %s",
1257 RT_FMT(r, s1), vifi,
1258 match ? "matches" : "doesn't match",
1259 match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
1260 "the filter");
1261 nrt++;
1262 continue;
1267 * If there is no room for this route in the current message,
1268 * send it & return how many routes we sent.
1270 if (datalen + ((r->rt_originmask == mask) ?
1271 (width + 1) :
1272 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
1273 *(p-1) |= 0x80;
1274 send_on_vif(v, 0, DVMRP_REPORT, datalen);
1275 return (nrt);
1278 if (r->rt_originmask != mask || datalen == 0) {
1279 mask = r->rt_originmask;
1280 width = r->rt_originwidth;
1281 if (datalen != 0) *(p-1) |= 0x80;
1282 *p++ = ((char *)&mask)[1];
1283 *p++ = ((char *)&mask)[2];
1284 *p++ = ((char *)&mask)[3];
1285 datalen += 3;
1287 for (i = 0; i < width; ++i)
1288 *p++ = ((char *)&(r->rt_origin))[i];
1290 metric = r->rt_metric + admetric;
1291 if (metric > UNREACHABLE)
1292 metric = UNREACHABLE;
1293 if (r->rt_parent != vifi && AVOID_TRANSIT(vifi, r))
1294 metric = UNREACHABLE;
1295 *p++ = (r->rt_parent == vifi && metric != UNREACHABLE) ?
1296 (char)(metric + UNREACHABLE) : /* "poisoned reverse" */
1297 (char)(metric);
1298 ++nrt;
1299 datalen += width + 1;
1301 if (datalen != 0) {
1302 *(p-1) |= 0x80;
1303 send_on_vif(v, 0, DVMRP_REPORT, datalen);
1305 return (nrt);
1309 * send the next chunk of our routing table to all neighbors.
1310 * return the length of the smallest chunk we sent out.
1313 report_next_chunk(void)
1315 vifi_t vifi;
1316 struct uvif *v;
1317 struct rtentry *sr;
1318 int i, n = 0, min = 20000;
1319 static int start_rt;
1321 if (nroutes <= 0)
1322 return (0);
1325 * find this round's starting route.
1327 for (sr = rt_end, i = start_rt; --i >= 0; ) {
1328 sr = sr->rt_prev;
1329 if (sr == RT_ADDR)
1330 sr = rt_end;
1334 * send one chunk of routes starting at this round's start to
1335 * all our neighbors.
1337 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
1338 if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
1339 n = report_chunk(ALL_ROUTES, sr, vifi, v->uv_dst_addr);
1340 if (n < min)
1341 min = n;
1344 if (min == 20000)
1345 min = 0; /* Neighborless router didn't send any routes */
1347 n = min;
1348 IF_DEBUG(DEBUG_ROUTE)
1349 dolog(LOG_INFO, 0, "update %d starting at %d of %d",
1350 n, (nroutes - start_rt), nroutes);
1352 start_rt = (start_rt + n) % nroutes;
1353 return (n);
1358 * Print the contents of the routing table on file 'fp'.
1360 void
1361 dump_routes(FILE *fp)
1363 struct rtentry *r;
1364 vifi_t i;
1366 fprintf(fp,
1367 "Multicast Routing Table (%u %s)\n%s\n",
1368 nroutes, (nroutes == 1) ? "entry" : "entries",
1369 " Origin-Subnet From-Gateway Metric Tmr Fl In-Vif Out-Vifs");
1371 for (r = routing_table; r != NULL; r = r->rt_next) {
1373 fprintf(fp, " %-18s %-15s ",
1374 inet_fmts(r->rt_origin, r->rt_originmask, s1),
1375 (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
1377 fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
1378 r->rt_metric);
1380 fprintf(fp, " %3u %c%c %3u ", r->rt_timer,
1381 (r->rt_flags & RTF_CHANGED) ? 'C' : '.',
1382 (r->rt_flags & RTF_HOLDDOWN) ? 'H' : '.',
1383 r->rt_parent);
1385 for (i = 0; i < numvifs; ++i) {
1386 struct listaddr *n;
1387 char l = '[';
1389 if (VIFM_ISSET(i, r->rt_children)) {
1390 if ((uvifs[i].uv_flags & VIFF_TUNNEL) &&
1391 !NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
1392 /* Don't print out parenthood of a leaf tunnel. */
1393 continue;
1394 fprintf(fp, " %u", i);
1395 if (!NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
1396 fprintf(fp, "*");
1397 for (n = uvifs[i].uv_neighbors; n; n = n->al_next) {
1398 if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
1399 fprintf(fp, "%c%d", l, n->al_index);
1400 l = ',';
1403 if (l == ',')
1404 fprintf(fp, "]");
1407 fprintf(fp, "\n");
1409 fprintf(fp, "\n");
1412 struct rtentry *
1413 determine_route(u_int32 src)
1415 struct rtentry *rt;
1417 for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
1418 if (rt->rt_origin == (src & rt->rt_originmask) &&
1419 rt->rt_metric != UNREACHABLE)
1420 break;
1422 return rt;