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 $
18 * This define statement saves a lot of space later
20 #define RT_ADDR (struct rtentry *)&routing_table
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 */
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 */
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
,
52 static void queue_blaster_report(vifi_t
, u_int32
, u_int32
, char *,
54 static void process_blaster_report(void *);
57 #include <sys/types.h>
61 * Return pointer to a specific route entry. This must be a separate
62 * function from find_route() which modifies rtp.
65 snmp_find_route(u_int32 src
, u_int32 mask
)
69 for (rt
= routing_table
; rt
; rt
= rt
->rt_next
) {
70 if (src
== rt
->rt_origin
&& mask
== rt
->rt_originmask
)
77 * Find next route entry > specification
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
))))
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
))
109 /* Continue until we get one with a valid next vif */
111 for (; (*rtpp
)->rt_children
&& *vifi
<numvifs
; (*vifi
)++)
112 if (VIFM_ISSET(*vifi
, (*rtpp
)->rt_children
))
115 } while( next_route(rtpp
, (*rtpp
)->rt_origin
, (*rtpp
)->rt_originmask
) );
122 * Initialize the routing table and associated variables.
127 routing_table
= NULL
;
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?
143 init_children_and_leaves(struct rtentry
*r
, vifi_t parent
, int first
)
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
);
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.
180 add_vif_to_routes(vifi_t 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.
205 delete_vif_from_routes(vifi_t vifi
)
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
);
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
237 add_neighbor_to_routes(vifi_t vifi
, int index
)
243 if (v
->uv_flags
& VIFF_NOFLOOD
)
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
262 delete_neighbor_from_routes(u_int32 addr
, vifi_t vifi
, int index
)
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
);
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
303 start_route_updates(void)
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.
319 find_route(u_int32 origin
, u_int32 mask
)
325 if (origin
== r
->rt_origin
&& mask
== r
->rt_originmask
) {
329 if (ntohl(mask
) < ntohl(r
->rt_originmask
) ||
330 (mask
== r
->rt_originmask
&&
331 ntohl(origin
) < ntohl(r
->rt_origin
))) {
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
351 create_route(u_int32 origin
, u_int32 mask
)
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;
366 r
->rt_dominants
= (u_int32
*)(r
+ 1);
367 bzero(r
->rt_dominants
, numvifs
* sizeof(u_int32
));
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
;
376 if (r
->rt_next
!= NULL
)
377 (r
->rt_next
)->rt_prev
= r
;
386 * Discard the routing table entry following the one to which 'prev_r' points.
389 discard_route(struct rtentry
*prev_r
)
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
;
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.
413 update_route(u_int32 origin
, u_int32 mask
, u_int metric
, u_int32 src
,
414 vifi_t vifi
, struct listaddr
*n
)
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
));
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
)) {
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
) {
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
));
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
460 create_route(origin
, mask
);
461 uvifs
[vifi
].uv_nroutes
++;
464 rtp
->rt_metric
= UNREACHABLE
; /* temporary; updated below */
468 * We now have a routing entry for the reported origin. Update it?
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
)
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.
499 init_children_and_leaves(r
, vifi
, 1);
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.
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
)
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
;
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?
557 old_gateway
= r
->rt_gateway
;
558 old_parent
= r
->rt_parent
;
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
,
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--;*/
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
),
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
;
612 * We don't necessarily want to forget about subordinateness
613 * so that we can become the dominant quickly if the current
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
),
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
);
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
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
),
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
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
),
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
);
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
);
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
),
693 * On every timer interrupt, advance the timer in each routing entry.
699 struct rtentry
*prev_r
;
700 extern u_long virtual_time
; /* from main.c */
702 for (prev_r
= RT_ADDR
, r
= routing_table
;
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
);
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) {
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",
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.
761 expire_all_routes(void)
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.
777 free_all_routes(void)
789 * Process an incoming neighbor probe message.
792 accept_probe(u_int32 src
, u_int32 dst
, char *p
, int datalen
, u_int32 level
)
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
)
805 if (a
->al_ctime
+ 2 * a
->al_timer
< now
) {
806 /* We haven't heard from it in a long time */
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;
829 "ignoring probe from non-neighbor %s (%lu seconds until next warning)", inet_fmt(src
, s1
), match
->al_ctime
+ match
->al_timer
- now
);
833 update_neighbor(vifi
, src
, DVMRP_PROBE
, p
, datalen
, level
);
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
);
857 /* masks are equal */
858 o1
= ntohl(r1
->origin
);
859 o2
= ntohl(r2
->origin
);
868 blaster_alloc(vifi_t 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;
892 * Queue a route report from a route-blaster.
893 * If the timer isn't running to process these reports,
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
;
902 int bblen
= sizeof(*bh
) + ((datalen
+ 3) & ~3);
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;
912 dolog(LOG_DEBUG
, 0, "increasing blasterbuf to %d bytes",
914 v
->uv_blasterbuf
= realloc(v
->uv_blasterbuf
,
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
;
923 v
->uv_blasterend
= v
->uv_blasterbuf
+ end
;
924 v
->uv_blastercur
= v
->uv_blasterbuf
+ cur
;
926 bh
= (struct blaster_hdr
*)v
->uv_blasterend
;
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 *));
938 dolog(LOG_ERR
, 0, "out of memory");
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
953 process_blaster_report(void *vifip
)
955 vifi_t vifi
= *(int *)vifip
;
957 struct blaster_hdr
*bh
;
960 IF_DEBUG(DEBUG_ROUTE
)
961 dolog(LOG_DEBUG
, 0, "processing vif %d blasted routes", vifi
);
963 for (i
= 0; i
< 5; i
++) {
964 if (v
->uv_blastercur
>= v
->uv_blasterend
)
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;
977 IF_DEBUG(DEBUG_ROUTE
)
978 dolog(LOG_DEBUG
, 0, "finish processing vif %d blaster", vifi
);
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.
995 accept_report(u_int32 src
, u_int32 dst
, char *p
, int datalen
, u_int32 level
)
998 int width
, i
, nrt
= 0;
1002 struct newrt rt
[4096];
1003 struct listaddr
*nbr
;
1005 if ((vifi
= find_vif(src
, dst
)) == NO_VIF
) {
1007 "ignoring route report from non-neighbor %s", inet_fmt(src
, s1
));
1011 if (uvifs
[vifi
].uv_flags
& VIFF_BLASTER
) {
1013 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
)