1 /* $NetBSD: route.c,v 1.12 2006/05/25 01:44:28 christos Exp $ */
4 * The mrouted program is covered by the license in the accompanying file
5 * named "LICENSE". Use of the mrouted program represents acceptance of
6 * the terms and conditions listed in that file.
8 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9 * Leland Stanford Junior University.
17 * This define statement saves a lot of space later
19 #define RT_ADDR (struct rtentry *)&routing_table
24 int routes_changed
; /* 1=>some routes have changed */
25 int delay_change_reports
; /* 1=>postpone change reports */
29 * The routing table is shared with prune.c , so must not be static.
31 struct rtentry
*routing_table
; /* pointer to list of route entries */
36 static struct rtentry
*rtp
; /* pointer to a route entry */
37 static struct rtentry
*rt_end
; /* pointer to last route entry */
38 unsigned int nroutes
; /* current number of route entries */
43 static int init_children_and_leaves(struct rtentry
*r
, vifi_t parent
);
44 static int find_route(u_int32_t origin
, u_int32_t mask
);
45 static void create_route(u_int32_t origin
, u_int32_t mask
);
46 static void discard_route(struct rtentry
*prev_r
);
47 static int compare_rts(const void *rt1
, const void *rt2
);
48 static int report_chunk(struct rtentry
*start_rt
, vifi_t vifi
, u_int32_t dst
);
51 * Initialize the routing table and associated variables.
59 routes_changed
= FALSE
;
60 delay_change_reports
= FALSE
;
65 * Initialize the children and leaf bits for route 'r', along with the
66 * associated dominant, subordinate, and leaf timing data structures.
67 * Return TRUE if this changes the value of either the children or
68 * leaf bitmaps for 'r'.
71 init_children_and_leaves(struct rtentry
*r
, vifi_t parent
)
75 vifbitmap_t old_children
, old_leaves
;
77 VIFM_COPY(r
->rt_children
, old_children
);
78 VIFM_COPY(r
->rt_leaves
, old_leaves
);
80 VIFM_CLRALL(r
->rt_children
);
81 VIFM_CLRALL(r
->rt_leaves
);
82 r
->rt_flags
&= ~RTF_LEAF_TIMING
;
84 for (vifi
= 0, v
= uvifs
; vifi
< numvifs
; ++vifi
, ++v
) {
85 r
->rt_dominants
[vifi
] = 0;
86 r
->rt_subordinates
[vifi
] = 0;
88 if (vifi
!= parent
&& !(v
->uv_flags
& (VIFF_DOWN
|VIFF_DISABLED
))) {
89 VIFM_SET(vifi
, r
->rt_children
);
90 if (v
->uv_neighbors
== NULL
) {
91 VIFM_SET(vifi
, r
->rt_leaves
);
92 r
->rt_leaf_timers
[vifi
] = 0;
95 r
->rt_leaf_timers
[vifi
] = LEAF_CONFIRMATION_TIME
;
96 r
->rt_flags
|= RTF_LEAF_TIMING
;
100 r
->rt_leaf_timers
[vifi
] = 0;
104 return (!VIFM_SAME(r
->rt_children
, old_children
) ||
105 !VIFM_SAME(r
->rt_leaves
, old_leaves
));
110 * A new vif has come up -- update the children and leaf bitmaps in all route
111 * entries to take that into account.
114 add_vif_to_routes(vifi_t vifi
)
120 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
121 if (r
->rt_metric
!= UNREACHABLE
&&
122 !VIFM_ISSET(vifi
, r
->rt_children
)) {
123 VIFM_SET(vifi
, r
->rt_children
);
124 r
->rt_dominants
[vifi
] = 0;
125 r
->rt_subordinates
[vifi
] = 0;
126 if (v
->uv_neighbors
== NULL
) {
127 VIFM_SET(vifi
, r
->rt_leaves
);
128 r
->rt_leaf_timers
[vifi
] = 0;
131 VIFM_CLR(vifi
, r
->rt_leaves
);
132 r
->rt_leaf_timers
[vifi
] = LEAF_CONFIRMATION_TIME
;
133 r
->rt_flags
|= RTF_LEAF_TIMING
;
135 update_table_entry(r
);
142 * A vif has gone down -- expire all routes that have that vif as parent,
143 * and update the children bitmaps in all other route entries to take into
144 * account the failed vif.
147 delete_vif_from_routes(vifi_t vifi
)
151 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
152 if (r
->rt_metric
!= UNREACHABLE
) {
153 if (vifi
== r
->rt_parent
) {
154 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
155 r
->rt_timer
= ROUTE_EXPIRE_TIME
;
156 r
->rt_metric
= UNREACHABLE
;
157 r
->rt_flags
|= RTF_CHANGED
;
158 routes_changed
= TRUE
;
160 else if (VIFM_ISSET(vifi
, r
->rt_children
)) {
161 VIFM_CLR(vifi
, r
->rt_children
);
162 VIFM_CLR(vifi
, r
->rt_leaves
);
163 r
->rt_subordinates
[vifi
] = 0;
164 r
->rt_leaf_timers
[vifi
] = 0;
165 update_table_entry(r
);
168 r
->rt_dominants
[vifi
] = 0;
176 * A neighbor has failed or become unreachable. If that neighbor was
177 * considered a dominant or subordinate router in any route entries,
178 * take appropriate action.
181 delete_neighbor_from_routes(u_int32_t addr
, vifi_t vifi
)
187 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
188 if (r
->rt_metric
!= UNREACHABLE
) {
189 if (r
->rt_dominants
[vifi
] == addr
) {
190 VIFM_SET(vifi
, r
->rt_children
);
191 r
->rt_dominants
[vifi
] = 0;
192 r
->rt_subordinates
[vifi
] = 0;
193 if (v
->uv_neighbors
== NULL
) {
194 VIFM_SET(vifi
, r
->rt_leaves
);
195 r
->rt_leaf_timers
[vifi
] = 0;
198 VIFM_CLR(vifi
, r
->rt_leaves
);
199 r
->rt_leaf_timers
[vifi
] = LEAF_CONFIRMATION_TIME
;
200 r
->rt_flags
|= RTF_LEAF_TIMING
;
202 update_table_entry(r
);
204 else if (r
->rt_subordinates
[vifi
] == addr
) {
205 r
->rt_subordinates
[vifi
] = 0;
206 if (v
->uv_neighbors
== NULL
) {
207 VIFM_SET(vifi
, r
->rt_leaves
);
208 update_table_entry(r
);
211 r
->rt_leaf_timers
[vifi
] = LEAF_CONFIRMATION_TIME
;
212 r
->rt_flags
|= RTF_LEAF_TIMING
;
215 else if (v
->uv_neighbors
== NULL
&&
216 r
->rt_leaf_timers
[vifi
] != 0) {
217 VIFM_SET(vifi
, r
->rt_leaves
);
218 r
->rt_leaf_timers
[vifi
] = 0;
219 update_table_entry(r
);
227 * Prepare for a sequence of ordered route updates by initializing a pointer
228 * to the start of the routing table. The pointer is used to remember our
229 * position in the routing table in order to avoid searching from the
230 * beginning for each update; this relies on having the route reports in
231 * a single message be in the same order as the route entries in the routing
235 start_route_updates(void)
242 * Starting at the route entry following the one to which 'rtp' points,
243 * look for a route entry matching the specified origin and mask. If a
244 * match is found, return TRUE and leave 'rtp' pointing at the found entry.
245 * If no match is found, return FALSE and leave 'rtp' pointing to the route
246 * entry preceding the point at which the new origin should be inserted.
247 * This code is optimized for the normal case in which the first entry to
248 * be examined is the matching entry.
251 find_route(u_int32_t origin
, u_int32_t mask
)
257 if (origin
== r
->rt_origin
&& mask
== r
->rt_originmask
) {
261 if (ntohl(mask
) < ntohl(r
->rt_originmask
) ||
262 (mask
== r
->rt_originmask
&&
263 ntohl(origin
) < ntohl(r
->rt_origin
))) {
273 * Create a new routing table entry for the specified origin and link it into
274 * the routing table. The shared variable 'rtp' is assumed to point to the
275 * routing entry after which the new one should be inserted. It is left
276 * pointing to the new entry.
278 * Only the origin, originmask, originwidth and flags fields are initialized
279 * in the new route entry; the caller is responsible for filling in the rest.
282 create_route(u_int32_t origin
, u_int32_t mask
)
286 if ((r
= (struct rtentry
*) malloc(sizeof(struct rtentry
) +
287 (2 * numvifs
* sizeof(u_int32_t
)) +
288 (numvifs
* sizeof(u_int
)))) == NULL
) {
289 logit(LOG_ERR
, 0, "ran out of memory"); /* fatal */
292 r
->rt_origin
= origin
;
293 r
->rt_originmask
= mask
;
294 if (((char *)&mask
)[3] != 0) r
->rt_originwidth
= 4;
295 else if (((char *)&mask
)[2] != 0) r
->rt_originwidth
= 3;
296 else if (((char *)&mask
)[1] != 0) r
->rt_originwidth
= 2;
297 else r
->rt_originwidth
= 1;
299 r
->rt_dominants
= (u_int32_t
*)(r
+ 1);
300 r
->rt_subordinates
= (u_int32_t
*)(r
->rt_dominants
+ numvifs
);
301 r
->rt_leaf_timers
= (u_int
*)(r
->rt_subordinates
+ numvifs
);
304 r
->rt_next
= rtp
->rt_next
;
307 if (r
->rt_next
!= NULL
)
308 (r
->rt_next
)->rt_prev
= r
;
317 * Discard the routing table entry following the one to which 'prev_r' points.
320 discard_route(struct rtentry
*prev_r
)
325 prev_r
->rt_next
= r
->rt_next
;
326 if (prev_r
->rt_next
!= NULL
)
327 (prev_r
->rt_next
)->rt_prev
= prev_r
;
336 * Process a route report for a single origin, creating or updating the
337 * corresponding routing table entry if necessary. 'src' is either the
338 * address of a neighboring router from which the report arrived, or zero
339 * to indicate a change of status of one of our own interfaces.
342 update_route(u_int32_t origin
, u_int32_t mask
, u_int metric
, u_int32_t src
,
349 * Compute an adjusted metric, taking into account the cost of the
350 * subnet or tunnel over which the report arrived, and normalizing
351 * all unreachable/poisoned metrics into a single value.
353 if (src
!= 0 && (metric
< 1 || metric
>= 2*UNREACHABLE
)) {
354 logit(LOG_WARNING
, 0,
355 "%s reports out-of-range metric %u for origin %s",
356 inet_fmt(src
), metric
,
357 inet_fmts(origin
, mask
));
360 adj_metric
= metric
+ uvifs
[vifi
].uv_metric
;
361 if (adj_metric
> UNREACHABLE
) adj_metric
= UNREACHABLE
;
364 * Look up the reported origin in the routing table.
366 if (!find_route(origin
, mask
)) {
369 * Don't create a new entry if the report says it's unreachable,
370 * or if the reported origin and mask are invalid.
372 if (adj_metric
== UNREACHABLE
) {
375 if (src
!= 0 && !inet_valid_subnet(origin
, mask
)) {
376 logit(LOG_WARNING
, 0,
377 "%s reports an invalid origin (%s) and/or mask (%08x)",
379 inet_fmt(origin
), ntohl(mask
));
384 * OK, create the new routing entry. 'rtp' will be left pointing
387 create_route(origin
, mask
);
390 * Now "steal away" any sources that belong under this route
391 * by deleting any cache entries they might have created
392 * and allowing the kernel to re-request them.
396 rtp
->rt_metric
= UNREACHABLE
; /* temporary; updated below */
400 * We now have a routing entry for the reported origin. Update it?
403 if (r
->rt_metric
== UNREACHABLE
) {
405 * The routing entry is for a formerly-unreachable or new origin.
406 * If the report claims reachability, update the entry to use
407 * the reported route.
409 if (adj_metric
== UNREACHABLE
)
413 init_children_and_leaves(r
, vifi
);
417 r
->rt_metric
= adj_metric
;
418 r
->rt_flags
|= RTF_CHANGED
;
419 routes_changed
= TRUE
;
420 update_table_entry(r
);
422 else if (src
== r
->rt_gateway
) {
424 * The report has come either from the interface directly-connected
425 * to the origin subnet (src and r->rt_gateway both equal zero) or
426 * from the gateway we have chosen as the best first-hop gateway back
427 * towards the origin (src and r->rt_gateway not equal zero). Reset
428 * the route timer and, if the reported metric has changed, update
429 * our entry accordingly.
432 if (adj_metric
== r
->rt_metric
)
435 if (adj_metric
== UNREACHABLE
) {
436 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
437 r
->rt_timer
= ROUTE_EXPIRE_TIME
;
439 else if (adj_metric
< r
->rt_metric
) {
440 if (init_children_and_leaves(r
, vifi
)) {
441 update_table_entry(r
);
444 r
->rt_metric
= adj_metric
;
445 r
->rt_flags
|= RTF_CHANGED
;
446 routes_changed
= TRUE
;
449 (r
->rt_gateway
!= 0 &&
450 (adj_metric
< r
->rt_metric
||
451 (adj_metric
== r
->rt_metric
&&
452 (ntohl(src
) < ntohl(r
->rt_gateway
) ||
453 r
->rt_timer
>= ROUTE_SWITCH_TIME
))))) {
455 * The report is for an origin we consider reachable; the report
456 * comes either from one of our own interfaces or from a gateway
457 * other than the one we have chosen as the best first-hop gateway
458 * back towards the origin. If the source of the update is one of
459 * our own interfaces, or if the origin is not a directly-connected
460 * subnet and the reported metric for that origin is better than
461 * what our routing entry says, update the entry to use the new
462 * gateway and metric. We also switch gateways if the reported
463 * metric is the same as the one in the route entry and the gateway
464 * associated with the route entry has not been heard from recently,
465 * or if the metric is the same but the reporting gateway has a lower
466 * IP address than the gateway associated with the route entry.
467 * Did you get all that?
469 if (r
->rt_parent
!= vifi
|| adj_metric
< r
->rt_metric
) {
471 * XXX Why do we do this if we are just changing the metric?
474 if (init_children_and_leaves(r
, vifi
)) {
475 update_table_entry(r
);
480 r
->rt_metric
= adj_metric
;
481 r
->rt_flags
|= RTF_CHANGED
;
482 routes_changed
= TRUE
;
484 else if (vifi
!= r
->rt_parent
) {
486 * The report came from a vif other than the route's parent vif.
487 * Update the children and leaf info, if necessary.
489 if (VIFM_ISSET(vifi
, r
->rt_children
)) {
491 * Vif is a child vif for this route.
493 if (metric
< r
->rt_metric
||
494 (metric
== r
->rt_metric
&&
495 ntohl(src
) < ntohl(uvifs
[vifi
].uv_lcl_addr
))) {
497 * Neighbor has lower metric to origin (or has same metric
498 * and lower IP address) -- it becomes the dominant router,
499 * and vif is no longer a child for me.
501 VIFM_CLR(vifi
, r
->rt_children
);
502 VIFM_CLR(vifi
, r
->rt_leaves
);
503 r
->rt_dominants
[vifi
] = src
;
504 r
->rt_subordinates
[vifi
] = 0;
505 r
->rt_leaf_timers
[vifi
] = 0;
506 update_table_entry(r
);
508 else if (metric
> UNREACHABLE
) { /* "poisoned reverse" */
510 * Neighbor considers this vif to be on path to route's
511 * origin; if no subordinate recorded, record this neighbor
512 * as subordinate and clear the leaf flag.
514 if (r
->rt_subordinates
[vifi
] == 0) {
515 VIFM_CLR(vifi
, r
->rt_leaves
);
516 r
->rt_subordinates
[vifi
] = src
;
517 r
->rt_leaf_timers
[vifi
] = 0;
518 update_table_entry(r
);
521 else if (src
== r
->rt_subordinates
[vifi
]) {
523 * Current subordinate no longer considers this vif to be on
524 * path to route's origin; it is no longer a subordinate
525 * router, and we set the leaf confirmation timer to give
526 * us time to hear from other subordinates.
528 r
->rt_subordinates
[vifi
] = 0;
529 if (uvifs
[vifi
].uv_neighbors
== NULL
||
530 uvifs
[vifi
].uv_neighbors
->al_next
== NULL
) {
531 VIFM_SET(vifi
, r
->rt_leaves
);
532 update_table_entry(r
);
535 r
->rt_leaf_timers
[vifi
] = LEAF_CONFIRMATION_TIME
;
536 r
->rt_flags
|= RTF_LEAF_TIMING
;
541 else if (src
== r
->rt_dominants
[vifi
] &&
542 (metric
> r
->rt_metric
||
543 (metric
== r
->rt_metric
&&
544 ntohl(src
) > ntohl(uvifs
[vifi
].uv_lcl_addr
)))) {
546 * Current dominant no longer has a lower metric to origin
547 * (or same metric and lower IP address); we adopt the vif
550 VIFM_SET(vifi
, r
->rt_children
);
551 r
->rt_dominants
[vifi
] = 0;
552 if (metric
> UNREACHABLE
) {
553 r
->rt_subordinates
[vifi
] = src
;
555 else if (uvifs
[vifi
].uv_neighbors
== NULL
||
556 uvifs
[vifi
].uv_neighbors
->al_next
== NULL
) {
557 VIFM_SET(vifi
, r
->rt_leaves
);
560 r
->rt_leaf_timers
[vifi
] = LEAF_CONFIRMATION_TIME
;
561 r
->rt_flags
|= RTF_LEAF_TIMING
;
563 update_table_entry(r
);
570 * On every timer interrupt, advance the timer in each routing entry.
576 struct rtentry
*prev_r
;
579 for (prev_r
= RT_ADDR
, r
= routing_table
;
581 prev_r
= r
, r
= r
->rt_next
) {
583 if ((r
->rt_timer
+= TIMER_INTERVAL
) < ROUTE_EXPIRE_TIME
) {
585 * Route is still good; see if any leaf timers need to be
588 if (r
->rt_flags
& RTF_LEAF_TIMING
) {
589 r
->rt_flags
&= ~RTF_LEAF_TIMING
;
590 for (vifi
= 0; vifi
< numvifs
; ++vifi
) {
591 if (r
->rt_leaf_timers
[vifi
] != 0) {
593 * Unlike other timers, leaf timers decrement.
595 if ((r
->rt_leaf_timers
[vifi
] -= TIMER_INTERVAL
) == 0){
597 /* If the vif is a physical leaf but has neighbors,
598 * it is not a tree leaf. If I am a leaf, then no
599 * interface with neighbors is a tree leaf. */
600 if (!(((uvifs
[vifi
].uv_flags
& VIFF_LEAF
) ||
601 (vifs_with_neighbors
== 1)) &&
602 (uvifs
[vifi
].uv_neighbors
!= NULL
))) {
604 VIFM_SET(vifi
, r
->rt_leaves
);
605 update_table_entry(r
);
611 r
->rt_flags
|= RTF_LEAF_TIMING
;
617 else if (r
->rt_timer
>= ROUTE_DISCARD_TIME
) {
619 * Time to garbage-collect the route entry.
621 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
622 discard_route(prev_r
);
625 else if (r
->rt_metric
!= UNREACHABLE
) {
627 * Time to expire the route entry. If the gateway is zero,
628 * i.e., it is a route to a directly-connected subnet, just
629 * set the timer back to zero; such routes expire only when
630 * the interface to the subnet goes down.
632 if (r
->rt_gateway
== 0) {
636 del_table_entry(r
, 0, DEL_ALL_ROUTES
);
637 r
->rt_metric
= UNREACHABLE
;
638 r
->rt_flags
|= RTF_CHANGED
;
639 routes_changed
= TRUE
;
647 * Mark all routes as unreachable. This function is called only from
648 * hup() in preparation for informing all neighbors that we are going
649 * off the air. For consistency, we ought also to delete all reachable
650 * route entries from the kernel, but since we are about to exit we rely
651 * on the kernel to do its own cleanup -- no point in making all those
652 * expensive kernel calls now.
655 expire_all_routes(void)
659 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
660 r
->rt_metric
= UNREACHABLE
;
661 r
->rt_flags
|= RTF_CHANGED
;
662 routes_changed
= TRUE
;
668 * Delete all the routes in the routing table.
671 free_all_routes(void)
683 * Process an incoming neighbor probe message.
686 accept_probe(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
,
691 if ((vifi
= find_vif(src
, dst
)) == NO_VIF
) {
693 "ignoring probe from non-neighbor %s", inet_fmt(src
));
697 update_neighbor(vifi
, src
, DVMRP_PROBE
, p
, datalen
, level
);
708 compare_rts(const void *rt1
, const void *rt2
)
710 const struct newrt
*r1
= (const struct newrt
*)rt1
;
711 const struct newrt
*r2
= (const struct newrt
*)rt2
;
712 u_int32_t m1
= ntohl(r1
->mask
);
713 u_int32_t m2
= ntohl(r2
->mask
);
721 /* masks are equal */
722 o1
= ntohl(r1
->origin
);
723 o2
= ntohl(r2
->origin
);
732 * Process an incoming route report message.
735 accept_report(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
,
739 int width
, i
, nrt
= 0;
743 struct newrt rt
[4096];
745 if ((vifi
= find_vif(src
, dst
)) == NO_VIF
) {
747 "ignoring route report from non-neighbor %s", inet_fmt(src
));
751 if (!update_neighbor(vifi
, src
, DVMRP_REPORT
, NULL
, 0, level
))
754 if (datalen
> 2*4096) {
756 "ignoring oversize (%d bytes) route report from %s",
757 datalen
, inet_fmt(src
));
761 while (datalen
> 0) { /* Loop through per-mask lists. */
764 logit(LOG_WARNING
, 0,
765 "received truncated route report from %s",
769 ((u_char
*)&mask
)[0] = 0xff; width
= 1;
770 if ((((u_char
*)&mask
)[1] = *p
++) != 0) width
= 2;
771 if ((((u_char
*)&mask
)[2] = *p
++) != 0) width
= 3;
772 if ((((u_char
*)&mask
)[3] = *p
++) != 0) width
= 4;
773 if (!inet_valid_mask(ntohl(mask
))) {
774 logit(LOG_WARNING
, 0,
775 "%s reports bogus netmask 0x%08x (%s)",
776 inet_fmt(src
), ntohl(mask
),
782 do { /* Loop through (origin, metric) pairs */
783 if (datalen
< width
+ 1) {
784 logit(LOG_WARNING
, 0,
785 "received truncated route report from %s",
790 for (i
= 0; i
< width
; ++i
)
791 ((char *)&origin
)[i
] = *p
++;
793 datalen
-= width
+ 1;
795 rt
[nrt
].origin
= origin
;
796 rt
[nrt
].metric
= (metric
& 0x7f);
798 } while (!(metric
& 0x80));
801 qsort((char*)rt
, nrt
, sizeof(rt
[0]), compare_rts
);
802 start_route_updates();
804 * If the last entry is default, change mask from 0xff000000 to 0
806 if (rt
[nrt
-1].origin
== 0)
809 logit(LOG_DEBUG
, 0, "Updating %d routes from %s to %s", nrt
,
810 inet_fmt(src
), inet_fmt(dst
));
811 for (i
= 0; i
< nrt
; ++i
) {
812 if (i
!= 0 && rt
[i
].origin
== rt
[i
-1].origin
&&
813 rt
[i
].mask
== rt
[i
-1].mask
) {
814 logit(LOG_WARNING
, 0, "%s reports duplicate route for %s",
816 inet_fmts(rt
[i
].origin
, rt
[i
].mask
));
819 update_route(rt
[i
].origin
, rt
[i
].mask
, rt
[i
].metric
,
823 if (routes_changed
&& !delay_change_reports
)
824 report_to_all_neighbors(CHANGED_ROUTES
);
829 * Send a route report message to destination 'dst', via virtual interface
830 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
833 report(int which_routes
, vifi_t vifi
, u_int32_t dst
)
844 src
= uvifs
[vifi
].uv_lcl_addr
;
846 p
= send_buf
+ MIN_IP_HEADER_LEN
+ IGMP_MINLEN
;
849 /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */
850 if ((vifs_with_neighbors
!= 1) && (uvifs
[vifi
].uv_flags
& VIFF_LEAF
)) {
851 *p
++ = 0; /* 0xff000000 mask */
854 *p
++ = 0; /* class A net 0.0.0.0 == default */
855 *p
++ = 0x81; /*XXX metric 1, is this safe? */
857 send_igmp(src
, dst
, IGMP_DVMRP
, DVMRP_REPORT
,
858 htonl(MROUTED_LEVEL
), datalen
);
863 nflags
= (uvifs
[vifi
].uv_flags
& VIFF_LEAF
) ? 0 : LEAF_FLAGS
;
865 for (r
= rt_end
; r
!= RT_ADDR
; r
= r
->rt_prev
) {
867 if (which_routes
== CHANGED_ROUTES
&& !(r
->rt_flags
& RTF_CHANGED
))
871 * If there is no room for this route in the current message,
872 * send the message and start a new one.
874 if (datalen
+ ((r
->rt_originmask
== mask
) ?
876 (r
->rt_originwidth
+ 4)) > MAX_DVMRP_DATA_LEN
) {
878 send_igmp(src
, dst
, IGMP_DVMRP
, DVMRP_REPORT
,
879 htonl(MROUTED_LEVEL
| nflags
), datalen
);
881 p
= send_buf
+ MIN_IP_HEADER_LEN
+ IGMP_MINLEN
;
886 if (r
->rt_originmask
!= mask
|| datalen
== 0) {
887 mask
= r
->rt_originmask
;
888 width
= r
->rt_originwidth
;
889 if (datalen
!= 0) *(p
-1) |= 0x80;
890 *p
++ = ((char *)&mask
)[1];
891 *p
++ = ((char *)&mask
)[2];
892 *p
++ = ((char *)&mask
)[3];
896 for (i
= 0; i
< width
; ++i
)
897 *p
++ = ((char *)&(r
->rt_origin
))[i
];
899 *p
++ = (r
->rt_parent
== vifi
&& r
->rt_metric
!= UNREACHABLE
) ?
900 (char)(r
->rt_metric
+ UNREACHABLE
) : /* "poisoned reverse" */
901 (char)(r
->rt_metric
);
903 datalen
+= width
+ 1;
908 send_igmp(src
, dst
, IGMP_DVMRP
, DVMRP_REPORT
,
909 htonl(MROUTED_LEVEL
| nflags
), datalen
);
915 * Send a route report message to all neighboring routers.
916 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
919 report_to_all_neighbors(int which_routes
)
924 int routes_changed_before
;
927 * Remember the state of the global routes_changed flag before
928 * generating the reports, and clear the flag.
930 routes_changed_before
= routes_changed
;
931 routes_changed
= FALSE
;
934 for (vifi
= 0, v
= uvifs
; vifi
< numvifs
; ++vifi
, ++v
) {
935 if (v
->uv_neighbors
!= NULL
) {
936 report(which_routes
, vifi
,
937 (v
->uv_flags
& VIFF_TUNNEL
) ? v
->uv_rmt_addr
943 * If there were changed routes before we sent the reports AND
944 * if no new changes occurred while sending the reports, clear
945 * the change flags in the individual route entries. If changes
946 * did occur while sending the reports, new reports will be
947 * generated at the next timer interrupt.
949 if (routes_changed_before
&& !routes_changed
) {
950 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
951 r
->rt_flags
&= ~RTF_CHANGED
;
956 * Set a flag to inhibit further reports of changed routes until the
957 * next timer interrupt. This is to alleviate update storms.
959 delay_change_reports
= TRUE
;
963 * Send a route report message to destination 'dst', via virtual interface
964 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
967 report_chunk(struct rtentry
*start_rt
, vifi_t vifi
, u_int32_t dst
)
979 src
= uvifs
[vifi
].uv_lcl_addr
;
980 p
= send_buf
+ MIN_IP_HEADER_LEN
+ IGMP_MINLEN
;
982 nflags
= (uvifs
[vifi
].uv_flags
& VIFF_LEAF
) ? 0 : LEAF_FLAGS
;
984 for (r
= start_rt
; r
!= RT_ADDR
; r
= r
->rt_prev
) {
987 /* Don't send poisoned routes back to parents if I am a leaf */
988 if ((vifs_with_neighbors
== 1) && (r
->rt_parent
== vifi
)
989 && (r
->rt_metric
> 1)) {
996 * If there is no room for this route in the current message,
997 * send it & return how many routes we sent.
999 if (datalen
+ ((r
->rt_originmask
== mask
) ?
1001 (r
->rt_originwidth
+ 4)) > MAX_DVMRP_DATA_LEN
) {
1003 send_igmp(src
, dst
, IGMP_DVMRP
, DVMRP_REPORT
,
1004 htonl(MROUTED_LEVEL
| nflags
), datalen
);
1007 if (r
->rt_originmask
!= mask
|| datalen
== 0) {
1008 mask
= r
->rt_originmask
;
1009 width
= r
->rt_originwidth
;
1010 if (datalen
!= 0) *(p
-1) |= 0x80;
1011 *p
++ = ((char *)&mask
)[1];
1012 *p
++ = ((char *)&mask
)[2];
1013 *p
++ = ((char *)&mask
)[3];
1016 for (i
= 0; i
< width
; ++i
)
1017 *p
++ = ((char *)&(r
->rt_origin
))[i
];
1019 *p
++ = (r
->rt_parent
== vifi
&& r
->rt_metric
!= UNREACHABLE
) ?
1020 (char)(r
->rt_metric
+ UNREACHABLE
) : /* "poisoned reverse" */
1021 (char)(r
->rt_metric
);
1023 datalen
+= width
+ 1;
1027 send_igmp(src
, dst
, IGMP_DVMRP
, DVMRP_REPORT
,
1028 htonl(MROUTED_LEVEL
| nflags
), datalen
);
1034 * send the next chunk of our routing table to all neighbors.
1035 * return the length of the smallest chunk we sent out.
1038 report_next_chunk(void)
1043 int i
, n
= 0, min
= 20000;
1044 static int start_rt
;
1050 * find this round's starting route.
1052 for (sr
= rt_end
, i
= start_rt
; --i
>= 0; ) {
1059 * send one chunk of routes starting at this round's start to
1060 * all our neighbors.
1062 for (vifi
= 0, v
= uvifs
; vifi
< numvifs
; ++vifi
, ++v
) {
1063 if ((v
->uv_neighbors
!= NULL
)
1065 && !(v
->uv_flags
& VIFF_LEAF
)
1068 n
= report_chunk(sr
, vifi
,
1069 (v
->uv_flags
& VIFF_TUNNEL
) ? v
->uv_rmt_addr
1076 min
= 0; /* Neighborless router didn't send any routes */
1079 logit(LOG_INFO
, 0, "update %d starting at %d of %d",
1080 n
, (nroutes
- start_rt
), nroutes
);
1082 start_rt
= (start_rt
+ n
) % nroutes
;
1088 * Print the contents of the routing table on file 'fp'.
1091 dump_routes(FILE *fp
)
1098 "Multicast Routing Table (%u %s)\n%s\n",
1099 nroutes
, (nroutes
== 1) ? "entry" : "entries",
1100 " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs");
1102 for (r
= routing_table
; r
!= NULL
; r
= r
->rt_next
) {
1104 fprintf(fp
, " %-18s %-15s ",
1105 inet_fmts(r
->rt_origin
, r
->rt_originmask
),
1106 (r
->rt_gateway
== 0) ? "" : inet_fmt(r
->rt_gateway
));
1108 fprintf(fp
, (r
->rt_metric
== UNREACHABLE
) ? " NR " : "%4u ",
1111 fprintf(fp
, " %3u %3u ", r
->rt_timer
, r
->rt_parent
);
1113 for (i
= 0; i
< numvifs
; ++i
) {
1114 if (VIFM_ISSET(i
, r
->rt_children
)) {
1115 fprintf(fp
, " %u%c",
1116 i
, VIFM_ISSET(i
, r
->rt_leaves
) ? '*' : ' ');
1125 determine_route(u_int32_t src
)
1129 for (rt
= routing_table
; rt
!= NULL
; rt
= rt
->rt_next
) {
1130 if (rt
->rt_origin
== (src
& rt
->rt_originmask
))