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