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