route.c revision 1.2 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.2 1994/05/08 15:08:56 brezak Exp $
12 */
13
14 #ifndef lint
15 static char rcsid[] = "$Id: route.c,v 1.2 1994/05/08 15:08:56 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 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_INFO, 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_INFO, 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 struct newrt {
687 u_long mask;
688 u_long origin;
689 int metric;
690 int pad;
691 };
692
693 int compare_rts(r1, r2)
694 register struct newrt *r1;
695 register struct newrt *r2;
696 {
697 register unsigned long m1 = ntohl(r1->mask);
698 register unsigned long m2 = ntohl(r2->mask);
699 register unsigned long o1, o2;
700
701 if (m1 > m2)
702 return (1);
703 if (m1 < m2)
704 return (-1);
705
706 /* masks are equal */
707 o1 = ntohl(r1->origin);
708 o2 = ntohl(r2->origin);
709 if (o1 > o2)
710 return (1);
711 if (o1 < o2)
712 return (-1);
713 return (0);
714 }
715
716 /*
717 * Process an incoming route report message.
718 */
719 void accept_report(src, dst, p, datalen)
720 u_long src, dst;
721 register char *p;
722 register int datalen;
723 {
724 vifi_t vifi;
725 register int width, i, nrt = 0;
726 int metric;
727 u_long mask;
728 u_long origin;
729 struct newrt rt[4096];
730
731 if ((vifi = find_vif(src, dst)) == NO_VIF) {
732 log(LOG_INFO, 0,
733 "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
734 return;
735 }
736
737 if (!update_neighbor(vifi, src, DVMRP_REPORT))
738 return;
739
740 if (datalen > 2*4096) {
741 log(LOG_INFO, 0,
742 "ignoring oversize (%d bytes) route report from %s",
743 datalen, inet_fmt(src, s1));
744 return;
745 }
746
747 while (datalen > 0) { /* Loop through per-mask lists. */
748 if (datalen < 3) {
749 log(LOG_WARNING, 0,
750 "received truncated route report from %s", inet_fmt(src, s1));
751 return;
752 }
753 ((char *)&mask)[0] = 0xff; width = 1;
754 if ((((char *)&mask)[1] = *p++) != 0) width = 2;
755 if ((((char *)&mask)[2] = *p++) != 0) width = 3;
756 if ((((char *)&mask)[3] = *p++) != 0) width = 4;
757 datalen -= 3;
758
759 do { /* Loop through (origin, metric) pairs */
760 if (datalen < width + 1) {
761 log(LOG_WARNING, 0,
762 "received truncated route report from %s", inet_fmt(src, s1));
763 return;
764 }
765 origin = 0;
766 for (i = 0; i < width; ++i)
767 ((char *)&origin)[i] = *p++;
768 metric = *p++;
769 datalen -= width + 1;
770 rt[nrt].mask = mask;
771 rt[nrt].origin = origin;
772 rt[nrt].metric = metric;
773 ++nrt;
774 } while (!(metric & 0x80));
775 }
776 qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
777 start_route_updates();
778 for (i = 0; i < nrt; ++i)
779 update_route(rt[i].origin, rt[i].mask, (rt[i].metric & 0x7f), src, vifi);
780
781 if (routes_changed && !delay_change_reports)
782 report_to_all_neighbors(CHANGED_ROUTES);
783 }
784
785
786 /*
787 * Send a route report message to destination 'dst', via virtual interface
788 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
789 */
790 void report(which_routes, vifi, dst)
791 int which_routes;
792 vifi_t vifi;
793 u_long dst;
794 {
795 register struct rtentry *r;
796 register char *p;
797 register int i;
798 int datalen;
799 int width;
800 u_long mask;
801 u_long src;
802
803 src = uvifs[vifi].uv_lcl_addr;
804
805 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
806 datalen = 0;
807 mask = 0;
808
809 for (r = routing_table; r != NULL; r = r->rt_next) {
810
811 if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
812 continue;
813
814 /*
815 * If there is no room for this route in the current message,
816 * send the message and start a new one.
817 */
818 if (datalen + ((r->rt_originmask == mask) ?
819 (width + 1) :
820 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
821 *(p-1) |= 0x80;
822 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
823 htonl(MROUTED_LEVEL), datalen);
824
825 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
826 datalen = 0;
827 mask = 0;
828 }
829
830 if(r->rt_originmask != mask) {
831 mask = r->rt_originmask;
832 width = r->rt_originwidth;
833 if (datalen != 0) *(p-1) |= 0x80;
834 *p++ = ((char *)&mask)[1];
835 *p++ = ((char *)&mask)[2];
836 *p++ = ((char *)&mask)[3];
837 datalen += 3;
838 }
839
840 for (i = 0; i < width; ++i)
841 *p++ = ((char *)&(r->rt_origin))[i];
842
843 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
844 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
845 (char)(r->rt_metric);
846
847 datalen += width + 1;
848 }
849
850 if (datalen != 0) {
851 *(p-1) |= 0x80;
852 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
853 htonl(MROUTED_LEVEL), datalen);
854 }
855 }
856
857
858 /*
859 * Send a route report message to all neighboring routers.
860 * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
861 */
862 void report_to_all_neighbors(which_routes)
863 int which_routes;
864 {
865 register vifi_t vifi;
866 register struct uvif *v;
867 register struct rtentry *r;
868 int routes_changed_before;
869
870 /*
871 * Remember the state of the global routes_changed flag before
872 * generating the reports, and clear the flag.
873 */
874 routes_changed_before = routes_changed;
875 routes_changed = FALSE;
876
877
878 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
879 if (v->uv_neighbors != NULL) {
880 report(which_routes, vifi,
881 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
882 : dvmrp_group);
883 }
884 }
885
886 /*
887 * If there were changed routes before we sent the reports AND
888 * if no new changes occurred while sending the reports, clear
889 * the change flags in the individual route entries. If changes
890 * did occur while sending the reports, new reports will be
891 * generated at the next timer interrupt.
892 */
893 if (routes_changed_before && !routes_changed) {
894 for (r = routing_table; r != NULL; r = r->rt_next) {
895 r->rt_flags &= ~RTF_CHANGED;
896 }
897 }
898
899 /*
900 * Set a flag to inhibit further reports of changed routes until the
901 * next timer interrupt. This is to alleviate update storms.
902 */
903 delay_change_reports = TRUE;
904 }
905
906 /*
907 * Send a route report message to destination 'dst', via virtual interface
908 * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
909 */
910 int report_chunk(start_rt, vifi, dst)
911 register struct rtentry *start_rt;
912 vifi_t vifi;
913 u_long dst;
914 {
915 register struct rtentry *r;
916 register char *p;
917 register int i;
918 register int nrt = 0;
919 int datalen;
920 int width;
921 u_long mask;
922 u_long src;
923
924 src = uvifs[vifi].uv_lcl_addr;
925 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
926 datalen = 0;
927 mask = 0;
928
929 for (r = start_rt; r != NULL; r = r->rt_next) {
930 /*
931 * If there is no room for this route in the current message,
932 * send it & return how many routes we sent.
933 */
934 if (datalen + ((r->rt_originmask == mask) ?
935 (width + 1) :
936 (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
937 *(p-1) |= 0x80;
938 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
939 htonl(MROUTED_LEVEL), datalen);
940 return (nrt);
941 }
942 if(r->rt_originmask != mask) {
943 mask = r->rt_originmask;
944 width = r->rt_originwidth;
945 if (datalen != 0) *(p-1) |= 0x80;
946 *p++ = ((char *)&mask)[1];
947 *p++ = ((char *)&mask)[2];
948 *p++ = ((char *)&mask)[3];
949 datalen += 3;
950 }
951 for (i = 0; i < width; ++i)
952 *p++ = ((char *)&(r->rt_origin))[i];
953
954 *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
955 (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
956 (char)(r->rt_metric);
957 ++nrt;
958 datalen += width + 1;
959 }
960 if (datalen != 0) {
961 *(p-1) |= 0x80;
962 send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
963 htonl(MROUTED_LEVEL), datalen);
964 }
965 return (nrt);
966 }
967
968 /*
969 * send the next chunk of our routing table to all neighbors.
970 */
971 int report_next_chunk()
972 {
973 register vifi_t vifi;
974 register struct uvif *v;
975 register struct rtentry *r;
976 register struct rtentry *sr;
977 register int i, n = 0;
978 static int start_rt;
979
980 if (nroutes <= 0)
981 return (0);
982
983 /*
984 * find this round's starting route.
985 */
986 for (sr = routing_table, i = start_rt; --i >= 0; ) {
987 sr = sr->rt_next;
988 if (sr == NULL)
989 sr = routing_table;
990 }
991 /*
992 * send one chunk of routes starting at this round's start to
993 * all our neighbors.
994 */
995 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
996 if (v->uv_neighbors != NULL) {
997 n = report_chunk(sr, vifi,
998 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
999 : dvmrp_group);
1000 }
1001 }
1002 if (debug)
1003 printf("update %d starting at %d of %d\n", n, start_rt, nroutes);
1004 start_rt = (start_rt + n) % nroutes;
1005 return (n);
1006 }
1007
1008
1009 /*
1010 * Print the contents of the routing table on file 'fp'.
1011 */
1012 void dump_routes(fp)
1013 FILE *fp;
1014 {
1015 register struct rtentry *r;
1016 register int i;
1017
1018 fprintf(fp,
1019 "Multicast Routing Table (%u %s)\n%s",
1020 nroutes, (nroutes == 1) ? "entry" : "entries",
1021 " Origin-Subnet From-Gateway Metric In-Vif Out-Vifs\n");
1022
1023 for (r = routing_table; r != NULL; r = r->rt_next) {
1024
1025 fprintf(fp, " %-15s %-15s ",
1026 inet_fmts(r->rt_origin, r->rt_originmask, s1),
1027 (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
1028
1029 fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
1030 r->rt_metric);
1031
1032 fprintf(fp, "%7u ",
1033 r->rt_parent);
1034
1035 for (i = 0; i < numvifs; ++i) {
1036 if (VIFM_ISSET(i, r->rt_children)) {
1037 fprintf(fp, " %u%c",
1038 i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
1039 }
1040 }
1041 fprintf(fp, "\n");
1042 }
1043 fprintf(fp, "\n");
1044 }
1045