route.c revision 1.3 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 * $Id: route.c,v 1.3 1995/06/01 02:26:04 mycroft Exp $
11 */
12
13
14 #include "defs.h"
15
16
17 /*
18 * This define statement saves a lot of space later
19 */
20 #define RT_ADDR (struct rtentry *)&routing_table
21
22 /*
23 * Exported variables.
24 */
25 int routes_changed; /* 1=>some routes have changed */
26 int delay_change_reports; /* 1=>postpone change reports */
27
28
29 /*
30 * The routing table is shared with prune.c , so must not be static.
31 */
32 struct rtentry *routing_table; /* pointer to list of route entries */
33
34 /*
35 * Private variables.
36 */
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 */
40
41 /*
42 * Initialize the routing table and associated variables.
43 */
44 void
45 init_routes()
46 {
47 routing_table = NULL;
48 nroutes = 0;
49 routes_changed = FALSE;
50 delay_change_reports = FALSE;
51 }
52
53
54 /*
55 * Initialize the children and leaf bits for route 'r', along with the
56 * associated dominant, subordinate, and leaf timing data structures.
57 * Return TRUE if this changes the value of either the children or
58 * leaf bitmaps for 'r'.
59 */
60 static int
61 init_children_and_leaves(r, parent)
62 register struct rtentry *r;
63 register vifi_t parent;
64 {
65 register vifi_t vifi;
66 register struct uvif *v;
67 vifbitmap_t old_children, old_leaves;
68
69 VIFM_COPY(r->rt_children, old_children);
70 VIFM_COPY(r->rt_leaves, old_leaves );
71
72 VIFM_CLRALL(r->rt_children);
73 VIFM_CLRALL(r->rt_leaves);
74 r->rt_flags &= ~RTF_LEAF_TIMING;
75
76 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
77 r->rt_dominants [vifi] = 0;
78 r->rt_subordinates[vifi] = 0;
79
80 if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
81 VIFM_SET(vifi, r->rt_children);
82 if (v->uv_neighbors == NULL) {
83 VIFM_SET(vifi, r->rt_leaves);
84 r->rt_leaf_timers[vifi] = 0;
85 }
86 else {
87 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
88 r->rt_flags |= RTF_LEAF_TIMING;
89 }
90 }
91 else {
92 r->rt_leaf_timers[vifi] = 0;
93 }
94 }
95
96 return (!VIFM_SAME(r->rt_children, old_children) ||
97 !VIFM_SAME(r->rt_leaves, old_leaves));
98 }
99
100
101 /*
102 * A new vif has come up -- update the children and leaf bitmaps in all route
103 * entries to take that into account.
104 */
105 void
106 add_vif_to_routes(vifi)
107 register vifi_t vifi;
108 {
109 register struct rtentry *r;
110 register struct uvif *v;
111
112 v = &uvifs[vifi];
113 for (r = routing_table; r != NULL; r = r->rt_next) {
114 if (r->rt_metric != UNREACHABLE &&
115 !VIFM_ISSET(vifi, r->rt_children)) {
116 VIFM_SET(vifi, r->rt_children);
117 r->rt_dominants [vifi] = 0;
118 r->rt_subordinates[vifi] = 0;
119 if (v->uv_neighbors == NULL) {
120 VIFM_SET(vifi, r->rt_leaves);
121 r->rt_leaf_timers[vifi] = 0;
122 }
123 else {
124 VIFM_CLR(vifi, r->rt_leaves);
125 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
126 r->rt_flags |= RTF_LEAF_TIMING;
127 }
128 update_table_entry(r);
129 }
130 }
131 }
132
133
134 /*
135 * A vif has gone down -- expire all routes that have that vif as parent,
136 * and update the children bitmaps in all other route entries to take into
137 * account the failed vif.
138 */
139 void
140 delete_vif_from_routes(vifi)
141 register vifi_t vifi;
142 {
143 register struct rtentry *r;
144
145 for (r = routing_table; r != NULL; r = r->rt_next) {
146 if (r->rt_metric != UNREACHABLE) {
147 if (vifi == r->rt_parent) {
148 del_table_entry(r, 0, DEL_ALL_ROUTES);
149 r->rt_timer = ROUTE_EXPIRE_TIME;
150 r->rt_metric = UNREACHABLE;
151 r->rt_flags |= RTF_CHANGED;
152 routes_changed = TRUE;
153 }
154 else if (VIFM_ISSET(vifi, r->rt_children)) {
155 VIFM_CLR(vifi, r->rt_children);
156 VIFM_CLR(vifi, r->rt_leaves);
157 r->rt_subordinates[vifi] = 0;
158 r->rt_leaf_timers [vifi] = 0;
159 update_table_entry(r);
160 }
161 else {
162 r->rt_dominants[vifi] = 0;
163 }
164 }
165 }
166 }
167
168
169 /*
170 * A neighbor has failed or become unreachable. If that neighbor was
171 * considered a dominant or subordinate router in any route entries,
172 * take appropriate action.
173 */
174 void
175 delete_neighbor_from_routes(addr, vifi)
176 register u_int32_t addr;
177 register vifi_t vifi;
178 {
179 register struct rtentry *r;
180 register struct uvif *v;
181
182 v = &uvifs[vifi];
183 for (r = routing_table; r != NULL; r = r->rt_next) {
184 if (r->rt_metric != UNREACHABLE) {
185 if (r->rt_dominants[vifi] == addr) {
186 VIFM_SET(vifi, r->rt_children);
187 r->rt_dominants [vifi] = 0;
188 r->rt_subordinates[vifi] = 0;
189 if (v->uv_neighbors == NULL) {
190 VIFM_SET(vifi, r->rt_leaves);
191 r->rt_leaf_timers[vifi] = 0;
192 }
193 else {
194 VIFM_CLR(vifi, r->rt_leaves);
195 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
196 r->rt_flags |= RTF_LEAF_TIMING;
197 }
198 update_table_entry(r);
199 }
200 else if (r->rt_subordinates[vifi] == addr) {
201 r->rt_subordinates[vifi] = 0;
202 if (v->uv_neighbors == NULL) {
203 VIFM_SET(vifi, r->rt_leaves);
204 update_table_entry(r);
205 }
206 else {
207 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
208 r->rt_flags |= RTF_LEAF_TIMING;
209 }
210 }
211 else if (v->uv_neighbors == NULL &&
212 r->rt_leaf_timers[vifi] != 0) {
213 VIFM_SET(vifi, r->rt_leaves);
214 r->rt_leaf_timers[vifi] = 0;
215 update_table_entry(r);
216 }
217 }
218 }
219 }
220
221
222 /*
223 * Prepare for a sequence of ordered route updates by initializing a pointer
224 * to the start of the routing table. The pointer is used to remember our
225 * position in the routing table in order to avoid searching from the
226 * beginning for each update; this relies on having the route reports in
227 * a single message be in the same order as the route entries in the routing
228 * table.
229 */
230 void
231 start_route_updates()
232 {
233 rtp = RT_ADDR;
234 }
235
236
237 /*
238 * Starting at the route entry following the one to which 'rtp' points,
239 * look for a route entry matching the specified origin and mask. If a
240 * match is found, return TRUE and leave 'rtp' pointing at the found entry.
241 * If no match is found, return FALSE and leave 'rtp' pointing to the route
242 * entry preceding the point at which the new origin should be inserted.
243 * This code is optimized for the normal case in which the first entry to
244 * be examined is the matching entry.
245 */
246 static int
247 find_route(origin, mask)
248 register u_int32_t origin, mask;
249 {
250 register struct rtentry *r;
251
252 r = rtp->rt_next;
253 while (r != NULL) {
254 if (origin == r->rt_origin && mask == r->rt_originmask) {
255 rtp = r;
256 return (TRUE);
257 }
258 if (ntohl(mask) < ntohl(r->rt_originmask) ||
259 (mask == r->rt_originmask &&
260 ntohl(origin) < ntohl(r->rt_origin))) {
261 rtp = r;
262 r = r->rt_next;
263 }
264 else break;
265 }
266 return (FALSE);
267 }
268
269 /*
270 * Create a new routing table entry for the specified origin and link it into
271 * the routing table. The shared variable 'rtp' is assumed to point to the
272 * routing entry after which the new one should be inserted. It is left
273 * pointing to the new entry.
274 *
275 * Only the origin, originmask, originwidth and flags fields are initialized
276 * in the new route entry; the caller is responsible for filling in the the
277 * rest.
278 */
279 static void
280 create_route(origin, mask)
281 u_int32_t origin, mask;
282 {
283 register struct rtentry *r;
284
285 if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) +
286 (2 * numvifs * sizeof(u_int32_t)) +
287 (numvifs * sizeof(u_long)))) == NULL) {
288 log(LOG_ERR, 0, "ran out of memory"); /* fatal */
289 }
290 r->rt_origin = origin;
291 r->rt_originmask = mask;
292 if (((char *)&mask)[3] != 0) r->rt_originwidth = 4;
293 else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3;
294 else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2;
295 else r->rt_originwidth = 1;
296 r->rt_flags = 0;
297 r->rt_dominants = (u_int32_t *)(r + 1);
298 r->rt_subordinates = (u_int32_t *)(r->rt_dominants + numvifs);
299 r->rt_leaf_timers = (u_long *)(r->rt_subordinates + numvifs);
300 r->rt_groups = NULL;
301
302 r->rt_next = rtp->rt_next;
303 rtp->rt_next = r;
304 r->rt_prev = rtp;
305 if (r->rt_next != NULL)
306 (r->rt_next)->rt_prev = r;
307 else
308 rt_end = r;
309 rtp = r;
310 ++nroutes;
311 }
312
313
314 /*
315 * Discard the routing table entry following the one to which 'prev_r' points.
316 */
317 static void
318 discard_route(prev_r)
319 register struct rtentry *prev_r;
320 {
321 register struct rtentry *r;
322
323 r = prev_r->rt_next;
324 prev_r->rt_next = r->rt_next;
325 if (prev_r->rt_next != NULL)
326 (prev_r->rt_next)->rt_prev = prev_r;
327 else
328 rt_end = prev_r;
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
341 update_route(origin, mask, metric, src, vifi)
342 u_int32_t origin, mask;
343 int metric;
344 u_int32_t src;
345 vifi_t vifi;
346 {
347 register struct rtentry *r;
348 struct rtentry *prev_r;
349 int adj_metric;
350
351 /*
352 * Compute an adjusted metric, taking into account the cost of the
353 * subnet or tunnel over which the report arrived, and normalizing
354 * all unreachable/poisoned metrics into a single value.
355 */
356 if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
357 log(LOG_WARNING, 0,
358 "%s reports out-of-range metric %u for origin %s",
359 inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2));
360 return;
361 }
362 adj_metric = metric + uvifs[vifi].uv_metric;
363 if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
364
365 /*
366 * Look up the reported origin in the routing table.
367 */
368 if (!find_route(origin, mask)) {
369 register struct rtentry *rp;
370 register struct gtable *gt;
371 register struct stable *st, **stnp;
372
373 /*
374 * Not found.
375 * Don't create a new entry if the report says it's unreachable,
376 * or if the reported origin and mask are invalid.
377 */
378 if (adj_metric == UNREACHABLE) {
379 return;
380 }
381 if (src != 0 && !inet_valid_subnet(origin, mask)) {
382 log(LOG_WARNING, 0,
383 "%s reports an invalid origin (%s) and/or mask (%08x)",
384 inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
385 return;
386 }
387
388 /*
389 * OK, create the new routing entry. 'rtp' will be left pointing
390 * to the new entry.
391 */
392 create_route(origin, mask);
393
394 /*
395 * Now "steal away" any sources that belong under this route
396 * by deleting any cache entries they might have created
397 * and allowing the kernel to re-request them.
398 */
399 steal_sources(rtp);
400
401 rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
402 }
403
404 /*
405 * We now have a routing entry for the reported origin. Update it?
406 */
407 r = rtp;
408 if (r->rt_metric == UNREACHABLE) {
409 /*
410 * The routing entry is for a formerly-unreachable or new origin.
411 * If the report claims reachability, update the entry to use
412 * the reported route.
413 */
414 if (adj_metric == UNREACHABLE)
415 return;
416
417 r->rt_parent = vifi;
418 init_children_and_leaves(r, vifi);
419
420 r->rt_gateway = src;
421 r->rt_timer = 0;
422 r->rt_metric = adj_metric;
423 r->rt_flags |= RTF_CHANGED;
424 routes_changed = TRUE;
425 update_table_entry(r);
426 }
427 else if (src == r->rt_gateway) {
428 /*
429 * The report has come either from the interface directly-connected
430 * to the origin subnet (src and r->rt_gateway both equal zero) or
431 * from the gateway we have chosen as the best first-hop gateway back
432 * towards the origin (src and r->rt_gateway not equal zero). Reset
433 * the route timer and, if the reported metric has changed, update
434 * our entry accordingly.
435 */
436 r->rt_timer = 0;
437 if (adj_metric == r->rt_metric)
438 return;
439
440 if (adj_metric == UNREACHABLE) {
441 del_table_entry(r, 0, DEL_ALL_ROUTES);
442 r->rt_timer = ROUTE_EXPIRE_TIME;
443 }
444 else if (adj_metric < r->rt_metric) {
445 if (init_children_and_leaves(r, vifi)) {
446 update_table_entry(r);
447 }
448 }
449 r->rt_metric = adj_metric;
450 r->rt_flags |= RTF_CHANGED;
451 routes_changed = TRUE;
452 }
453 else if (src == 0 ||
454 (r->rt_gateway != 0 &&
455 (adj_metric < r->rt_metric ||
456 (adj_metric == r->rt_metric &&
457 r->rt_timer >= ROUTE_SWITCH_TIME)))) {
458 /*
459 * The report is for an origin we consider reachable; the report
460 * comes either from one of our own interfaces or from a gateway
461 * other than the one we have chosen as the best first-hop gateway
462 * back towards the origin. If the source of the update is one of
463 * our own interfaces, or if the origin is not a directly-connected
464 * subnet and the reported metric for that origin is better than
465 * what our routing entry says, update the entry to use the new
466 * gateway and metric. We also switch gateways if the reported
467 * metric is the same as the one in the route entry and the gateway
468 * associated with the route entry has not been heard from recently.
469 * Did you get all that?
470 */
471 if (r->rt_parent != vifi || adj_metric < r->rt_metric) {
472 r->rt_parent = vifi;
473 if (init_children_and_leaves(r, vifi)) {
474 update_table_entry(r);
475 }
476 }
477 r->rt_gateway = src;
478 r->rt_timer = 0;
479 r->rt_metric = adj_metric;
480 r->rt_flags |= RTF_CHANGED;
481 routes_changed = TRUE;
482 }
483 else if (vifi != r->rt_parent) {
484 /*
485 * The report came from a vif other than the route's parent vif.
486 * Update the children and leaf info, if necessary.
487 */
488 if (VIFM_ISSET(vifi, r->rt_children)) {
489 /*
490 * Vif is a child vif for this route.
491 */
492 if (metric < r->rt_metric ||
493 (metric == r->rt_metric &&
494 ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
495 /*
496 * Neighbor has lower metric to origin (or has same metric
497 * and lower IP address) -- it becomes the dominant router,
498 * and vif is no longer a child for me.
499 */
500 VIFM_CLR(vifi, r->rt_children);
501 VIFM_CLR(vifi, r->rt_leaves);
502 r->rt_dominants [vifi] = src;
503 r->rt_subordinates[vifi] = 0;
504 r->rt_leaf_timers [vifi] = 0;
505 update_table_entry(r);
506 }
507 else if (metric > UNREACHABLE) { /* "poisoned reverse" */
508 /*
509 * Neighbor considers this vif to be on path to route's
510 * origin; if no subordinate recorded, record this neighbor
511 * as subordinate and clear the leaf flag.
512 */
513 if (r->rt_subordinates[vifi] == 0) {
514 VIFM_CLR(vifi, r->rt_leaves);
515 r->rt_subordinates[vifi] = src;
516 r->rt_leaf_timers [vifi] = 0;
517 update_table_entry(r);
518 }
519 }
520 else if (src == r->rt_subordinates[vifi]) {
521 /*
522 * Current subordinate no longer considers this vif to be on
523 * path to route's origin; it is no longer a subordinate
524 * router, and we set the leaf confirmation timer to give
525 * us time to hear from other subordinates.
526 */
527 r->rt_subordinates[vifi] = 0;
528 if (uvifs[vifi].uv_neighbors == NULL ||
529 uvifs[vifi].uv_neighbors->al_next == NULL) {
530 VIFM_SET(vifi, r->rt_leaves);
531 update_table_entry(r);
532 }
533 else {
534 r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME;
535 r->rt_flags |= RTF_LEAF_TIMING;
536 }
537 }
538
539 }
540 else if (src == r->rt_dominants[vifi] &&
541 (metric > r->rt_metric ||
542 (metric == r->rt_metric &&
543 ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
544 /*
545 * Current dominant no longer has a lower metric to origin
546 * (or same metric and lower IP address); we adopt the vif
547 * as our own child.
548 */
549 VIFM_SET(vifi, r->rt_children);
550 r->rt_dominants [vifi] = 0;
551 if (metric > UNREACHABLE) {
552 r->rt_subordinates[vifi] = src;
553 }
554 else if (uvifs[vifi].uv_neighbors == NULL ||
555 uvifs[vifi].uv_neighbors->al_next == NULL) {
556 VIFM_SET(vifi, r->rt_leaves);
557 }
558 else {
559 r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
560 r->rt_flags |= RTF_LEAF_TIMING;
561 }
562 update_table_entry(r);
563 }
564 }
565 }
566
567
568 /*
569 * On every timer interrupt, advance the timer in each routing entry.
570 */
571 void
572 age_routes()
573 {
574 register struct rtentry *r;
575 register struct rtentry *prev_r;
576 register vifi_t vifi;
577
578 for (prev_r = RT_ADDR, r = routing_table;
579 r != NULL;
580 prev_r = r, r = r->rt_next) {
581
582 if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) {
583 /*
584 * Route is still good; see if any leaf timers need to be
585 * advanced.
586 */
587 if (r->rt_flags & RTF_LEAF_TIMING) {
588 r->rt_flags &= ~RTF_LEAF_TIMING;
589 for (vifi = 0; vifi < numvifs; ++vifi) {
590 if (r->rt_leaf_timers[vifi] != 0) {
591 /*
592 * Unlike other timers, leaf timers decrement.
593 */
594 if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){
595 #ifdef NOTYET
596 /* If the vif is a physical leaf but has neighbors,
597 * it is not a tree leaf. If I am a leaf, then no
598 * interface with neighbors is a tree leaf. */
599 if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) ||
600 (vifs_with_neighbors == 1)) &&
601 (uvifs[vifi].uv_neighbors != NULL))) {
602 #endif
603 VIFM_SET(vifi, r->rt_leaves);
604 update_table_entry(r);
605 #ifdef NOTYET
606 }
607 #endif
608 }
609 else {
610 r->rt_flags |= RTF_LEAF_TIMING;
611 }
612 }
613 }
614 }
615 }
616 else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
617 /*
618 * Time to garbage-collect the route entry.
619 */
620 del_table_entry(r, 0, DEL_ALL_ROUTES);
621 discard_route(prev_r);
622 r = prev_r;
623 }
624 else if (r->rt_metric != UNREACHABLE) {
625 /*
626 * Time to expire the route entry. If the gateway is zero,
627 * i.e., it is a route to a directly-connected subnet, just
628 * set the timer back to zero; such routes expire only when
629 * the interface to the subnet goes down.
630 */
631 if (r->rt_gateway == 0) {
632 r->rt_timer = 0;
633 }
634 else {
635 del_table_entry(r, 0, DEL_ALL_ROUTES);
636 r->rt_metric = UNREACHABLE;
637 r->rt_flags |= RTF_CHANGED;
638 routes_changed = TRUE;
639 }
640 }
641 }
642 }
643
644
645 /*
646 * Mark all routes as unreachable. This function is called only from
647 * hup() in preparation for informing all neighbors that we are going
648 * off the air. For consistency, we ought also to delete all reachable
649 * route entries from the kernel, but since we are about to exit we rely
650 * on the kernel to do its own cleanup -- no point in making all those
651 * expensive kernel calls now.
652 */
653 void
654 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 * Delete all the routes in the routing table.
668 */
669 void
670 free_all_routes()
671 {
672 register struct rtentry *r;
673
674 r = RT_ADDR;
675
676 while (r->rt_next)
677 discard_route(r);
678 }
679
680
681 /*
682 * Process an incoming neighbor probe message.
683 */
684 void
685 accept_probe(src, dst, p, datalen, level)
686 u_int32_t src;
687 u_int32_t dst;
688 char *p;
689 int datalen;
690 u_int32_t level;
691 {
692 vifi_t vifi;
693
694 if ((vifi = find_vif(src, dst)) == NO_VIF) {
695 log(LOG_INFO, 0,
696 "ignoring probe from non-neighbor %s", inet_fmt(src, s1));
697 return;
698 }
699
700 if (!update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level))
701 return;
702
703 report(ALL_ROUTES, vifi, src);
704 }
705
706 struct newrt {
707 u_int32_t mask;
708 u_int32_t origin;
709 int metric;
710 int pad;
711 };
712
713 int
714 compare_rts(r1, r2)
715 register struct newrt *r1;
716 register struct newrt *r2;
717 {
718 register u_int32_t m1 = ntohl(r1->mask);
719 register u_int32_t m2 = ntohl(r2->mask);
720 register u_int32_t o1, o2;
721
722 if (m1 > m2)
723 return (-1);
724 if (m1 < m2)
725 return (1);
726
727 /* masks are equal */
728 o1 = ntohl(r1->origin);
729 o2 = ntohl(r2->origin);
730 if (o1 > o2)
731 return (-1);
732 if (o1 < o2)
733 return (1);
734 return (0);
735 }
736
737 /*
738 * Process an incoming route report message.
739 */
740 void
741 accept_report(src, dst, p, datalen, level)
742 u_int32_t src, dst, level;
743 register char *p;
744 register int datalen;
745 {
746 vifi_t vifi;
747 register int width, i, nrt = 0;
748 int metric;
749 u_int32_t mask;
750 u_int32_t origin;
751 struct newrt rt[4096];
752
753 if ((vifi = find_vif(src, dst)) == NO_VIF) {
754 log(LOG_INFO, 0,
755 "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
756 return;
757 }
758
759 if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level))
760 return;
761
762 if (datalen > 2*4096) {
763 log(LOG_INFO, 0,
764 "ignoring oversize (%d bytes) route report from %s",
765 datalen, inet_fmt(src, s1));
766 return;
767 }
768
769 while (datalen > 0) { /* Loop through per-mask lists. */
770
771 if (datalen < 3) {
772 log(LOG_WARNING, 0,
773 "received truncated route report from %s",
774 inet_fmt(src, s1));
775 return;
776 }
777 ((u_char *)&mask)[0] = 0xff; width = 1;
778 if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
779 if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
780 if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
781 datalen -= 3;
782
783 do { /* Loop through (origin, metric) pairs */
784 if (datalen < width + 1) {
785 log(LOG_WARNING, 0,
786 "received truncated route report from %s",
787 inet_fmt(src, s1));
788 return;
789 }
790 origin = 0;
791 for (i = 0; i < width; ++i)
792 ((char *)&origin)[i] = *p++;
793 metric = *p++;
794 datalen -= width + 1;
795 rt[nrt].mask = mask;
796 rt[nrt].origin = origin;
797 rt[nrt].metric = (metric & 0x7f);
798 ++nrt;
799 } while (!(metric & 0x80));
800 }
801 qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
802 start_route_updates();
803 /*
804 * If the last entry is default, change mask from 0xff000000 to 0
805 */
806 if (rt[nrt-1].origin == 0)
807 rt[nrt-1].mask = 0;
808
809 log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
810 inet_fmt(src, s1), inet_fmt(dst, s2));
811 for (i = 0; i < nrt; ++i)
812 update_route(rt[i].origin, rt[i].mask, rt[i].metric,
813 src, vifi);
814
815 if (routes_changed && !delay_change_reports)
816 report_to_all_neighbors(CHANGED_ROUTES);
817 }
818
819
820 /*
821 * Send a route report message to destination 'dst', via virtual interface
822 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
823 */
824 void
825 report(which_routes, vifi, dst)
826 int which_routes;
827 vifi_t vifi;
828 u_int32_t dst;
829 {
830 register struct rtentry *r;
831 register char *p;
832 register int i;
833 int datalen;
834 int width;
835 u_int32_t mask;
836 u_int32_t src;
837 u_int32_t nflags;
838
839 src = uvifs[vifi].uv_lcl_addr;
840
841 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
842 datalen = 0;
843 mask = 0;
844
845 #ifdef NOTYET
846 /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */
847 if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) {
848 *p++ = 0; /* 0xff000000 mask */
849 *p++ = 0;
850 *p++ = 0;
851 *p++ = 0; /* class A net 0.0.0.0 == default */
852 *p++ = 0x81; /*XXX metric 1, is this safe? */
853 datalen += 5;
854 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
855 htonl(MROUTED_LEVEL), datalen);
856 return;
857 }
858 #endif
859
860 nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
861
862 for (r = rt_end; r != RT_ADDR; r = r->rt_prev) {
863
864 if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
865 continue;
866
867 /*
868 * If there is no room for this route in the current message,
869 * send the message and start a new one.
870 */
871 if (datalen + ((r->rt_originmask == mask) ?
872 (width + 1) :
873 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
874 *(p-1) |= 0x80;
875 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
876 htonl(MROUTED_LEVEL | nflags), datalen);
877
878 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
879 datalen = 0;
880 mask = 0;
881 }
882
883 if(r->rt_originmask != mask) {
884 mask = r->rt_originmask;
885 width = r->rt_originwidth;
886 if (datalen != 0) *(p-1) |= 0x80;
887 *p++ = ((char *)&mask)[1];
888 *p++ = ((char *)&mask)[2];
889 *p++ = ((char *)&mask)[3];
890 datalen += 3;
891 }
892
893 for (i = 0; i < width; ++i)
894 *p++ = ((char *)&(r->rt_origin))[i];
895
896 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
897 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
898 (char)(r->rt_metric);
899
900 datalen += width + 1;
901 }
902
903 if (datalen != 0) {
904 *(p-1) |= 0x80;
905 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
906 htonl(MROUTED_LEVEL | nflags), datalen);
907 }
908 }
909
910
911 /*
912 * Send a route report message to all neighboring routers.
913 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
914 */
915 void
916 report_to_all_neighbors(which_routes)
917 int which_routes;
918 {
919 register vifi_t vifi;
920 register struct uvif *v;
921 register struct rtentry *r;
922 int routes_changed_before;
923
924 /*
925 * Remember the state of the global routes_changed flag before
926 * generating the reports, and clear the flag.
927 */
928 routes_changed_before = routes_changed;
929 routes_changed = FALSE;
930
931
932 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
933 if (v->uv_neighbors != NULL) {
934 report(which_routes, vifi,
935 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
936 : dvmrp_group);
937 }
938 }
939
940 /*
941 * If there were changed routes before we sent the reports AND
942 * if no new changes occurred while sending the reports, clear
943 * the change flags in the individual route entries. If changes
944 * did occur while sending the reports, new reports will be
945 * generated at the next timer interrupt.
946 */
947 if (routes_changed_before && !routes_changed) {
948 for (r = routing_table; r != NULL; r = r->rt_next) {
949 r->rt_flags &= ~RTF_CHANGED;
950 }
951 }
952
953 /*
954 * Set a flag to inhibit further reports of changed routes until the
955 * next timer interrupt. This is to alleviate update storms.
956 */
957 delay_change_reports = TRUE;
958 }
959
960 /*
961 * Send a route report message to destination 'dst', via virtual interface
962 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
963 */
964 int
965 report_chunk(start_rt, vifi, dst)
966 register struct rtentry *start_rt;
967 vifi_t vifi;
968 u_int32_t dst;
969 {
970 register struct rtentry *r;
971 register char *p;
972 register int i;
973 register int nrt = 0;
974 int datalen;
975 int width;
976 u_int32_t mask;
977 u_int32_t src;
978 u_int32_t nflags;
979
980 src = uvifs[vifi].uv_lcl_addr;
981 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
982 datalen = 0;
983 mask = 0;
984
985 nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
986
987 for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
988
989 #ifdef NOTYET
990 /* Don't send poisoned routes back to parents if I am a leaf */
991 if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi)
992 && (r->rt_metric > 1)) {
993 ++nrt;
994 continue;
995 }
996 #endif
997
998 /*
999 * If there is no room for this route in the current message,
1000 * send it & return how many routes we sent.
1001 */
1002 if (datalen + ((r->rt_originmask == mask) ?
1003 (width + 1) :
1004 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
1005 *(p-1) |= 0x80;
1006 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
1007 htonl(MROUTED_LEVEL | nflags), datalen);
1008 return (nrt);
1009 }
1010 if(r->rt_originmask != mask) {
1011 mask = r->rt_originmask;
1012 width = r->rt_originwidth;
1013 if (datalen != 0) *(p-1) |= 0x80;
1014 *p++ = ((char *)&mask)[1];
1015 *p++ = ((char *)&mask)[2];
1016 *p++ = ((char *)&mask)[3];
1017 datalen += 3;
1018 }
1019 for (i = 0; i < width; ++i)
1020 *p++ = ((char *)&(r->rt_origin))[i];
1021
1022 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
1023 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
1024 (char)(r->rt_metric);
1025 ++nrt;
1026 datalen += width + 1;
1027 }
1028 if (datalen != 0) {
1029 *(p-1) |= 0x80;
1030 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
1031 htonl(MROUTED_LEVEL | nflags), datalen);
1032 }
1033 return (nrt);
1034 }
1035
1036 /*
1037 * send the next chunk of our routing table to all neighbors.
1038 * return the length of the smallest chunk we sent out.
1039 */
1040 int
1041 report_next_chunk()
1042 {
1043 register vifi_t vifi;
1044 register struct uvif *v;
1045 register struct rtentry *sr;
1046 register int i, n = 0, min = 20000;
1047 static int start_rt;
1048
1049 if (nroutes <= 0)
1050 return (0);
1051
1052 /*
1053 * find this round's starting route.
1054 */
1055 for (sr = rt_end, i = start_rt; --i >= 0; ) {
1056 sr = sr->rt_prev;
1057 if (sr == RT_ADDR)
1058 sr = rt_end;
1059 }
1060
1061 /*
1062 * send one chunk of routes starting at this round's start to
1063 * all our neighbors.
1064 */
1065 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
1066 if ((v->uv_neighbors != NULL)
1067 #ifdef NOTYET
1068 && !(v->uv_flags & VIFF_LEAF)
1069 #endif
1070 ) {
1071 n = report_chunk(sr, vifi,
1072 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
1073 : dvmrp_group);
1074 if (n < min)
1075 min = n;
1076 }
1077 }
1078 if (min == 20000)
1079 min = 0; /* Neighborless router didn't send any routes */
1080
1081 n = min;
1082 log(LOG_INFO, 0, "update %d starting at %d of %d",
1083 n, (nroutes - start_rt), nroutes);
1084
1085 start_rt = (start_rt + n) % nroutes;
1086 return (n);
1087 }
1088
1089
1090 /*
1091 * Print the contents of the routing table on file 'fp'.
1092 */
1093 void
1094 dump_routes(fp)
1095 FILE *fp;
1096 {
1097 register struct rtentry *r;
1098 register int i;
1099 register time_t thyme = time(0);
1100
1101
1102 fprintf(fp,
1103 "Multicast Routing Table (%u %s)\n%s\n",
1104 nroutes, (nroutes == 1) ? "entry" : "entries",
1105 " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs");
1106
1107 for (r = routing_table; r != NULL; r = r->rt_next) {
1108
1109 fprintf(fp, " %-18s %-15s ",
1110 inet_fmts(r->rt_origin, r->rt_originmask, s1),
1111 (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
1112
1113 fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
1114 r->rt_metric);
1115
1116 fprintf(fp, " %3u %3u ", r->rt_timer, r->rt_parent);
1117
1118 for (i = 0; i < numvifs; ++i) {
1119 if (VIFM_ISSET(i, r->rt_children)) {
1120 fprintf(fp, " %u%c",
1121 i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
1122 }
1123 }
1124 fprintf(fp, "\n");
1125 }
1126 fprintf(fp, "\n");
1127 }
1128
1129 struct rtentry *
1130 determine_route(src)
1131 u_int32_t src;
1132 {
1133 struct rtentry *rt;
1134
1135 for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
1136 if (rt->rt_origin == (src & rt->rt_originmask))
1137 break;
1138 }
1139 return rt;
1140 }
1141