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