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