route.c revision 1.1 1 /*
2 * The mrouted program is covered by the license in the accompanying file
3 * named "LICENSE". Use of the mrouted program represents acceptance of
4 * the terms and conditions listed in that file.
5 *
6 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7 * Leland Stanford Junior University.
8 *
9 *
10 * from: Id: route.c,v 1.5 1993/06/24 05:11:16 deering Exp
11 * $Id: route.c,v 1.1 1994/01/11 20:16:02 brezak Exp $
12 */
13
14 #ifndef lint
15 static char rcsid[] = "$Id: route.c,v 1.1 1994/01/11 20:16:02 brezak Exp $";
16 #endif
17
18 #include "defs.h"
19
20
21 /*
22 * Exported variables.
23 */
24 int routes_changed; /* 1=>some routes have changed */
25 int delay_change_reports; /* 1=>postpone change reports */
26
27
28 /*
29 * Private variables.
30 */
31 static struct rtentry *routing_table; /* pointer to list of route entries */
32 static struct rtentry *rtp; /* pointer to a route entry */
33 static unsigned nroutes; /* current number of route entries */
34
35
36 /*
37 * Initialize the routing table and associated variables.
38 */
39 void init_routes()
40 {
41 routing_table = NULL;
42 nroutes = 0;
43 routes_changed = FALSE;
44 delay_change_reports = FALSE;
45 }
46
47
48 /*
49 * Initialize the children and leaf bits for route 'r', along with the
50 * associated dominant, subordinate, and leaf timing data structures.
51 * Return TRUE if this changes the value of either the children or
52 * leaf bitmaps for 'r'.
53 */
54 static int init_children_and_leaves(r, parent)
55 register struct rtentry *r;
56 register vifi_t parent;
57 {
58 register vifi_t vifi;
59 register struct uvif *v;
60 vifbitmap_t old_children, old_leaves;
61
62 VIFM_COPY(r->rt_children, old_children);
63 VIFM_COPY(r->rt_leaves, old_leaves );
64
65 VIFM_CLRALL(r->rt_children);
66 VIFM_CLRALL(r->rt_leaves);
67 r->rt_flags &= ~RTF_LEAF_TIMING;
68
69 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
70 r->rt_dominants [vifi] = 0;
71 r->rt_subordinates[vifi] = 0;
72
73 if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
74 VIFM_SET(vifi, r->rt_children);
75 if (v->uv_neighbors == NULL) {
76 VIFM_SET(vifi, r->rt_leaves);
77 r->rt_leaf_timers[vifi] = 0;
78 }
79 else {
80 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
81 r->rt_flags |= RTF_LEAF_TIMING;
82 }
83 }
84 else {
85 r->rt_leaf_timers[vifi] = 0;
86 }
87 }
88
89 return (!VIFM_SAME(r->rt_children, old_children) ||
90 !VIFM_SAME(r->rt_leaves, old_leaves));
91 }
92
93
94 /*
95 * A new vif has come up -- update the children and leaf bitmaps in all route
96 * entries to take that into account.
97 */
98 void add_vif_to_routes(vifi)
99 register vifi_t vifi;
100 {
101 register struct rtentry *r;
102 register struct uvif *v;
103
104 v = &uvifs[vifi];
105 for (r = routing_table; r != NULL; r = r->rt_next) {
106 if (r->rt_metric != UNREACHABLE &&
107 !VIFM_ISSET(vifi, r->rt_children)) {
108 VIFM_SET(vifi, r->rt_children);
109 r->rt_dominants [vifi] = 0;
110 r->rt_subordinates[vifi] = 0;
111 if (v->uv_neighbors == NULL) {
112 VIFM_SET(vifi, r->rt_leaves);
113 r->rt_leaf_timers[vifi] = 0;
114 }
115 else {
116 VIFM_CLR(vifi, r->rt_leaves);
117 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
118 r->rt_flags |= RTF_LEAF_TIMING;
119 }
120 k_update_route(r);
121 }
122 }
123 }
124
125
126 /*
127 * A vif has gone down -- expire all routes that have that vif as parent,
128 * and update the children bitmaps in all other route entries to take into
129 * account the failed vif.
130 */
131 void delete_vif_from_routes(vifi)
132 register vifi_t vifi;
133 {
134 register struct rtentry *r;
135
136 for (r = routing_table; r != NULL; r = r->rt_next) {
137 if (r->rt_metric != UNREACHABLE) {
138 if (vifi == r->rt_parent) {
139 k_del_route(r);
140 r->rt_timer = ROUTE_EXPIRE_TIME;
141 r->rt_metric = UNREACHABLE;
142 r->rt_flags |= RTF_CHANGED;
143 routes_changed = TRUE;
144 }
145 else if (VIFM_ISSET(vifi, r->rt_children)) {
146 VIFM_CLR(vifi, r->rt_children);
147 VIFM_CLR(vifi, r->rt_leaves);
148 r->rt_subordinates[vifi] = 0;
149 r->rt_leaf_timers [vifi] = 0;
150 k_update_route(r);
151 }
152 else {
153 r->rt_dominants[vifi] = 0;
154 }
155 }
156 }
157 }
158
159
160 /*
161 * A neighbor has failed or become unreachable. If that neighbor was
162 * considered a dominant or subordinate router in any route entries,
163 * take appropriate action.
164 */
165 void delete_neighbor_from_routes(addr, vifi)
166 register u_long addr;
167 register vifi_t vifi;
168 {
169 register struct rtentry *r;
170 register struct uvif *v;
171
172 v = &uvifs[vifi];
173 for (r = routing_table; r != NULL; r = r->rt_next) {
174 if (r->rt_metric != UNREACHABLE) {
175 if (r->rt_dominants[vifi] == addr) {
176 VIFM_SET(vifi, r->rt_children);
177 r->rt_dominants [vifi] = 0;
178 r->rt_subordinates[vifi] = 0;
179 if (v->uv_neighbors == NULL) {
180 VIFM_SET(vifi, r->rt_leaves);
181 r->rt_leaf_timers[vifi] = 0;
182 }
183 else {
184 VIFM_CLR(vifi, r->rt_leaves);
185 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
186 r->rt_flags |= RTF_LEAF_TIMING;
187 }
188 k_update_route(r);
189 }
190 else if (r->rt_subordinates[vifi] == addr) {
191 r->rt_subordinates[vifi] = 0;
192 if (v->uv_neighbors == NULL) {
193 VIFM_SET(vifi, r->rt_leaves);
194 k_update_route(r);
195 }
196 else {
197 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
198 r->rt_flags |= RTF_LEAF_TIMING;
199 }
200 }
201 else if (v->uv_neighbors == NULL &&
202 r->rt_leaf_timers[vifi] != 0) {
203 VIFM_SET(vifi, r->rt_leaves);
204 r->rt_leaf_timers[vifi] = 0;
205 k_update_route(r);
206 }
207 }
208 }
209 }
210
211
212 /*
213 * Prepare for a sequence of ordered route updates by initializing a pointer
214 * to the start of the routing table. The pointer is used to remember our
215 * position in the routing table in order to avoid searching from the
216 * beginning for each update; this relies on having the route reports in
217 * a single message be in the same order as the route entries in the routing
218 * table.
219 */
220 void start_route_updates()
221 {
222 rtp = (struct rtentry *)&routing_table;
223 }
224
225
226 /*
227 * Starting at the route entry following the one to which 'rtp' points,
228 * look for a route entry matching the specified origin and mask. If a
229 * match is found, return TRUE and leave 'rtp' pointing at the found entry.
230 * If no match is found, return FALSE and leave 'rtp' pointing to the route
231 * entry preceding the point at which the new origin should be inserted.
232 * This code is optimized for the normal case in which the first entry to
233 * be examined is the matching entry.
234 */
235 static int find_route(origin, mask)
236 register u_long origin, mask;
237 {
238 register struct rtentry *r;
239
240 r = rtp->rt_next;
241 while (r != NULL) {
242 if (origin == r->rt_origin && mask == r->rt_originmask) {
243 rtp = r;
244 return (TRUE);
245 }
246 if (ntohl(mask) > ntohl(r->rt_originmask) ||
247 (mask == r->rt_originmask &&
248 ntohl(origin) > ntohl(r->rt_origin))) {
249 rtp = r;
250 r = r->rt_next;
251 }
252 else break;
253 }
254 return (FALSE);
255 }
256
257
258 /*
259 * Search the entire routing table, looking for an entry which conflicts
260 * with the given origin and mask, for example, an entry which has the same
261 * origin under a different mask. If a conflicting entry is found, return
262 * a pointer to the entry preceding it (to facilitate deletion); if no
263 * conflict is found, return NULL.
264 */
265 static struct rtentry *find_conflicting_route(origin, mask)
266 register u_long origin, mask;
267 {
268 register struct rtentry *r, *prev_r;
269
270 for (prev_r = (struct rtentry *)&routing_table, r = routing_table;
271 r != NULL;
272 prev_r = r, r = r->rt_next ) {
273 if ((origin & r->rt_originmask) == r->rt_origin ||
274 (r->rt_origin & mask) == origin) {
275 return (prev_r);
276 }
277 }
278 return (NULL);
279 }
280
281
282 /*
283 * Create a new routing table entry for the specified origin and link it into
284 * the routing table. The shared variable 'rtp' is assumed to point to the
285 * routing entry after which the new one should be inserted. It is left
286 * pointing to the new entry.
287 *
288 * Only the origin, originmask, originwidth and flags fields are initialized
289 * in the new route entry; the caller is responsible for filling in the the
290 * rest.
291 */
292 static void create_route(origin, mask)
293 u_long origin, mask;
294 {
295 register struct rtentry *r;
296
297 if ((r = (struct rtentry *) malloc(sizeof(struct rtentry)
298 + (3 * numvifs * sizeof(u_long)))) == NULL) {
299 log(LOG_ERR, 0, "ran out of memory"); /* fatal */
300 }
301 r->rt_origin = origin;
302 r->rt_originmask = mask;
303 if (((char *)&mask)[3] != 0) r->rt_originwidth = 4;
304 else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3;
305 else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2;
306 else r->rt_originwidth = 1;
307 r->rt_flags = 0;
308 r->rt_dominants = (u_long *)(r + 1);
309 r->rt_subordinates = (u_long *)(r->rt_dominants + numvifs);
310 r->rt_leaf_timers = (u_long *)(r->rt_subordinates + numvifs);
311
312 r->rt_next = rtp->rt_next;
313 rtp->rt_next = r;
314 rtp = r;
315 ++nroutes;
316 }
317
318
319 /*
320 * Discard the routing table entry following the one to which 'prev_r' points.
321 */
322 static void discard_route(prev_r)
323 register struct rtentry *prev_r;
324 {
325 register struct rtentry *r;
326
327 r = prev_r->rt_next;
328 prev_r->rt_next = r->rt_next;
329 free((char *)r);
330 --nroutes;
331 }
332
333
334 /*
335 * Process a route report for a single origin, creating or updating the
336 * corresponding routing table entry if necessary. 'src' is either the
337 * address of a neighboring router from which the report arrived, or zero
338 * to indicate a change of status of one of our own interfaces.
339 */
340 void update_route(origin, mask, metric, src, vifi)
341 u_long origin, mask;
342 int metric;
343 u_long src;
344 vifi_t vifi;
345 {
346 register struct rtentry *r;
347 struct rtentry *prev_r;
348 int adj_metric;
349
350 /*
351 * Compute an adjusted metric, taking into account the cost of the
352 * subnet or tunnel over which the report arrived, and normalizing
353 * all unreachable/poisoned metrics into a single value.
354 */
355 if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
356 log(LOG_WARNING, 0,
357 "%s reports out-of-range metric %u for origin %s",
358 inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2));
359 return;
360 }
361 adj_metric = metric + uvifs[vifi].uv_metric;
362 if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
363
364 /*
365 * Look up the reported origin in the routing table.
366 */
367 if (!find_route(origin, mask)) {
368 /*
369 * Not found.
370 * Don't create a new entry if the report says it's unreachable,
371 * or if the reported origin and mask are invalid.
372 */
373 if (adj_metric == UNREACHABLE) {
374 return;
375 }
376 if (src != 0 && !inet_valid_subnet(origin, mask)) {
377 log(LOG_WARNING, 0,
378 "%s reports an invalid origin (%s) and/or mask (%08x)",
379 inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
380 return;
381 }
382
383 /*
384 * If the new origin and mask are inconsistent with an entry
385 * already in the routing table, either ignore this update
386 * (if it came from another router), or delete the conflicting
387 * entry (if the update is for a directly-connected subnet).
388 */
389 if ((prev_r = find_conflicting_route(origin, mask)) != NULL ) {
390 if (src != 0) {
391 log(LOG_WARNING, 0,
392 "%s reports a conflicting origin (%s) and mask (%08x)",
393 inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
394 return;
395 }
396 else {
397 r = prev_r->rt_next;
398 log(LOG_WARNING, 0,
399 "deleting route with conflicting origin (%s), mask (%08x)",
400 inet_fmt(r->rt_origin, s1), ntohl(r->rt_originmask));
401
402 if (r->rt_metric != UNREACHABLE) {
403 k_del_route(r);
404 }
405 discard_route(prev_r);
406 if (rtp == r) rtp = prev_r;
407 }
408 }
409
410 /*
411 * OK, create the new routing entry. 'rtp' will be left pointing
412 * to the new entry.
413 */
414 create_route(origin, mask);
415
416 rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
417 }
418
419 /*
420 * We now have a routing entry for the reported origin. Update it?
421 */
422 r = rtp;
423 if (r->rt_metric == UNREACHABLE) {
424 /*
425 * The routing entry is for a formerly-unreachable or new origin.
426 * If the report claims reachability, update the entry to use
427 * the reported route.
428 */
429 if (adj_metric == UNREACHABLE)
430 return;
431
432 r->rt_parent = vifi;
433 init_children_and_leaves(r, vifi);
434 k_add_route(r);
435 r->rt_gateway = src;
436 r->rt_timer = 0;
437 r->rt_metric = adj_metric;
438 r->rt_flags |= RTF_CHANGED;
439 routes_changed = TRUE;
440 }
441 else if (src == r->rt_gateway) {
442 /*
443 * The report has come either from the interface directly-connected
444 * to the origin subnet (src and r->rt_gateway both equal zero) or
445 * from the gateway we have chosen as the best first-hop gateway back
446 * towards the origin (src and r->rt_gateway not equal zero). Reset
447 * the route timer and, if the reported metric has changed, update
448 * our entry accordingly.
449 */
450 r->rt_timer = 0;
451 if (adj_metric == r->rt_metric)
452 return;
453
454 if (adj_metric == UNREACHABLE) {
455 k_del_route(r);
456 r->rt_timer = ROUTE_EXPIRE_TIME;
457 }
458 else if (adj_metric < r->rt_metric) {
459 if (init_children_and_leaves(r, vifi)) {
460 k_update_route(r);
461 }
462 }
463 r->rt_metric = adj_metric;
464 r->rt_flags |= RTF_CHANGED;
465 routes_changed = TRUE;
466 }
467 else if (src == 0 ||
468 (r->rt_gateway != 0 &&
469 (adj_metric < r->rt_metric ||
470 (adj_metric == r->rt_metric &&
471 r->rt_timer >= ROUTE_SWITCH_TIME)))) {
472 /*
473 * The report is for an origin we consider reachable; the report
474 * comes either from one of our own interfaces or from a gateway
475 * other than the one we have chosen as the best first-hop gateway
476 * back towards the origin. If the source of the update is one of
477 * our own interfaces, or if the origin is not a directly-connected
478 * subnet and the reported metric for that origin is better than
479 * what our routing entry says, update the entry to use the new
480 * gateway and metric. We also switch gateways if the reported
481 * metric is the same as the one in the route entry and the gateway
482 * associated with the route entry has not been heard from recently.
483 * Did you get all that?
484 */
485 if (r->rt_parent != vifi || adj_metric < r->rt_metric) {
486 r->rt_parent = vifi;
487 if (init_children_and_leaves(r, vifi)) {
488 k_update_route(r);
489 }
490 }
491 r->rt_gateway = src;
492 r->rt_timer = 0;
493 r->rt_metric = adj_metric;
494 r->rt_flags |= RTF_CHANGED;
495 routes_changed = TRUE;
496 }
497 else if (vifi != r->rt_parent) {
498 /*
499 * The report came from a vif other than the route's parent vif.
500 * Update the children and leaf info, if necessary.
501 */
502 if (VIFM_ISSET(vifi, r->rt_children)) {
503 /*
504 * Vif is a child vif for this route.
505 */
506 if (metric < r->rt_metric ||
507 (metric == r->rt_metric &&
508 ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
509 /*
510 * Neighbor has lower metric to origin (or has same metric
511 * and lower IP address) -- it becomes the dominant router,
512 * and vif is no longer a child for me.
513 */
514 VIFM_CLR(vifi, r->rt_children);
515 VIFM_CLR(vifi, r->rt_leaves);
516 r->rt_dominants [vifi] = src;
517 r->rt_subordinates[vifi] = 0;
518 r->rt_leaf_timers [vifi] = 0;
519 k_update_route(r);
520 }
521 else if (metric > UNREACHABLE) { /* "poisoned reverse" */
522 /*
523 * Neighbor considers this vif to be on path to route's
524 * origin; if no subordinate recorded, record this neighbor
525 * as subordinate and clear the leaf flag.
526 */
527 if (r->rt_subordinates[vifi] == 0) {
528 VIFM_CLR(vifi, r->rt_leaves);
529 r->rt_subordinates[vifi] = src;
530 r->rt_leaf_timers [vifi] = 0;
531 k_update_route(r);
532 }
533 }
534 else if (src == r->rt_subordinates[vifi]) {
535 /*
536 * Current subordinate no longer considers this vif to be on
537 * path to route's origin; it is no longer a subordinate
538 * router, and we set the leaf confirmation timer to give
539 * us time to hear from other subordinates.
540 */
541 r->rt_subordinates[vifi] = 0;
542 if (uvifs[vifi].uv_neighbors == NULL ||
543 uvifs[vifi].uv_neighbors->al_next == NULL) {
544 VIFM_SET(vifi, r->rt_leaves);
545 k_update_route(r);
546 }
547 else {
548 r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME;
549 r->rt_flags |= RTF_LEAF_TIMING;
550 }
551 }
552
553 }
554 else if (src == r->rt_dominants[vifi] &&
555 (metric > r->rt_metric ||
556 (metric == r->rt_metric &&
557 ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
558 /*
559 * Current dominant no longer has a lower metric to origin
560 * (or same metric and lower IP address); we adopt the vif
561 * as our own child.
562 */
563 VIFM_SET(vifi, r->rt_children);
564 r->rt_dominants [vifi] = 0;
565 if (metric > UNREACHABLE) {
566 r->rt_subordinates[vifi] = src;
567 }
568 else if (uvifs[vifi].uv_neighbors == NULL ||
569 uvifs[vifi].uv_neighbors->al_next == NULL) {
570 VIFM_SET(vifi, r->rt_leaves);
571 }
572 else {
573 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
574 r->rt_flags |= RTF_LEAF_TIMING;
575 }
576 k_update_route(r);
577 }
578 }
579 }
580
581
582 /*
583 * On every timer interrupt, advance the timer in each routing entry.
584 */
585 void age_routes()
586 {
587 register struct rtentry *r;
588 register struct rtentry *prev_r;
589 register vifi_t vifi;
590
591 for (prev_r = (struct rtentry *)&routing_table, r = routing_table;
592 r != NULL;
593 prev_r = r, r = r->rt_next) {
594
595 if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) {
596 /*
597 * Route is still good; see if any leaf timers need to be
598 * advanced.
599 */
600 if (r->rt_flags & RTF_LEAF_TIMING) {
601 r->rt_flags &= ~RTF_LEAF_TIMING;
602 for (vifi = 0; vifi < numvifs; ++vifi) {
603 if (r->rt_leaf_timers[vifi] != 0) {
604 /*
605 * Unlike other timers, leaf timers decrement.
606 */
607 if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){
608 VIFM_SET(vifi, r->rt_leaves);
609 k_update_route(r);
610 }
611 else {
612 r->rt_flags |= RTF_LEAF_TIMING;
613 }
614 }
615 }
616 }
617 }
618 else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
619 /*
620 * Time to garbage-collect the route entry.
621 */
622 discard_route(prev_r);
623 r = prev_r;
624 }
625 else if (r->rt_metric != UNREACHABLE) {
626 /*
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.
631 */
632 if (r->rt_gateway == 0) {
633 r->rt_timer = 0;
634 }
635 else {
636 k_del_route(r);
637 r->rt_metric = UNREACHABLE;
638 r->rt_flags |= RTF_CHANGED;
639 routes_changed = TRUE;
640 }
641 }
642 }
643 }
644
645
646 /*
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.
653 */
654 void expire_all_routes()
655 {
656 register struct rtentry *r;
657
658 for (r = routing_table; r != NULL; r = r->rt_next) {
659 r->rt_metric = UNREACHABLE;
660 r->rt_flags |= RTF_CHANGED;
661 routes_changed = TRUE;
662 }
663 }
664
665
666 /*
667 * Process an incoming neighbor probe message.
668 */
669 void accept_probe(src, dst)
670 u_long src, dst;
671 {
672 vifi_t vifi;
673
674 if ((vifi = find_vif(src, dst)) == NO_VIF) {
675 log(LOG_INFO, 0,
676 "ignoring probe from non-neighbor %s", inet_fmt(src, s1));
677 return;
678 }
679
680 if (!update_neighbor(vifi, src, DVMRP_PROBE))
681 return;
682
683 report(ALL_ROUTES, vifi, src);
684 }
685
686
687 /*
688 * Process an incoming route report message.
689 */
690 void accept_report(src, dst, p, datalen)
691 u_long src, dst;
692 register char *p;
693 register int datalen;
694 {
695 vifi_t vifi;
696 register int width, i;
697 int metric;
698 u_long mask;
699 u_long origin;
700
701 if ((vifi = find_vif(src, dst)) == NO_VIF) {
702 log(LOG_INFO, 0,
703 "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
704 return;
705 }
706
707 if (!update_neighbor(vifi, src, DVMRP_REPORT))
708 return;
709
710 start_route_updates();
711
712 while (datalen > 0) { /* Loop through per-mask lists. */
713
714 if (datalen < 3) {
715 log(LOG_WARNING, 0,
716 "received truncated route report from %s", inet_fmt(src, s1));
717 return;
718 }
719 ((char *)&mask)[0] = 0xff; width = 1;
720 if ((((char *)&mask)[1] = *p++) != 0) width = 2;
721 if ((((char *)&mask)[2] = *p++) != 0) width = 3;
722 if ((((char *)&mask)[3] = *p++) != 0) width = 4;
723 datalen -= 3;
724
725 do { /* Loop through (origin, metric) pairs */
726
727 if (datalen < width + 1) {
728 log(LOG_WARNING, 0,
729 "received truncated route report from %s", inet_fmt(src, s1));
730 return;
731 }
732 origin = 0;
733 for (i = 0; i < width; ++i)
734 ((char *)&origin)[i] = *p++;
735 metric = *p++;
736 datalen -= width + 1;
737
738 update_route(origin, mask, (metric & 0x7f), src, vifi);
739
740 } while (!(metric & 0x80));
741 }
742
743 if (routes_changed && !delay_change_reports)
744 report_to_all_neighbors(CHANGED_ROUTES);
745 }
746
747
748 /*
749 * Send a route report message to destination 'dst', via virtual interface
750 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
751 */
752 void report(which_routes, vifi, dst)
753 int which_routes;
754 vifi_t vifi;
755 u_long dst;
756 {
757 register struct rtentry *r;
758 register char *p;
759 register int i;
760 int datalen;
761 int width;
762 u_long mask;
763 u_long src;
764
765 src = uvifs[vifi].uv_lcl_addr;
766
767 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
768 datalen = 0;
769 mask = 0;
770
771 for (r = routing_table; r != NULL; r = r->rt_next) {
772
773 if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
774 continue;
775
776 /*
777 * If there is no room for this route in the current message,
778 * send the message and start a new one.
779 */
780 if (datalen + ((r->rt_originmask == mask) ?
781 (width + 1) :
782 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
783 *(p-1) |= 0x80;
784 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
785 htonl(MROUTED_LEVEL), datalen);
786
787 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
788 datalen = 0;
789 mask = 0;
790 }
791
792 if(r->rt_originmask != mask) {
793 mask = r->rt_originmask;
794 width = r->rt_originwidth;
795 if (datalen != 0) *(p-1) |= 0x80;
796 *p++ = ((char *)&mask)[1];
797 *p++ = ((char *)&mask)[2];
798 *p++ = ((char *)&mask)[3];
799 datalen += 3;
800 }
801
802 for (i = 0; i < width; ++i)
803 *p++ = ((char *)&(r->rt_origin))[i];
804
805 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
806 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
807 (char)(r->rt_metric);
808
809 datalen += width + 1;
810 }
811
812 if (datalen != 0) {
813 *(p-1) |= 0x80;
814 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
815 htonl(MROUTED_LEVEL), datalen);
816 }
817 }
818
819
820 /*
821 * Send a route report message to all neighboring routers.
822 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
823 */
824 void report_to_all_neighbors(which_routes)
825 int which_routes;
826 {
827 register vifi_t vifi;
828 register struct uvif *v;
829 register struct rtentry *r;
830 int routes_changed_before;
831
832 /*
833 * Remember the state of the global routes_changed flag before
834 * generating the reports, and clear the flag.
835 */
836 routes_changed_before = routes_changed;
837 routes_changed = FALSE;
838
839
840 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
841 if (v->uv_neighbors != NULL) {
842 report(which_routes, vifi,
843 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
844 : dvmrp_group);
845 }
846 }
847
848 /*
849 * If there were changed routes before we sent the reports AND
850 * if no new changes occurred while sending the reports, clear
851 * the change flags in the individual route entries. If changes
852 * did occur while sending the reports, new reports will be
853 * generated at the next timer interrupt.
854 */
855 if (routes_changed_before && !routes_changed) {
856 for (r = routing_table; r != NULL; r = r->rt_next) {
857 r->rt_flags &= ~RTF_CHANGED;
858 }
859 }
860
861 /*
862 * Set a flag to inhibit further reports of changed routes until the
863 * next timer interrupt. This is to alleviate update storms.
864 */
865 delay_change_reports = TRUE;
866 }
867
868
869 /*
870 * Print the contents of the routing table on file 'fp'.
871 */
872 void dump_routes(fp)
873 FILE *fp;
874 {
875 register struct rtentry *r;
876 register int i;
877
878 fprintf(fp,
879 "Multicast Routing Table (%u %s)\n%s",
880 nroutes, (nroutes == 1) ? "entry" : "entries",
881 " Origin-Subnet From-Gateway Metric In-Vif Out-Vifs\n");
882
883 for (r = routing_table; r != NULL; r = r->rt_next) {
884
885 fprintf(fp, " %-15s %-15s ",
886 inet_fmts(r->rt_origin, r->rt_originmask, s1),
887 (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
888
889 fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
890 r->rt_metric);
891
892 fprintf(fp, "%7u ",
893 r->rt_parent);
894
895 for (i = 0; i < numvifs; ++i) {
896 if (VIFM_ISSET(i, r->rt_children)) {
897 fprintf(fp, " %u%c",
898 i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
899 }
900 }
901 fprintf(fp, "\n");
902 }
903 fprintf(fp, "\n");
904 }
905