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