xref: /openbsd/usr.sbin/mrouted/route.c (revision 323ce4b6)
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(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
init_routes()54 init_routes()
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
init_children_and_leaves(struct rtentry * r,vifi_t parent)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
add_vif_to_routes(vifi_t vifi)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
delete_vif_from_routes(vifi_t vifi)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
delete_neighbor_from_routes(u_int32_t addr,vifi_t vifi)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
start_route_updates(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
find_route(u_int32_t origin,u_int32_t mask)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
create_route(u_int32_t origin,u_int32_t mask)282 create_route(u_int32_t origin, u_int32_t mask)
283 {
284     struct rtentry *r;
285 
286     if ((r = 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
discard_route(struct rtentry * prev_r)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
update_route(u_int32_t origin,u_int32_t mask,u_int metric,u_int32_t src,vifi_t vifi)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, s1), metric, inet_fmts(origin, mask, s2));
356 	return;
357     }
358     adj_metric = metric + uvifs[vifi].uv_metric;
359     if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
360 
361     /*
362      * Look up the reported origin in the routing table.
363      */
364     if (!find_route(origin, mask)) {
365 	/*
366 	 * Not found.
367 	 * Don't create a new entry if the report says it's unreachable,
368 	 * or if the reported origin and mask are invalid.
369 	 */
370 	if (adj_metric == UNREACHABLE) {
371 	    return;
372 	}
373 	if (src != 0 && !inet_valid_subnet(origin, mask)) {
374 	    logit(LOG_WARNING, 0,
375 		"%s reports an invalid origin (%s) and/or mask (%08x)",
376 		inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
377 	    return;
378 	}
379 
380 	/*
381 	 * OK, create the new routing entry.  'rtp' will be left pointing
382 	 * to the new entry.
383 	 */
384 	create_route(origin, mask);
385 
386 	/*
387 	 * Now "steal away" any sources that belong under this route
388 	 * by deleting any cache entries they might have created
389 	 * and allowing the kernel to re-request them.
390 	 */
391 	steal_sources(rtp);
392 
393 	rtp->rt_metric = UNREACHABLE;	/* temporary; updated below */
394     }
395 
396     /*
397      * We now have a routing entry for the reported origin.  Update it?
398      */
399     r = rtp;
400     if (r->rt_metric == UNREACHABLE) {
401 	/*
402 	 * The routing entry is for a formerly-unreachable or new origin.
403 	 * If the report claims reachability, update the entry to use
404 	 * the reported route.
405 	 */
406 	if (adj_metric == UNREACHABLE)
407 	    return;
408 
409 	r->rt_parent   = vifi;
410 	init_children_and_leaves(r, vifi);
411 
412 	r->rt_gateway  = src;
413 	r->rt_timer    = 0;
414 	r->rt_metric   = adj_metric;
415 	r->rt_flags   |= RTF_CHANGED;
416 	routes_changed = TRUE;
417 	update_table_entry(r);
418     }
419     else if (src == r->rt_gateway) {
420 	/*
421 	 * The report has come either from the interface directly-connected
422 	 * to the origin subnet (src and r->rt_gateway both equal zero) or
423 	 * from the gateway we have chosen as the best first-hop gateway back
424 	 * towards the origin (src and r->rt_gateway not equal zero).  Reset
425 	 * the route timer and, if the reported metric has changed, update
426 	 * our entry accordingly.
427 	 */
428 	r->rt_timer = 0;
429 	if (adj_metric == r->rt_metric)
430 	    return;
431 
432 	if (adj_metric == UNREACHABLE) {
433 	    del_table_entry(r, 0, DEL_ALL_ROUTES);
434 	    r->rt_timer = ROUTE_EXPIRE_TIME;
435 	}
436 	else if (adj_metric < r->rt_metric) {
437 	    if (init_children_and_leaves(r, vifi)) {
438 		update_table_entry(r);
439 	    }
440 	}
441 	r->rt_metric   = adj_metric;
442 	r->rt_flags   |= RTF_CHANGED;
443 	routes_changed = TRUE;
444     }
445     else if (src == 0 ||
446 	     (r->rt_gateway != 0 &&
447 	      (adj_metric < r->rt_metric ||
448 	       (adj_metric == r->rt_metric &&
449 		(ntohl(src) < ntohl(r->rt_gateway) ||
450 		 r->rt_timer >= ROUTE_SWITCH_TIME))))) {
451 	/*
452 	 * The report is for an origin we consider reachable; the report
453 	 * comes either from one of our own interfaces or from a gateway
454 	 * other than the one we have chosen as the best first-hop gateway
455 	 * back towards the origin.  If the source of the update is one of
456 	 * our own interfaces, or if the origin is not a directly-connected
457 	 * subnet and the reported metric for that origin is better than
458 	 * what our routing entry says, update the entry to use the new
459 	 * gateway and metric.  We also switch gateways if the reported
460 	 * metric is the same as the one in the route entry and the gateway
461 	 * associated with the route entry has not been heard from recently,
462 	 * or if the metric is the same but the reporting gateway has a lower
463 	 * IP address than the gateway associated with the route entry.
464 	 * Did you get all that?
465 	 */
466 	if (r->rt_parent != vifi || adj_metric < r->rt_metric) {
467 	    /*
468 	     * XXX Why do we do this if we are just changing the metric?
469 	     */
470 	    r->rt_parent = vifi;
471 	    if (init_children_and_leaves(r, vifi)) {
472 		update_table_entry(r);
473 	    }
474 	}
475 	r->rt_gateway  = src;
476 	r->rt_timer    = 0;
477 	r->rt_metric   = adj_metric;
478 	r->rt_flags   |= RTF_CHANGED;
479 	routes_changed = TRUE;
480     }
481     else if (vifi != r->rt_parent) {
482 	/*
483 	 * The report came from a vif other than the route's parent vif.
484 	 * Update the children and leaf info, if necessary.
485 	 */
486 	if (VIFM_ISSET(vifi, r->rt_children)) {
487 	    /*
488 	     * Vif is a child vif for this route.
489 	     */
490 	    if (metric  < r->rt_metric ||
491 		(metric == r->rt_metric &&
492 		 ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
493 		/*
494 		 * Neighbor has lower metric to origin (or has same metric
495 		 * and lower IP address) -- it becomes the dominant router,
496 		 * and vif is no longer a child for me.
497 		 */
498 		VIFM_CLR(vifi, r->rt_children);
499 		VIFM_CLR(vifi, r->rt_leaves);
500 		r->rt_dominants   [vifi] = src;
501 		r->rt_subordinates[vifi] = 0;
502 		r->rt_leaf_timers [vifi] = 0;
503 		update_table_entry(r);
504 	    }
505 	    else if (metric > UNREACHABLE) {	/* "poisoned reverse" */
506 		/*
507 		 * Neighbor considers this vif to be on path to route's
508 		 * origin; if no subordinate recorded, record this neighbor
509 		 * as subordinate and clear the leaf flag.
510 		 */
511 		if (r->rt_subordinates[vifi] == 0) {
512 		    VIFM_CLR(vifi, r->rt_leaves);
513 		    r->rt_subordinates[vifi] = src;
514 		    r->rt_leaf_timers [vifi] = 0;
515 		    update_table_entry(r);
516 		}
517 	    }
518 	    else if (src == r->rt_subordinates[vifi]) {
519 		/*
520 		 * Current subordinate no longer considers this vif to be on
521 		 * path to route's origin; it is no longer a subordinate
522 		 * router, and we set the leaf confirmation timer to give
523 		 * us time to hear from other subordinates.
524 		 */
525 		r->rt_subordinates[vifi] = 0;
526 		if (uvifs[vifi].uv_neighbors == NULL ||
527 		    uvifs[vifi].uv_neighbors->al_next == NULL) {
528 		    VIFM_SET(vifi, r->rt_leaves);
529 		    update_table_entry(r);
530 		}
531 		else {
532 		    r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME;
533 		    r->rt_flags |= RTF_LEAF_TIMING;
534 		}
535 	    }
536 
537 	}
538 	else if (src == r->rt_dominants[vifi] &&
539 		 (metric  > r->rt_metric ||
540 		  (metric == r->rt_metric &&
541 		   ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
542 	    /*
543 	     * Current dominant no longer has a lower metric to origin
544 	     * (or same metric and lower IP address); we adopt the vif
545 	     * as our own child.
546 	     */
547 	    VIFM_SET(vifi, r->rt_children);
548 	    r->rt_dominants  [vifi] = 0;
549 	    if (metric > UNREACHABLE) {
550 		r->rt_subordinates[vifi] = src;
551 	    }
552 	    else if (uvifs[vifi].uv_neighbors == NULL ||
553 		     uvifs[vifi].uv_neighbors->al_next == NULL) {
554 		VIFM_SET(vifi, r->rt_leaves);
555 	    }
556 	    else {
557 		r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
558 		r->rt_flags |= RTF_LEAF_TIMING;
559 	    }
560 	    update_table_entry(r);
561 	}
562     }
563 }
564 
565 
566 /*
567  * On every timer interrupt, advance the timer in each routing entry.
568  */
569 void
age_routes(void)570 age_routes(void)
571 {
572     struct rtentry *r;
573     struct rtentry *prev_r;
574     vifi_t vifi;
575 
576     for (prev_r = RT_ADDR, r = routing_table;
577 	 r != NULL;
578 	 prev_r = r, r = r->rt_next) {
579 
580 	if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) {
581 	    /*
582 	     * Route is still good; see if any leaf timers need to be
583 	     * advanced.
584 	     */
585 	    if (r->rt_flags & RTF_LEAF_TIMING) {
586 		r->rt_flags &= ~RTF_LEAF_TIMING;
587 		for (vifi = 0; vifi < numvifs; ++vifi) {
588 		    if (r->rt_leaf_timers[vifi] != 0) {
589 			/*
590 			 * Unlike other timers, leaf timers decrement.
591 			 */
592 			if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){
593 #ifdef NOTYET
594 			    /* If the vif is a physical leaf but has neighbors,
595 			     * it is not a tree leaf.  If I am a leaf, then no
596 			     * interface with neighbors is a tree leaf. */
597 			    if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) ||
598 				   (vifs_with_neighbors == 1)) &&
599 				  (uvifs[vifi].uv_neighbors != NULL))) {
600 #endif
601 				VIFM_SET(vifi, r->rt_leaves);
602 				update_table_entry(r);
603 #ifdef NOTYET
604 			    }
605 #endif
606 			}
607 			else {
608 			    r->rt_flags |= RTF_LEAF_TIMING;
609 			}
610 		    }
611 		}
612 	    }
613 	}
614 	else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
615 	    /*
616 	     * Time to garbage-collect the route entry.
617 	     */
618 	    del_table_entry(r, 0, DEL_ALL_ROUTES);
619 	    discard_route(prev_r);
620 	    r = prev_r;
621 	}
622 	else if (r->rt_metric != UNREACHABLE) {
623 	    /*
624 	     * Time to expire the route entry.  If the gateway is zero,
625 	     * i.e., it is a route to a directly-connected subnet, just
626 	     * set the timer back to zero; such routes expire only when
627 	     * the interface to the subnet goes down.
628 	     */
629 	    if (r->rt_gateway == 0) {
630 		r->rt_timer = 0;
631 	    }
632 	    else {
633 		del_table_entry(r, 0, DEL_ALL_ROUTES);
634 		r->rt_metric   = UNREACHABLE;
635 		r->rt_flags   |= RTF_CHANGED;
636 		routes_changed = TRUE;
637 	    }
638 	}
639     }
640 }
641 
642 
643 /*
644  * Mark all routes as unreachable.  This function is called only from
645  * hup() in preparation for informing all neighbors that we are going
646  * off the air.  For consistency, we ought also to delete all reachable
647  * route entries from the kernel, but since we are about to exit we rely
648  * on the kernel to do its own cleanup -- no point in making all those
649  * expensive kernel calls now.
650  */
651 void
expire_all_routes(void)652 expire_all_routes(void)
653 {
654     struct rtentry *r;
655 
656     for (r = routing_table; r != NULL; r = r->rt_next) {
657 	r->rt_metric   = UNREACHABLE;
658 	r->rt_flags   |= RTF_CHANGED;
659 	routes_changed = TRUE;
660     }
661 }
662 
663 
664 /*
665  * Delete all the routes in the routing table.
666  */
667 void
free_all_routes(void)668 free_all_routes(void)
669 {
670     struct rtentry *r;
671 
672     r = RT_ADDR;
673 
674     while (r->rt_next)
675 	discard_route(r);
676 }
677 
678 
679 /*
680  * Process an incoming neighbor probe message.
681  */
682 void
accept_probe(u_int32_t src,u_int32_t dst,char * p,int datalen,u_int32_t level)683 accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
684     u_int32_t level)
685 {
686     vifi_t vifi;
687 
688     if ((vifi = find_vif(src, dst)) == NO_VIF) {
689 	logit(LOG_INFO, 0,
690 	    "ignoring probe from non-neighbor %s", inet_fmt(src, s1));
691 	return;
692     }
693 
694     update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level);
695 }
696 
697 struct newrt {
698 	u_int32_t mask;
699 	u_int32_t origin;
700 	int metric;
701 	int pad;
702 };
703 
704 static int
compare_rts(const void * rt1,const void * rt2)705 compare_rts(const void *rt1, const void *rt2)
706 {
707     struct newrt *r1 = (struct newrt *)rt1;
708     struct newrt *r2 = (struct newrt *)rt2;
709     u_int32_t m1 = ntohl(r1->mask);
710     u_int32_t m2 = ntohl(r2->mask);
711     u_int32_t o1, o2;
712 
713     if (m1 > m2)
714 	return (-1);
715     if (m1 < m2)
716 	return (1);
717 
718     /* masks are equal */
719     o1 = ntohl(r1->origin);
720     o2 = ntohl(r2->origin);
721     if (o1 > o2)
722 	return (-1);
723     if (o1 < o2)
724 	return (1);
725     return (0);
726 }
727 
728 /*
729  * Process an incoming route report message.
730  */
731 void
accept_report(u_int32_t src,u_int32_t dst,char * p,int datalen,u_int32_t level)732 accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
733     u_int32_t level)
734 {
735     vifi_t vifi;
736     int width, i, nrt = 0;
737     int metric;
738     u_int32_t mask;
739     u_int32_t origin;
740     struct newrt rt[4096];
741 
742     if ((vifi = find_vif(src, dst)) == NO_VIF) {
743 	logit(LOG_INFO, 0,
744 	    "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
745 	return;
746     }
747 
748     if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level))
749 	return;
750 
751     if (datalen > 2*4096) {
752 	logit(LOG_INFO, 0,
753 	    "ignoring oversize (%d bytes) route report from %s",
754 	    datalen, inet_fmt(src, s1));
755 	return;
756     }
757 
758     while (datalen > 0) {	/* Loop through per-mask lists. */
759 
760 	if (datalen < 3) {
761 	    logit(LOG_WARNING, 0,
762 		"received truncated route report from %s",
763 		inet_fmt(src, s1));
764 	    return;
765 	}
766 	((u_char *)&mask)[0] = 0xff;            width = 1;
767 	if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
768 	if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
769 	if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
770 	if (!inet_valid_mask(ntohl(mask))) {
771 	    logit(LOG_WARNING, 0,
772 		"%s reports bogus netmask 0x%08x (%s)",
773 		inet_fmt(src, s1), ntohl(mask), inet_fmt(mask, s2));
774 	    return;
775 	}
776 	datalen -= 3;
777 
778 	do {			/* Loop through (origin, metric) pairs */
779 	    if (datalen < width + 1) {
780 		logit(LOG_WARNING, 0,
781 		    "received truncated route report from %s",
782 		    inet_fmt(src, s1));
783 		return;
784 	    }
785 	    origin = 0;
786 	    for (i = 0; i < width; ++i)
787 		((char *)&origin)[i] = *p++;
788 	    metric = *p++;
789 	    datalen -= width + 1;
790 	    rt[nrt].mask   = mask;
791 	    rt[nrt].origin = origin;
792 	    rt[nrt].metric = (metric & 0x7f);
793 	    ++nrt;
794 	} while (!(metric & 0x80));
795     }
796 
797     qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
798     start_route_updates();
799     /*
800      * If the last entry is default, change mask from 0xff000000 to 0
801      */
802     if (rt[nrt-1].origin == 0)
803 	rt[nrt-1].mask = 0;
804 
805     logit(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
806 		inet_fmt(src, s1), inet_fmt(dst, s2));
807     for (i = 0; i < nrt; ++i) {
808 	if (i != 0 && rt[i].origin == rt[i-1].origin &&
809 		      rt[i].mask == rt[i-1].mask) {
810 	    logit(LOG_WARNING, 0, "%s reports duplicate route for %s",
811 		inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2));
812 	    continue;
813 	}
814 	update_route(rt[i].origin, rt[i].mask, rt[i].metric,
815 		     src, vifi);
816     }
817 
818     if (routes_changed && !delay_change_reports)
819 	report_to_all_neighbors(CHANGED_ROUTES);
820 }
821 
822 
823 /*
824  * Send a route report message to destination 'dst', via virtual interface
825  * 'vifi'.  'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
826  */
827 void
report(int which_routes,vifi_t vifi,u_int32_t dst)828 report(int which_routes, vifi_t vifi, u_int32_t dst)
829 {
830     struct rtentry *r;
831     char *p;
832     int i;
833     int datalen = 0;
834     int width = 0;
835     u_int32_t mask = 0;
836     u_int32_t src;
837     u_int32_t nflags;
838 
839     src = uvifs[vifi].uv_lcl_addr;
840 
841     p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
842 
843 #ifdef NOTYET
844     /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */
845     if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) {
846       *p++ = 0;       /* 0xff000000 mask */
847       *p++ = 0;
848       *p++ = 0;
849       *p++ = 0;       /* class A net 0.0.0.0 == default */
850       *p++ = 0x81;    /*XXX metric 1, is this safe? */
851       datalen += 5;
852       send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
853                 htonl(MROUTED_LEVEL), datalen);
854       return;
855     }
856 #endif
857 
858     nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
859 
860     for (r = rt_end; r != RT_ADDR; r = r->rt_prev) {
861 
862 	if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
863 	    continue;
864 
865 	/*
866 	 * If there is no room for this route in the current message,
867 	 * send the message and start a new one.
868 	 */
869 	if (datalen + ((r->rt_originmask == mask) ?
870 		       (width + 1) :
871 		       (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
872 	    *(p-1) |= 0x80;
873 	    send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
874 		      htonl(MROUTED_LEVEL | nflags), datalen);
875 
876 	    p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
877 	    datalen = 0;
878 	    mask = 0;
879 	}
880 
881 	if (r->rt_originmask != mask || datalen == 0) {
882 	    mask  = r->rt_originmask;
883 	    width = r->rt_originwidth;
884 	    if (datalen != 0) *(p-1) |= 0x80;
885 	    *p++ = ((char *)&mask)[1];
886 	    *p++ = ((char *)&mask)[2];
887 	    *p++ = ((char *)&mask)[3];
888 	    datalen += 3;
889 	}
890 
891 	for (i = 0; i < width; ++i)
892 	    *p++ = ((char *)&(r->rt_origin))[i];
893 
894 	*p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
895 	    (char)(r->rt_metric + UNREACHABLE) :  /* "poisoned reverse" */
896 		(char)(r->rt_metric);
897 
898 	datalen += width + 1;
899     }
900 
901     if (datalen != 0) {
902 	*(p-1) |= 0x80;
903 	send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
904 		  htonl(MROUTED_LEVEL | nflags), datalen);
905     }
906 }
907 
908 
909 /*
910  * Send a route report message to all neighboring routers.
911  * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
912  */
913 void
report_to_all_neighbors(int which_routes)914 report_to_all_neighbors(int which_routes)
915 {
916     vifi_t vifi;
917     struct uvif *v;
918     struct rtentry *r;
919     int routes_changed_before;
920 
921     /*
922      * Remember the state of the global routes_changed flag before
923      * generating the reports, and clear the flag.
924      */
925     routes_changed_before = routes_changed;
926     routes_changed = FALSE;
927 
928 
929     for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
930 	if (v->uv_neighbors != NULL) {
931 	    report(which_routes, vifi,
932 		   (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
933 		   : dvmrp_group);
934 	}
935     }
936 
937     /*
938      * If there were changed routes before we sent the reports AND
939      * if no new changes occurred while sending the reports, clear
940      * the change flags in the individual route entries.  If changes
941      * did occur while sending the reports, new reports will be
942      * generated at the next timer interrupt.
943      */
944     if (routes_changed_before && !routes_changed) {
945 	for (r = routing_table; r != NULL; r = r->rt_next) {
946 	    r->rt_flags &= ~RTF_CHANGED;
947 	}
948     }
949 
950     /*
951      * Set a flag to inhibit further reports of changed routes until the
952      * next timer interrupt.  This is to alleviate update storms.
953      */
954     delay_change_reports = TRUE;
955 }
956 
957 /*
958  * Send a route report message to destination 'dst', via virtual interface
959  * 'vifi'.  'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
960  */
961 static int
report_chunk(struct rtentry * start_rt,vifi_t vifi,u_int32_t dst)962 report_chunk(struct rtentry *start_rt, vifi_t vifi, u_int32_t dst)
963 {
964     struct rtentry *r;
965     char *p;
966     int i;
967     int nrt = 0;
968     int datalen = 0;
969     int width = 0;
970     u_int32_t mask = 0;
971     u_int32_t src;
972     u_int32_t nflags;
973 
974     src = uvifs[vifi].uv_lcl_addr;
975     p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
976 
977     nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
978 
979     for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
980 
981 #ifdef NOTYET
982 	/* Don't send poisoned routes back to parents if I am a leaf */
983 	if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi)
984 		&& (r->rt_metric > 1)) {
985 	    ++nrt;
986 	    continue;
987 	}
988 #endif
989 
990 	/*
991 	 * If there is no room for this route in the current message,
992 	 * send it & return how many routes we sent.
993 	 */
994 	if (datalen + ((r->rt_originmask == mask) ?
995 		       (width + 1) :
996 		       (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
997 	    *(p-1) |= 0x80;
998 	    send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
999 		      htonl(MROUTED_LEVEL | nflags), datalen);
1000 	    return (nrt);
1001 	}
1002 	if (r->rt_originmask != mask || datalen == 0) {
1003 	    mask  = r->rt_originmask;
1004 	    width = r->rt_originwidth;
1005 	    if (datalen != 0) *(p-1) |= 0x80;
1006 	    *p++ = ((char *)&mask)[1];
1007 	    *p++ = ((char *)&mask)[2];
1008 	    *p++ = ((char *)&mask)[3];
1009 	    datalen += 3;
1010 	}
1011 	for (i = 0; i < width; ++i)
1012 	    *p++ = ((char *)&(r->rt_origin))[i];
1013 
1014 	*p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
1015 	    (char)(r->rt_metric + UNREACHABLE) :  /* "poisoned reverse" */
1016 		(char)(r->rt_metric);
1017 	++nrt;
1018 	datalen += width + 1;
1019     }
1020     if (datalen != 0) {
1021 	*(p-1) |= 0x80;
1022 	send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
1023 		  htonl(MROUTED_LEVEL | nflags), datalen);
1024     }
1025     return (nrt);
1026 }
1027 
1028 /*
1029  * send the next chunk of our routing table to all neighbors.
1030  * return the length of the smallest chunk we sent out.
1031  */
1032 int
report_next_chunk(void)1033 report_next_chunk(void)
1034 {
1035     vifi_t vifi;
1036     struct uvif *v;
1037     struct rtentry *sr;
1038     int i, n = 0, min = 20000;
1039     static int start_rt;
1040 
1041     if (nroutes <= 0)
1042 	return (0);
1043 
1044     /*
1045      * find this round's starting route.
1046      */
1047     for (sr = rt_end, i = start_rt; --i >= 0; ) {
1048 	sr = sr->rt_prev;
1049 	if (sr == RT_ADDR)
1050 	    sr = rt_end;
1051     }
1052 
1053     /*
1054      * send one chunk of routes starting at this round's start to
1055      * all our neighbors.
1056      */
1057     for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
1058 	if ((v->uv_neighbors != NULL)
1059 #ifdef NOTYET
1060 	&& !(v->uv_flags & VIFF_LEAF)
1061 #endif
1062 		) {
1063 	    n = report_chunk(sr, vifi,
1064 			     (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
1065 			     : dvmrp_group);
1066 	    if (n < min)
1067 		min = n;
1068 	}
1069     }
1070     if (min == 20000)
1071 	min = 0;	/* Neighborless router didn't send any routes */
1072 
1073     n = min;
1074     logit(LOG_INFO, 0, "update %d starting at %d of %d",
1075 	n, (nroutes - start_rt), nroutes);
1076 
1077     start_rt = (start_rt + n) % nroutes;
1078     return (n);
1079 }
1080 
1081 
1082 /*
1083  * Print the contents of the routing table on file 'fp'.
1084  */
1085 void
dump_routes(FILE * fp)1086 dump_routes(FILE *fp)
1087 {
1088     struct rtentry *r;
1089     vifi_t i;
1090 
1091 
1092     fprintf(fp,
1093 	    "Multicast Routing Table (%u %s)\n%s\n",
1094 	    nroutes, (nroutes == 1) ? "entry" : "entries",
1095 	    " Origin-Subnet      From-Gateway    Metric Tmr In-Vif  Out-Vifs");
1096 
1097     for (r = routing_table; r != NULL; r = r->rt_next) {
1098 
1099 	fprintf(fp, " %-18s %-15s ",
1100 		inet_fmts(r->rt_origin, r->rt_originmask, s1),
1101 		(r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
1102 
1103 	fprintf(fp, (r->rt_metric == UNREACHABLE) ? "  NR " : "%4u ",
1104 		r->rt_metric);
1105 
1106 	fprintf(fp, "  %3u %3u   ", r->rt_timer, r->rt_parent);
1107 
1108 	for (i = 0; i < numvifs; ++i) {
1109 	    if (VIFM_ISSET(i, r->rt_children)) {
1110 		fprintf(fp, " %u%c",
1111 			i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
1112 	    }
1113 	}
1114 	fprintf(fp, "\n");
1115     }
1116     fprintf(fp, "\n");
1117 }
1118 
1119 struct rtentry *
determine_route(u_int32_t src)1120 determine_route(u_int32_t src)
1121 {
1122     struct rtentry *rt;
1123 
1124     for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
1125 	if (rt->rt_origin == (src & rt->rt_originmask))
1126 	    break;
1127     }
1128     return rt;
1129 }
1130