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 $
13 * $DragonFly: src/usr.sbin/mrouted/route.c,v 1.5 2005/12/05 00:58:50 swildner Exp $
19 * This define statement saves a lot of space later
21 #define RT_ADDR (struct rtentry *)&routing_table
26 int routes_changed
; /* 1=>some routes have changed */
27 int delay_change_reports
; /* 1=>postpone change reports */
31 * The routing table is shared with prune.c , so must not be static.
33 struct rtentry
*routing_table
; /* pointer to list of route entries */
38 static struct rtentry
*rtp
; /* pointer to a route entry */
39 static struct rtentry
*rt_end
; /* pointer to last route entry */
40 unsigned int nroutes
; /* current number of route entries */
45 static int init_children_and_leaves(struct rtentry
*r
,
46 vifi_t parent
, int first
);
47 static int find_route (u_int32 origin
, u_int32 mask
);
48 static void create_route(u_int32 origin
, u_int32 mask
);
49 static void discard_route(struct rtentry
*prev_r
);
50 static int compare_rts (const void *rt1
, const void *rt2
);
51 static int report_chunk (int, struct rtentry
*start_rt
, vifi_t vifi
,
53 static void queue_blaster_report(vifi_t
, u_int32
, u_int32
, char *,
55 static void process_blaster_report(void *);
58 #include <sys/types.h>
62 * Return pointer to a specific route entry. This must be a separate
63 * function from find_route() which modifies rtp.
66 snmp_find_route(u_int32 src
, u_int32 mask
)
70 for (rt
= routing_table
; rt
; rt
= rt
->rt_next
) {
71 if (src
== rt
->rt_origin
&& mask
== rt
->rt_originmask
)
78 * Find next route entry > specification
81 next_route(struct rtentry
**rtpp
, u_int32 src
, u_int32 mask
)
83 struct rtentry
*rt
, *rbest
= NULL
;
85 /* Among all entries > spec, find "lowest" one in order */
86 for (rt
= routing_table
; rt
; rt
=rt
->rt_next
) {
87 if ((ntohl(rt
->rt_origin
) > ntohl(src
)
88 || (ntohl(rt
->rt_origin
) == ntohl(src
)
89 && ntohl(rt
->rt_originmask
) > ntohl(mask
)))
90 && (!rbest
|| (ntohl(rt
->rt_origin
) < ntohl(rbest
->rt_origin
))
91 || (ntohl(rt
->rt_origin
) == ntohl(rbest
->rt_origin
)
92 && ntohl(rt
->rt_originmask
) < ntohl(rbest
->rt_originmask
))))
100 * Given a routing table entry, and a vifi, find the next vifi/entry
103 next_route_child(struct rtentry
**rtpp
, u_int32 src
, u_int32 mask
, vifi_t
*vifi
)
105 /* Get (S,M) entry */
106 if (!((*rtpp
) = snmp_find_route(src
,mask
)))
107 if (!next_route(rtpp
, src
, mask
))
110 /* Continue until we get one with a valid next vif */
112 for (; (*rtpp
)->rt_children
&& *vifi
<numvifs
; (*vifi
)++)
113 if (VIFM_ISSET(*vifi
, (*rtpp
)->rt_children
))
116 } while( next_route(rtpp
, (*rtpp
)->rt_origin
, (*rtpp
)->rt_originmask
) );
123 * Initialize the routing table and associated variables.
128 routing_table
= NULL
;
131 routes_changed
= FALSE
;
132 delay_change_reports
= FALSE
;
137 * Initialize the children bits for route 'r', along with the
138 * associated dominant and subordinate data structures.
139 * If first is set, initialize dominants, otherwise keep old
140 * dominants on non-parent interfaces.
141 * XXX Does this need a return value?
144 init_children_and_leaves(struct rtentry
*r
, vifi_t parent
, int first
)
148 vifbitmap_t old_children
;
149 nbrbitmap_t old_subords
;
151 VIFM_COPY(r
->rt_children
, old_children
);
152 NBRM_COPY(r
->rt_subordinates
, old_subords
);
154 VIFM_CLRALL(r
->rt_children
);
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
);
163 NBRM_SETMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
165 if (vifi
!= parent
&& !(v
->uv_flags
& (VIFF_DOWN
|VIFF_DISABLED
)) &&
166 !(!first
&& r
->rt_dominants
[vifi
])) {
167 VIFM_SET(vifi
, r
->rt_children
);
171 return (!VIFM_SAME(r
->rt_children
, old_children
) ||
172 !NBRM_SAME(r
->rt_subordinates
, old_subords
));
177 * A new vif has come up -- update the children bitmaps in all route
178 * entries to take that into account.
181 add_vif_to_routes(vifi_t vifi
)
187 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
188 if (r
->rt_metric
!= UNREACHABLE
&&
189 !VIFM_ISSET(vifi
, r
->rt_children
)) {
190 VIFM_SET(vifi
, r
->rt_children
);
191 r
->rt_dominants
[vifi
] = 0;
192 /*XXX isn't uv_nbrmap going to be empty?*/
193 NBRM_CLRMASK(r
->rt_subordinates
, v
->uv_nbrmap
);
194 update_table_entry(r
, r
->rt_gateway
);
201 * A vif has gone down -- expire all routes that have that vif as parent,
202 * and update the children bitmaps in all other route entries to take into
203 * account the failed vif.
206 delete_vif_from_routes(vifi_t vifi
)
210 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
211 if (r
->rt_metric
!= UNREACHABLE
) {
212 if (vifi
== r
->rt_parent
) {
213 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
214 r
->rt_timer
= ROUTE_EXPIRE_TIME
;
215 r
->rt_metric
= UNREACHABLE
;
216 r
->rt_flags
|= RTF_CHANGED
;
217 routes_changed
= TRUE
;
219 else if (VIFM_ISSET(vifi
, r
->rt_children
)) {
220 VIFM_CLR(vifi
, r
->rt_children
);
221 NBRM_CLRMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
222 update_table_entry(r
, r
->rt_gateway
);
225 r
->rt_dominants
[vifi
] = 0;
233 * A new neighbor has come up. If we're flooding on the neighbor's
234 * vif, mark that neighbor as subordinate for all routes whose parent
238 add_neighbor_to_routes(vifi_t vifi
, int index
)
244 if (v
->uv_flags
& VIFF_NOFLOOD
)
246 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
247 if (r
->rt_metric
!= UNREACHABLE
&& r
->rt_parent
!= vifi
&&
248 !AVOID_TRANSIT(vifi
, r
)) {
249 NBRM_SET(index
, r
->rt_subordinates
);
250 update_table_entry(r
, r
->rt_gateway
);
257 * A neighbor has failed or become unreachable. If that neighbor was
258 * considered a dominant or subordinate router in any route entries,
259 * take appropriate action. Expire all routes this neighbor advertised
263 delete_neighbor_from_routes(u_int32 addr
, vifi_t vifi
, int index
)
269 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
270 if (r
->rt_metric
!= UNREACHABLE
) {
271 if (r
->rt_parent
== vifi
&& r
->rt_gateway
== addr
) {
272 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
273 r
->rt_timer
= ROUTE_EXPIRE_TIME
;
274 r
->rt_metric
= UNREACHABLE
;
275 r
->rt_flags
|= RTF_CHANGED
;
276 routes_changed
= TRUE
;
277 } else if (r
->rt_dominants
[vifi
] == addr
) {
278 VIFM_SET(vifi
, r
->rt_children
);
279 r
->rt_dominants
[vifi
] = 0;
280 if ((uvifs
[vifi
].uv_flags
& VIFF_NOFLOOD
) ||
281 AVOID_TRANSIT(vifi
, r
))
282 NBRM_CLRMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
284 NBRM_SETMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
285 update_table_entry(r
, r
->rt_gateway
);
286 } else if (NBRM_ISSET(index
, r
->rt_subordinates
)) {
287 NBRM_CLR(index
, r
->rt_subordinates
);
288 update_table_entry(r
, r
->rt_gateway
);
296 * Prepare for a sequence of ordered route updates by initializing a pointer
297 * to the start of the routing table. The pointer is used to remember our
298 * position in the routing table in order to avoid searching from the
299 * beginning for each update; this relies on having the route reports in
300 * a single message be in the same order as the route entries in the routing
304 start_route_updates(void)
311 * Starting at the route entry following the one to which 'rtp' points,
312 * look for a route entry matching the specified origin and mask. If a
313 * match is found, return TRUE and leave 'rtp' pointing at the found entry.
314 * If no match is found, return FALSE and leave 'rtp' pointing to the route
315 * entry preceding the point at which the new origin should be inserted.
316 * This code is optimized for the normal case in which the first entry to
317 * be examined is the matching entry.
320 find_route(u_int32 origin
, u_int32 mask
)
326 if (origin
== r
->rt_origin
&& mask
== r
->rt_originmask
) {
330 if (ntohl(mask
) < ntohl(r
->rt_originmask
) ||
331 (mask
== r
->rt_originmask
&&
332 ntohl(origin
) < ntohl(r
->rt_origin
))) {
342 * Create a new routing table entry for the specified origin and link it into
343 * the routing table. The shared variable 'rtp' is assumed to point to the
344 * routing entry after which the new one should be inserted. It is left
345 * pointing to the new entry.
347 * Only the origin, originmask, originwidth and flags fields are initialized
348 * in the new route entry; the caller is responsible for filling in the the
352 create_route(u_int32 origin
, u_int32 mask
)
356 if ((r
= (struct rtentry
*) malloc(sizeof(struct rtentry
) +
357 (numvifs
* sizeof(u_int32
)))) == NULL
) {
358 dolog(LOG_ERR
, 0, "ran out of memory"); /* fatal */
360 r
->rt_origin
= origin
;
361 r
->rt_originmask
= mask
;
362 if (((char *)&mask
)[3] != 0) r
->rt_originwidth
= 4;
363 else if (((char *)&mask
)[2] != 0) r
->rt_originwidth
= 3;
364 else if (((char *)&mask
)[1] != 0) r
->rt_originwidth
= 2;
365 else r
->rt_originwidth
= 1;
367 r
->rt_dominants
= (u_int32
*)(r
+ 1);
368 bzero(r
->rt_dominants
, numvifs
* sizeof(u_int32
));
370 VIFM_CLRALL(r
->rt_children
);
371 NBRM_CLRALL(r
->rt_subordinates
);
372 NBRM_CLRALL(r
->rt_subordadv
);
374 r
->rt_next
= rtp
->rt_next
;
377 if (r
->rt_next
!= NULL
)
378 (r
->rt_next
)->rt_prev
= r
;
387 * Discard the routing table entry following the one to which 'prev_r' points.
390 discard_route(struct rtentry
*prev_r
)
395 uvifs
[r
->rt_parent
].uv_nroutes
--;
396 /*???nbr???.al_nroutes--;*/
397 prev_r
->rt_next
= r
->rt_next
;
398 if (prev_r
->rt_next
!= NULL
)
399 (prev_r
->rt_next
)->rt_prev
= prev_r
;
408 * Process a route report for a single origin, creating or updating the
409 * corresponding routing table entry if necessary. 'src' is either the
410 * address of a neighboring router from which the report arrived, or zero
411 * to indicate a change of status of one of our own interfaces.
414 update_route(u_int32 origin
, u_int32 mask
, u_int metric
, u_int32 src
,
415 vifi_t vifi
, struct listaddr
*n
)
421 * Compute an adjusted metric, taking into account the cost of the
422 * subnet or tunnel over which the report arrived, and normalizing
423 * all unreachable/poisoned metrics into a single value.
425 if (src
!= 0 && (metric
< 1 || metric
>= 2*UNREACHABLE
)) {
426 dolog(LOG_WARNING
, 0,
427 "%s reports out-of-range metric %u for origin %s",
428 inet_fmt(src
, s1
), metric
, inet_fmts(origin
, mask
, s2
));
431 adj_metric
= metric
+ uvifs
[vifi
].uv_metric
;
432 if (adj_metric
> UNREACHABLE
) adj_metric
= UNREACHABLE
;
435 * Look up the reported origin in the routing table.
437 if (!find_route(origin
, mask
)) {
440 * Don't create a new entry if the report says it's unreachable,
441 * or if the reported origin and mask are invalid.
443 if (adj_metric
== UNREACHABLE
) {
446 if (src
!= 0 && !inet_valid_subnet(origin
, mask
)) {
447 dolog(LOG_WARNING
, 0,
448 "%s reports an invalid origin (%s) and/or mask (%08x)",
449 inet_fmt(src
, s1
), inet_fmt(origin
, s2
), ntohl(mask
));
453 IF_DEBUG(DEBUG_RTDETAIL
)
454 dolog(LOG_DEBUG
, 0, "%s advertises new route %s",
455 inet_fmt(src
, s1
), inet_fmts(origin
, mask
, s2
));
458 * OK, create the new routing entry. 'rtp' will be left pointing
461 create_route(origin
, mask
);
462 uvifs
[vifi
].uv_nroutes
++;
465 rtp
->rt_metric
= UNREACHABLE
; /* temporary; updated below */
469 * We now have a routing entry for the reported origin. Update it?
472 if (r
->rt_metric
== UNREACHABLE
) {
474 * The routing entry is for a formerly-unreachable or new origin.
475 * If the report claims reachability, update the entry to use
476 * the reported route.
478 if (adj_metric
== UNREACHABLE
)
481 IF_DEBUG(DEBUG_RTDETAIL
)
482 dolog(LOG_DEBUG
, 0, "%s advertises %s with adj_metric %d (ours was %d)",
483 inet_fmt(src
, s1
), inet_fmts(origin
, mask
, s2
),
484 adj_metric
, r
->rt_metric
);
487 * Now "steal away" any sources that belong under this route
488 * by deleting any cache entries they might have created
489 * and allowing the kernel to re-request them.
491 * If we haven't performed final initialization yet and are
492 * just collecting the routing table, we can't have any
493 * sources so we don't perform this step.
500 init_children_and_leaves(r
, vifi
, 1);
503 r
->rt_metric
= adj_metric
;
504 r
->rt_flags
|= RTF_CHANGED
;
505 routes_changed
= TRUE
;
506 update_table_entry(r
, r
->rt_gateway
);
508 else if (src
== r
->rt_gateway
) {
510 * The report has come either from the interface directly-connected
511 * to the origin subnet (src and r->rt_gateway both equal zero) or
512 * from the gateway we have chosen as the best first-hop gateway back
513 * towards the origin (src and r->rt_gateway not equal zero). Reset
514 * the route timer and, if the reported metric has changed, update
515 * our entry accordingly.
519 IF_DEBUG(DEBUG_RTDETAIL
)
520 dolog(LOG_DEBUG
, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)",
521 inet_fmt(src
, s1
), inet_fmts(origin
, mask
, s2
),
522 adj_metric
, r
->rt_metric
);
524 if (adj_metric
== r
->rt_metric
)
527 if (adj_metric
== UNREACHABLE
) {
528 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
529 r
->rt_timer
= ROUTE_EXPIRE_TIME
;
531 r
->rt_metric
= adj_metric
;
532 r
->rt_flags
|= RTF_CHANGED
;
533 routes_changed
= TRUE
;
536 (r
->rt_gateway
!= 0 &&
537 (adj_metric
< r
->rt_metric
||
538 (adj_metric
== r
->rt_metric
&&
539 (ntohl(src
) < ntohl(r
->rt_gateway
) ||
540 r
->rt_timer
>= ROUTE_SWITCH_TIME
))))) {
542 * The report is for an origin we consider reachable; the report
543 * comes either from one of our own interfaces or from a gateway
544 * other than the one we have chosen as the best first-hop gateway
545 * back towards the origin. If the source of the update is one of
546 * our own interfaces, or if the origin is not a directly-connected
547 * subnet and the reported metric for that origin is better than
548 * what our routing entry says, update the entry to use the new
549 * gateway and metric. We also switch gateways if the reported
550 * metric is the same as the one in the route entry and the gateway
551 * associated with the route entry has not been heard from recently,
552 * or if the metric is the same but the reporting gateway has a lower
553 * IP address than the gateway associated with the route entry.
554 * Did you get all that?
558 old_gateway
= r
->rt_gateway
;
559 old_parent
= r
->rt_parent
;
563 IF_DEBUG(DEBUG_RTDETAIL
)
564 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)",
565 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
566 adj_metric
, inet_fmt(old_gateway
, s3
), old_parent
,
569 if (old_parent
!= vifi
) {
570 init_children_and_leaves(r
, vifi
, 0);
571 uvifs
[old_parent
].uv_nroutes
--;
572 uvifs
[vifi
].uv_nroutes
++;
574 if (old_gateway
!= src
) {
575 update_table_entry(r
, old_gateway
);
576 /*???old_gateway???->al_nroutes--;*/
580 r
->rt_metric
= adj_metric
;
581 r
->rt_flags
|= RTF_CHANGED
;
582 routes_changed
= TRUE
;
584 else if (vifi
!= r
->rt_parent
) {
586 * The report came from a vif other than the route's parent vif.
587 * Update the children info, if necessary.
589 if (AVOID_TRANSIT(vifi
, r
)) {
591 * The route's parent is a vif from which we're not supposed
592 * to transit onto this vif. Simply ignore the update.
594 IF_DEBUG(DEBUG_RTDETAIL
)
595 dolog(LOG_DEBUG
, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)",
596 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
598 } else if (VIFM_ISSET(vifi
, r
->rt_children
)) {
600 * Vif is a child vif for this route.
602 if (metric
< r
->rt_metric
||
603 (metric
== r
->rt_metric
&&
604 ntohl(src
) < ntohl(uvifs
[vifi
].uv_lcl_addr
))) {
606 * Neighbor has lower metric to origin (or has same metric
607 * and lower IP address) -- it becomes the dominant router,
608 * and vif is no longer a child for me.
610 VIFM_CLR(vifi
, r
->rt_children
);
611 r
->rt_dominants
[vifi
] = src
;
613 * We don't necessarily want to forget about subordinateness
614 * so that we can become the dominant quickly if the current
617 NBRM_CLRMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
618 update_table_entry(r
, r
->rt_gateway
);
619 IF_DEBUG(DEBUG_RTDETAIL
)
620 dolog(LOG_DEBUG
, 0, "%s on vif %d becomes dominant for %s with metric %d",
621 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
624 else if (metric
> UNREACHABLE
) { /* "poisoned reverse" */
626 * Neighbor considers this vif to be on path to route's
627 * origin; record this neighbor as subordinate
629 if (!NBRM_ISSET(n
->al_index
, r
->rt_subordinates
)) {
630 IF_DEBUG(DEBUG_RTDETAIL
)
631 dolog(LOG_DEBUG
, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d",
632 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
633 metric
- UNREACHABLE
);
634 NBRM_SET(n
->al_index
, r
->rt_subordinates
);
635 update_table_entry(r
, r
->rt_gateway
);
637 IF_DEBUG(DEBUG_RTDETAIL
)
638 dolog(LOG_DEBUG
, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d",
639 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
640 metric
- UNREACHABLE
);
642 NBRM_SET(n
->al_index
, r
->rt_subordadv
);
644 else if (NBRM_ISSET(n
->al_index
, r
->rt_subordinates
)) {
646 * Current subordinate no longer considers this vif to be on
647 * path to route's origin; it is no longer a subordinate
650 IF_DEBUG(DEBUG_RTDETAIL
)
651 dolog(LOG_DEBUG
, 0, "%s on vif %d is no longer a subordinate for %s with metric %d",
652 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
654 NBRM_CLR(n
->al_index
, r
->rt_subordinates
);
655 update_table_entry(r
, r
->rt_gateway
);
659 else if (src
== r
->rt_dominants
[vifi
] &&
660 (metric
> r
->rt_metric
||
661 (metric
== r
->rt_metric
&&
662 ntohl(src
) > ntohl(uvifs
[vifi
].uv_lcl_addr
)))) {
664 * Current dominant no longer has a lower metric to origin
665 * (or same metric and lower IP address); we adopt the vif
668 IF_DEBUG(DEBUG_RTDETAIL
)
669 dolog(LOG_DEBUG
, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d",
670 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
672 VIFM_SET(vifi
, r
->rt_children
);
673 r
->rt_dominants
[vifi
] = 0;
674 if (uvifs
[vifi
].uv_flags
& VIFF_NOFLOOD
)
675 NBRM_CLRMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
677 NBRM_SETMASK(r
->rt_subordinates
, uvifs
[vifi
].uv_nbrmap
);
678 if (metric
> UNREACHABLE
) {
679 NBRM_SET(n
->al_index
, r
->rt_subordinates
);
680 NBRM_SET(n
->al_index
, r
->rt_subordadv
);
682 update_table_entry(r
, r
->rt_gateway
);
684 IF_DEBUG(DEBUG_RTDETAIL
)
685 dolog(LOG_DEBUG
, 0, "%s on vif %d advertises %s with metric %d (ignored)",
686 inet_fmt(src
, s1
), vifi
, inet_fmts(origin
, mask
, s2
),
694 * On every timer interrupt, advance the timer in each routing entry.
700 struct rtentry
*prev_r
;
701 extern u_long virtual_time
; /* from main.c */
703 for (prev_r
= RT_ADDR
, r
= routing_table
;
705 prev_r
= r
, r
= r
->rt_next
) {
707 if ((r
->rt_timer
+= TIMER_INTERVAL
) >= ROUTE_DISCARD_TIME
) {
709 * Time to garbage-collect the route entry.
711 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
712 discard_route(prev_r
);
715 else if (r
->rt_timer
>= ROUTE_EXPIRE_TIME
&&
716 r
->rt_metric
!= UNREACHABLE
) {
718 * Time to expire the route entry. If the gateway is zero,
719 * i.e., it is a route to a directly-connected subnet, just
720 * set the timer back to zero; such routes expire only when
721 * the interface to the subnet goes down.
723 if (r
->rt_gateway
== 0) {
727 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
728 r
->rt_metric
= UNREACHABLE
;
729 r
->rt_flags
|= RTF_CHANGED
;
730 routes_changed
= TRUE
;
733 else if (virtual_time
% (ROUTE_REPORT_INTERVAL
* 2) == 0) {
735 * Time out subordinateness that hasn't been reported in
736 * the last 2 intervals.
738 if (!NBRM_SAME(r
->rt_subordinates
, r
->rt_subordadv
)) {
739 IF_DEBUG(DEBUG_ROUTE
)
740 dolog(LOG_DEBUG
, 0, "rt %s sub 0x%08x%08x subadv 0x%08x%08x metric %d",
742 r
->rt_subordinates
.hi
, r
->rt_subordinates
.lo
,
743 r
->rt_subordadv
.hi
, r
->rt_subordadv
.lo
, r
->rt_metric
);
744 NBRM_MASK(r
->rt_subordinates
, r
->rt_subordadv
);
745 update_table_entry(r
, r
->rt_gateway
);
747 NBRM_CLRALL(r
->rt_subordadv
);
754 * Mark all routes as unreachable. This function is called only from
755 * hup() in preparation for informing all neighbors that we are going
756 * off the air. For consistency, we ought also to delete all reachable
757 * route entries from the kernel, but since we are about to exit we rely
758 * on the kernel to do its own cleanup -- no point in making all those
759 * expensive kernel calls now.
762 expire_all_routes(void)
766 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
767 r
->rt_metric
= UNREACHABLE
;
768 r
->rt_flags
|= RTF_CHANGED
;
769 routes_changed
= TRUE
;
775 * Delete all the routes in the routing table.
778 free_all_routes(void)
790 * Process an incoming neighbor probe message.
793 accept_probe(u_int32 src
, u_int32 dst
, char *p
, int datalen
, u_int32 level
)
796 static struct listaddr
*unknowns
= NULL
;
798 if ((vifi
= find_vif(src
, dst
)) == NO_VIF
) {
799 struct listaddr
*a
, **prev
;
800 struct listaddr
*match
= NULL
;
801 time_t now
= time(0);
803 for (prev
= &unknowns
, a
= *prev
; a
; a
= *prev
) {
804 if (a
->al_addr
== src
)
806 if (a
->al_ctime
+ 2 * a
->al_timer
< now
) {
807 /* We haven't heard from it in a long time */
815 match
= *prev
= (struct listaddr
*)malloc(sizeof(struct listaddr
));
816 match
->al_next
= NULL
;
817 match
->al_addr
= src
;
818 match
->al_timer
= OLD_NEIGHBOR_EXPIRE_TIME
;
819 match
->al_ctime
= now
- match
->al_timer
;
822 if (match
->al_ctime
+ match
->al_timer
<= now
) {
823 dolog(LOG_WARNING
, 0,
824 "ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s",
825 inet_fmt(src
, s1
), s1
);
826 match
->al_timer
*= 2;
830 "ignoring probe from non-neighbor %s (%d seconds until next warning)", inet_fmt(src
, s1
), match
->al_ctime
+ match
->al_timer
- now
);
834 update_neighbor(vifi
, src
, DVMRP_PROBE
, p
, datalen
, level
);
845 compare_rts(const void *rt1
, const void *rt2
)
847 struct newrt
*r1
= (struct newrt
*)rt1
;
848 struct newrt
*r2
= (struct newrt
*)rt2
;
849 u_int32 m1
= ntohl(r1
->mask
);
850 u_int32 m2
= ntohl(r2
->mask
);
858 /* masks are equal */
859 o1
= ntohl(r1
->origin
);
860 o2
= ntohl(r2
->origin
);
869 blaster_alloc(vifi_t vifi
)
874 if (v
->uv_blasterbuf
)
875 free(v
->uv_blasterbuf
);
877 v
->uv_blasterlen
= 64*1024;
878 v
->uv_blasterbuf
= malloc(v
->uv_blasterlen
);
879 v
->uv_blastercur
= v
->uv_blasterend
= v
->uv_blasterbuf
;
880 if (v
->uv_blastertimer
)
881 timer_clearTimer(v
->uv_blastertimer
);
882 v
->uv_blastertimer
= 0;
893 * Queue a route report from a route-blaster.
894 * If the timer isn't running to process these reports,
898 queue_blaster_report(vifi_t vifi
, u_int32 src
, u_int32 dst
, char *p
,
899 int datalen
, u_int32 level
)
901 struct blaster_hdr
*bh
;
903 int bblen
= sizeof(*bh
) + ((datalen
+ 3) & ~3);
906 if (v
->uv_blasterend
- v
->uv_blasterbuf
+
907 bblen
> v
->uv_blasterlen
) {
908 int end
= v
->uv_blasterend
- v
->uv_blasterbuf
;
909 int cur
= v
->uv_blastercur
- v
->uv_blasterbuf
;
911 v
->uv_blasterlen
*= 2;
913 dolog(LOG_DEBUG
, 0, "increasing blasterbuf to %d bytes",
915 v
->uv_blasterbuf
= realloc(v
->uv_blasterbuf
,
917 if (v
->uv_blasterbuf
== NULL
) {
918 dolog(LOG_WARNING
, ENOMEM
, "turning off blaster on vif %d", vifi
);
919 v
->uv_blasterlen
= 0;
920 v
->uv_blasterend
= v
->uv_blastercur
= NULL
;
921 v
->uv_flags
&= ~VIFF_BLASTER
;
924 v
->uv_blasterend
= v
->uv_blasterbuf
+ end
;
925 v
->uv_blastercur
= v
->uv_blasterbuf
+ cur
;
927 bh
= (struct blaster_hdr
*)v
->uv_blasterend
;
930 bh
->bh_level
= level
;
931 bh
->bh_datalen
= datalen
;
932 bcopy(p
, (char *)(bh
+ 1), datalen
);
933 v
->uv_blasterend
+= bblen
;
935 if (v
->uv_blastertimer
== 0) {
936 int *i
= (int *)malloc(sizeof(int *));
939 dolog(LOG_ERR
, 0, "out of memory");
943 v
->uv_blastertimer
= timer_setTimer(5,
944 process_blaster_report
, i
);
949 * Periodic process; process up to 5 of the routes in the route-blaster
950 * queue. If there are more routes remaining, reschedule myself to run
954 process_blaster_report(void *vifip
)
956 vifi_t vifi
= *(int *)vifip
;
958 struct blaster_hdr
*bh
;
961 IF_DEBUG(DEBUG_ROUTE
)
962 dolog(LOG_DEBUG
, 0, "processing vif %d blasted routes", vifi
);
964 for (i
= 0; i
< 5; i
++) {
965 if (v
->uv_blastercur
>= v
->uv_blasterend
)
967 bh
= (struct blaster_hdr
*)v
->uv_blastercur
;
968 v
->uv_blastercur
+= sizeof(*bh
) + ((bh
->bh_datalen
+ 3) & ~3);
969 accept_report(bh
->bh_src
, bh
->bh_dst
, (char *)(bh
+ 1),
970 -bh
->bh_datalen
, bh
->bh_level
);
973 if (v
->uv_blastercur
>= v
->uv_blasterend
) {
974 v
->uv_blastercur
= v
->uv_blasterbuf
;
975 v
->uv_blasterend
= v
->uv_blasterbuf
;
976 v
->uv_blastertimer
= 0;
978 IF_DEBUG(DEBUG_ROUTE
)
979 dolog(LOG_DEBUG
, 0, "finish processing vif %d blaster", vifi
);
981 IF_DEBUG(DEBUG_ROUTE
)
982 dolog(LOG_DEBUG
, 0, "more blasted routes to come on vif %d", vifi
);
983 v
->uv_blastertimer
= timer_setTimer(1,
984 process_blaster_report
, vifip
);
989 * Process an incoming route report message.
990 * If the report arrived on a vif marked as a "blaster", then just
991 * queue it and return; queue_blaster_report() will schedule it for
992 * processing later. If datalen is negative, then this is actually
993 * a queued report so actually process it instead of queueing it.
996 accept_report(u_int32 src
, u_int32 dst
, char *p
, int datalen
, u_int32 level
)
999 int width
, i
, nrt
= 0;
1003 struct newrt rt
[4096];
1004 struct listaddr
*nbr
;
1006 if ((vifi
= find_vif(src
, dst
)) == NO_VIF
) {
1008 "ignoring route report from non-neighbor %s", inet_fmt(src
, s1
));
1012 if (uvifs
[vifi
].uv_flags
& VIFF_BLASTER
)
1014 queue_blaster_report(vifi
, src
, dst
, p
, datalen
, level
);
1020 if (!(nbr
= update_neighbor(vifi
, src
, DVMRP_REPORT
, NULL
, 0, level
)))
1023 if (datalen
> 2*4096) {
1025 "ignoring oversize (%d bytes) route report from %s",
1026 datalen
, inet_fmt(src
, s1
));
1030 while (datalen
> 0) { /* Loop through per-mask lists. */
1033 dolog(LOG_WARNING
, 0,
1034 "received truncated route report from %s",
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
));
1050 do { /* Loop through (origin, metric) pairs */
1051 if (datalen
< width
+ 1) {
1052 dolog(LOG_WARNING
, 0,
1053 "received truncated route report from %s",
1058 for (i
= 0; i
< width
; ++i
)
1059 ((char *)&origin
)[i
] = *p
++;
1061 datalen
-= width
+ 1;
1062 rt
[nrt
].mask
= mask
;
1063 rt
[nrt
].origin
= origin
;
1064 rt
[nrt
].metric
= (metric
& 0x7f);
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)
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
));
1087 /* Only filter non-poisoned updates. */
1088 if (uvifs
[vifi
].uv_filter
&& rt
[i
].metric
< UNREACHABLE
) {
1089 struct vf_element
*vfe
;
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
)) {
1100 if ((rt
[i
].origin
& vfe
->vfe_mask
) == vfe
->vfe_addr
) {
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
),
1112 match
? "matches" : "doesn't match",
1113 match
? inet_fmts(vfe
->vfe_addr
, vfe
->vfe_mask
, s2
) :
1116 rt
[i
].metric
+= vfe
->vfe_addmetric
;
1117 if (rt
[i
].metric
> UNREACHABLE
)
1119 rt
[i
].metric
= UNREACHABLE
;
1122 update_route(rt
[i
].origin
, rt
[i
].mask
, rt
[i
].metric
,
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.
1136 report(int which_routes
, vifi_t vifi
, u_int32 dst
)
1142 while (r
!= RT_ADDR
) {
1143 i
= report_chunk(which_routes
, r
, vifi
, dst
);
1151 * Send a route report message to all neighboring routers.
1152 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
1155 report_to_all_neighbors(int which_routes
)
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.
1201 report_chunk(int which_routes
, struct rtentry
*start_rt
, vifi_t vifi
,
1208 struct uvif
*v
= &uvifs
[vifi
];
1213 int admetric
= v
->uv_admetric
;
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
)) {
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
) {
1235 if (v
->uv_filter
&& v
->uv_filter
->vf_flags
& VFF_BIDIR
) {
1236 struct vf_element
*vfe
;
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
)) {
1247 if ((r
->rt_origin
& vfe
->vfe_mask
) == vfe
->vfe_addr
) {
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
) :
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
) ?
1272 (r
->rt_originwidth
+ 4)) > MAX_DVMRP_DATA_LEN
) {
1274 send_on_vif(v
, 0, DVMRP_REPORT
, datalen
);
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];
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" */
1299 datalen
+= width
+ 1;
1303 send_on_vif(v
, 0, DVMRP_REPORT
, datalen
);
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)
1318 int i
, n
= 0, min
= 20000;
1319 static int start_rt
;
1325 * find this round's starting route.
1327 for (sr
= rt_end
, i
= start_rt
; --i
>= 0; ) {
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
);
1345 min
= 0; /* Neighborless router didn't send any routes */
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
;
1358 * Print the contents of the routing table on file 'fp'.
1361 dump_routes(FILE *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 ",
1380 fprintf(fp
, " %3u %c%c %3u ", r
->rt_timer
,
1381 (r
->rt_flags
& RTF_CHANGED
) ? 'C' : '.',
1382 (r
->rt_flags
& RTF_HOLDDOWN
) ? 'H' : '.',
1385 for (i
= 0; i
< numvifs
; ++i
) {
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. */
1394 fprintf(fp
, " %u", i
);
1395 if (!NBRM_ISSETMASK(uvifs
[i
].uv_nbrmap
, r
->rt_subordinates
))
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
);
1413 determine_route(u_int32 src
)
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
)