route.c revision 1.1 1 1.1 brezak /*
2 1.1 brezak * The mrouted program is covered by the license in the accompanying file
3 1.1 brezak * named "LICENSE". Use of the mrouted program represents acceptance of
4 1.1 brezak * the terms and conditions listed in that file.
5 1.1 brezak *
6 1.1 brezak * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7 1.1 brezak * Leland Stanford Junior University.
8 1.1 brezak *
9 1.1 brezak *
10 1.1 brezak * from: Id: route.c,v 1.5 1993/06/24 05:11:16 deering Exp
11 1.1 brezak * $Id: route.c,v 1.1 1994/01/11 20:16:02 brezak Exp $
12 1.1 brezak */
13 1.1 brezak
14 1.1 brezak #ifndef lint
15 1.1 brezak static char rcsid[] = "$Id: route.c,v 1.1 1994/01/11 20:16:02 brezak Exp $";
16 1.1 brezak #endif
17 1.1 brezak
18 1.1 brezak #include "defs.h"
19 1.1 brezak
20 1.1 brezak
21 1.1 brezak /*
22 1.1 brezak * Exported variables.
23 1.1 brezak */
24 1.1 brezak int routes_changed; /* 1=>some routes have changed */
25 1.1 brezak int delay_change_reports; /* 1=>postpone change reports */
26 1.1 brezak
27 1.1 brezak
28 1.1 brezak /*
29 1.1 brezak * Private variables.
30 1.1 brezak */
31 1.1 brezak static struct rtentry *routing_table; /* pointer to list of route entries */
32 1.1 brezak static struct rtentry *rtp; /* pointer to a route entry */
33 1.1 brezak static unsigned nroutes; /* current number of route entries */
34 1.1 brezak
35 1.1 brezak
36 1.1 brezak /*
37 1.1 brezak * Initialize the routing table and associated variables.
38 1.1 brezak */
39 1.1 brezak void init_routes()
40 1.1 brezak {
41 1.1 brezak routing_table = NULL;
42 1.1 brezak nroutes = 0;
43 1.1 brezak routes_changed = FALSE;
44 1.1 brezak delay_change_reports = FALSE;
45 1.1 brezak }
46 1.1 brezak
47 1.1 brezak
48 1.1 brezak /*
49 1.1 brezak * Initialize the children and leaf bits for route 'r', along with the
50 1.1 brezak * associated dominant, subordinate, and leaf timing data structures.
51 1.1 brezak * Return TRUE if this changes the value of either the children or
52 1.1 brezak * leaf bitmaps for 'r'.
53 1.1 brezak */
54 1.1 brezak static int init_children_and_leaves(r, parent)
55 1.1 brezak register struct rtentry *r;
56 1.1 brezak register vifi_t parent;
57 1.1 brezak {
58 1.1 brezak register vifi_t vifi;
59 1.1 brezak register struct uvif *v;
60 1.1 brezak vifbitmap_t old_children, old_leaves;
61 1.1 brezak
62 1.1 brezak VIFM_COPY(r->rt_children, old_children);
63 1.1 brezak VIFM_COPY(r->rt_leaves, old_leaves );
64 1.1 brezak
65 1.1 brezak VIFM_CLRALL(r->rt_children);
66 1.1 brezak VIFM_CLRALL(r->rt_leaves);
67 1.1 brezak r->rt_flags &= ~RTF_LEAF_TIMING;
68 1.1 brezak
69 1.1 brezak for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
70 1.1 brezak r->rt_dominants [vifi] = 0;
71 1.1 brezak r->rt_subordinates[vifi] = 0;
72 1.1 brezak
73 1.1 brezak if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
74 1.1 brezak VIFM_SET(vifi, r->rt_children);
75 1.1 brezak if (v->uv_neighbors == NULL) {
76 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
77 1.1 brezak r->rt_leaf_timers[vifi] = 0;
78 1.1 brezak }
79 1.1 brezak else {
80 1.1 brezak r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
81 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
82 1.1 brezak }
83 1.1 brezak }
84 1.1 brezak else {
85 1.1 brezak r->rt_leaf_timers[vifi] = 0;
86 1.1 brezak }
87 1.1 brezak }
88 1.1 brezak
89 1.1 brezak return (!VIFM_SAME(r->rt_children, old_children) ||
90 1.1 brezak !VIFM_SAME(r->rt_leaves, old_leaves));
91 1.1 brezak }
92 1.1 brezak
93 1.1 brezak
94 1.1 brezak /*
95 1.1 brezak * A new vif has come up -- update the children and leaf bitmaps in all route
96 1.1 brezak * entries to take that into account.
97 1.1 brezak */
98 1.1 brezak void add_vif_to_routes(vifi)
99 1.1 brezak register vifi_t vifi;
100 1.1 brezak {
101 1.1 brezak register struct rtentry *r;
102 1.1 brezak register struct uvif *v;
103 1.1 brezak
104 1.1 brezak v = &uvifs[vifi];
105 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
106 1.1 brezak if (r->rt_metric != UNREACHABLE &&
107 1.1 brezak !VIFM_ISSET(vifi, r->rt_children)) {
108 1.1 brezak VIFM_SET(vifi, r->rt_children);
109 1.1 brezak r->rt_dominants [vifi] = 0;
110 1.1 brezak r->rt_subordinates[vifi] = 0;
111 1.1 brezak if (v->uv_neighbors == NULL) {
112 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
113 1.1 brezak r->rt_leaf_timers[vifi] = 0;
114 1.1 brezak }
115 1.1 brezak else {
116 1.1 brezak VIFM_CLR(vifi, r->rt_leaves);
117 1.1 brezak r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
118 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
119 1.1 brezak }
120 1.1 brezak k_update_route(r);
121 1.1 brezak }
122 1.1 brezak }
123 1.1 brezak }
124 1.1 brezak
125 1.1 brezak
126 1.1 brezak /*
127 1.1 brezak * A vif has gone down -- expire all routes that have that vif as parent,
128 1.1 brezak * and update the children bitmaps in all other route entries to take into
129 1.1 brezak * account the failed vif.
130 1.1 brezak */
131 1.1 brezak void delete_vif_from_routes(vifi)
132 1.1 brezak register vifi_t vifi;
133 1.1 brezak {
134 1.1 brezak register struct rtentry *r;
135 1.1 brezak
136 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
137 1.1 brezak if (r->rt_metric != UNREACHABLE) {
138 1.1 brezak if (vifi == r->rt_parent) {
139 1.1 brezak k_del_route(r);
140 1.1 brezak r->rt_timer = ROUTE_EXPIRE_TIME;
141 1.1 brezak r->rt_metric = UNREACHABLE;
142 1.1 brezak r->rt_flags |= RTF_CHANGED;
143 1.1 brezak routes_changed = TRUE;
144 1.1 brezak }
145 1.1 brezak else if (VIFM_ISSET(vifi, r->rt_children)) {
146 1.1 brezak VIFM_CLR(vifi, r->rt_children);
147 1.1 brezak VIFM_CLR(vifi, r->rt_leaves);
148 1.1 brezak r->rt_subordinates[vifi] = 0;
149 1.1 brezak r->rt_leaf_timers [vifi] = 0;
150 1.1 brezak k_update_route(r);
151 1.1 brezak }
152 1.1 brezak else {
153 1.1 brezak r->rt_dominants[vifi] = 0;
154 1.1 brezak }
155 1.1 brezak }
156 1.1 brezak }
157 1.1 brezak }
158 1.1 brezak
159 1.1 brezak
160 1.1 brezak /*
161 1.1 brezak * A neighbor has failed or become unreachable. If that neighbor was
162 1.1 brezak * considered a dominant or subordinate router in any route entries,
163 1.1 brezak * take appropriate action.
164 1.1 brezak */
165 1.1 brezak void delete_neighbor_from_routes(addr, vifi)
166 1.1 brezak register u_long addr;
167 1.1 brezak register vifi_t vifi;
168 1.1 brezak {
169 1.1 brezak register struct rtentry *r;
170 1.1 brezak register struct uvif *v;
171 1.1 brezak
172 1.1 brezak v = &uvifs[vifi];
173 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
174 1.1 brezak if (r->rt_metric != UNREACHABLE) {
175 1.1 brezak if (r->rt_dominants[vifi] == addr) {
176 1.1 brezak VIFM_SET(vifi, r->rt_children);
177 1.1 brezak r->rt_dominants [vifi] = 0;
178 1.1 brezak r->rt_subordinates[vifi] = 0;
179 1.1 brezak if (v->uv_neighbors == NULL) {
180 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
181 1.1 brezak r->rt_leaf_timers[vifi] = 0;
182 1.1 brezak }
183 1.1 brezak else {
184 1.1 brezak VIFM_CLR(vifi, r->rt_leaves);
185 1.1 brezak r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
186 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
187 1.1 brezak }
188 1.1 brezak k_update_route(r);
189 1.1 brezak }
190 1.1 brezak else if (r->rt_subordinates[vifi] == addr) {
191 1.1 brezak r->rt_subordinates[vifi] = 0;
192 1.1 brezak if (v->uv_neighbors == NULL) {
193 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
194 1.1 brezak k_update_route(r);
195 1.1 brezak }
196 1.1 brezak else {
197 1.1 brezak r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
198 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
199 1.1 brezak }
200 1.1 brezak }
201 1.1 brezak else if (v->uv_neighbors == NULL &&
202 1.1 brezak r->rt_leaf_timers[vifi] != 0) {
203 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
204 1.1 brezak r->rt_leaf_timers[vifi] = 0;
205 1.1 brezak k_update_route(r);
206 1.1 brezak }
207 1.1 brezak }
208 1.1 brezak }
209 1.1 brezak }
210 1.1 brezak
211 1.1 brezak
212 1.1 brezak /*
213 1.1 brezak * Prepare for a sequence of ordered route updates by initializing a pointer
214 1.1 brezak * to the start of the routing table. The pointer is used to remember our
215 1.1 brezak * position in the routing table in order to avoid searching from the
216 1.1 brezak * beginning for each update; this relies on having the route reports in
217 1.1 brezak * a single message be in the same order as the route entries in the routing
218 1.1 brezak * table.
219 1.1 brezak */
220 1.1 brezak void start_route_updates()
221 1.1 brezak {
222 1.1 brezak rtp = (struct rtentry *)&routing_table;
223 1.1 brezak }
224 1.1 brezak
225 1.1 brezak
226 1.1 brezak /*
227 1.1 brezak * Starting at the route entry following the one to which 'rtp' points,
228 1.1 brezak * look for a route entry matching the specified origin and mask. If a
229 1.1 brezak * match is found, return TRUE and leave 'rtp' pointing at the found entry.
230 1.1 brezak * If no match is found, return FALSE and leave 'rtp' pointing to the route
231 1.1 brezak * entry preceding the point at which the new origin should be inserted.
232 1.1 brezak * This code is optimized for the normal case in which the first entry to
233 1.1 brezak * be examined is the matching entry.
234 1.1 brezak */
235 1.1 brezak static int find_route(origin, mask)
236 1.1 brezak register u_long origin, mask;
237 1.1 brezak {
238 1.1 brezak register struct rtentry *r;
239 1.1 brezak
240 1.1 brezak r = rtp->rt_next;
241 1.1 brezak while (r != NULL) {
242 1.1 brezak if (origin == r->rt_origin && mask == r->rt_originmask) {
243 1.1 brezak rtp = r;
244 1.1 brezak return (TRUE);
245 1.1 brezak }
246 1.1 brezak if (ntohl(mask) > ntohl(r->rt_originmask) ||
247 1.1 brezak (mask == r->rt_originmask &&
248 1.1 brezak ntohl(origin) > ntohl(r->rt_origin))) {
249 1.1 brezak rtp = r;
250 1.1 brezak r = r->rt_next;
251 1.1 brezak }
252 1.1 brezak else break;
253 1.1 brezak }
254 1.1 brezak return (FALSE);
255 1.1 brezak }
256 1.1 brezak
257 1.1 brezak
258 1.1 brezak /*
259 1.1 brezak * Search the entire routing table, looking for an entry which conflicts
260 1.1 brezak * with the given origin and mask, for example, an entry which has the same
261 1.1 brezak * origin under a different mask. If a conflicting entry is found, return
262 1.1 brezak * a pointer to the entry preceding it (to facilitate deletion); if no
263 1.1 brezak * conflict is found, return NULL.
264 1.1 brezak */
265 1.1 brezak static struct rtentry *find_conflicting_route(origin, mask)
266 1.1 brezak register u_long origin, mask;
267 1.1 brezak {
268 1.1 brezak register struct rtentry *r, *prev_r;
269 1.1 brezak
270 1.1 brezak for (prev_r = (struct rtentry *)&routing_table, r = routing_table;
271 1.1 brezak r != NULL;
272 1.1 brezak prev_r = r, r = r->rt_next ) {
273 1.1 brezak if ((origin & r->rt_originmask) == r->rt_origin ||
274 1.1 brezak (r->rt_origin & mask) == origin) {
275 1.1 brezak return (prev_r);
276 1.1 brezak }
277 1.1 brezak }
278 1.1 brezak return (NULL);
279 1.1 brezak }
280 1.1 brezak
281 1.1 brezak
282 1.1 brezak /*
283 1.1 brezak * Create a new routing table entry for the specified origin and link it into
284 1.1 brezak * the routing table. The shared variable 'rtp' is assumed to point to the
285 1.1 brezak * routing entry after which the new one should be inserted. It is left
286 1.1 brezak * pointing to the new entry.
287 1.1 brezak *
288 1.1 brezak * Only the origin, originmask, originwidth and flags fields are initialized
289 1.1 brezak * in the new route entry; the caller is responsible for filling in the the
290 1.1 brezak * rest.
291 1.1 brezak */
292 1.1 brezak static void create_route(origin, mask)
293 1.1 brezak u_long origin, mask;
294 1.1 brezak {
295 1.1 brezak register struct rtentry *r;
296 1.1 brezak
297 1.1 brezak if ((r = (struct rtentry *) malloc(sizeof(struct rtentry)
298 1.1 brezak + (3 * numvifs * sizeof(u_long)))) == NULL) {
299 1.1 brezak log(LOG_ERR, 0, "ran out of memory"); /* fatal */
300 1.1 brezak }
301 1.1 brezak r->rt_origin = origin;
302 1.1 brezak r->rt_originmask = mask;
303 1.1 brezak if (((char *)&mask)[3] != 0) r->rt_originwidth = 4;
304 1.1 brezak else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3;
305 1.1 brezak else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2;
306 1.1 brezak else r->rt_originwidth = 1;
307 1.1 brezak r->rt_flags = 0;
308 1.1 brezak r->rt_dominants = (u_long *)(r + 1);
309 1.1 brezak r->rt_subordinates = (u_long *)(r->rt_dominants + numvifs);
310 1.1 brezak r->rt_leaf_timers = (u_long *)(r->rt_subordinates + numvifs);
311 1.1 brezak
312 1.1 brezak r->rt_next = rtp->rt_next;
313 1.1 brezak rtp->rt_next = r;
314 1.1 brezak rtp = r;
315 1.1 brezak ++nroutes;
316 1.1 brezak }
317 1.1 brezak
318 1.1 brezak
319 1.1 brezak /*
320 1.1 brezak * Discard the routing table entry following the one to which 'prev_r' points.
321 1.1 brezak */
322 1.1 brezak static void discard_route(prev_r)
323 1.1 brezak register struct rtentry *prev_r;
324 1.1 brezak {
325 1.1 brezak register struct rtentry *r;
326 1.1 brezak
327 1.1 brezak r = prev_r->rt_next;
328 1.1 brezak prev_r->rt_next = r->rt_next;
329 1.1 brezak free((char *)r);
330 1.1 brezak --nroutes;
331 1.1 brezak }
332 1.1 brezak
333 1.1 brezak
334 1.1 brezak /*
335 1.1 brezak * Process a route report for a single origin, creating or updating the
336 1.1 brezak * corresponding routing table entry if necessary. 'src' is either the
337 1.1 brezak * address of a neighboring router from which the report arrived, or zero
338 1.1 brezak * to indicate a change of status of one of our own interfaces.
339 1.1 brezak */
340 1.1 brezak void update_route(origin, mask, metric, src, vifi)
341 1.1 brezak u_long origin, mask;
342 1.1 brezak int metric;
343 1.1 brezak u_long src;
344 1.1 brezak vifi_t vifi;
345 1.1 brezak {
346 1.1 brezak register struct rtentry *r;
347 1.1 brezak struct rtentry *prev_r;
348 1.1 brezak int adj_metric;
349 1.1 brezak
350 1.1 brezak /*
351 1.1 brezak * Compute an adjusted metric, taking into account the cost of the
352 1.1 brezak * subnet or tunnel over which the report arrived, and normalizing
353 1.1 brezak * all unreachable/poisoned metrics into a single value.
354 1.1 brezak */
355 1.1 brezak if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
356 1.1 brezak log(LOG_WARNING, 0,
357 1.1 brezak "%s reports out-of-range metric %u for origin %s",
358 1.1 brezak inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2));
359 1.1 brezak return;
360 1.1 brezak }
361 1.1 brezak adj_metric = metric + uvifs[vifi].uv_metric;
362 1.1 brezak if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
363 1.1 brezak
364 1.1 brezak /*
365 1.1 brezak * Look up the reported origin in the routing table.
366 1.1 brezak */
367 1.1 brezak if (!find_route(origin, mask)) {
368 1.1 brezak /*
369 1.1 brezak * Not found.
370 1.1 brezak * Don't create a new entry if the report says it's unreachable,
371 1.1 brezak * or if the reported origin and mask are invalid.
372 1.1 brezak */
373 1.1 brezak if (adj_metric == UNREACHABLE) {
374 1.1 brezak return;
375 1.1 brezak }
376 1.1 brezak if (src != 0 && !inet_valid_subnet(origin, mask)) {
377 1.1 brezak log(LOG_WARNING, 0,
378 1.1 brezak "%s reports an invalid origin (%s) and/or mask (%08x)",
379 1.1 brezak inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
380 1.1 brezak return;
381 1.1 brezak }
382 1.1 brezak
383 1.1 brezak /*
384 1.1 brezak * If the new origin and mask are inconsistent with an entry
385 1.1 brezak * already in the routing table, either ignore this update
386 1.1 brezak * (if it came from another router), or delete the conflicting
387 1.1 brezak * entry (if the update is for a directly-connected subnet).
388 1.1 brezak */
389 1.1 brezak if ((prev_r = find_conflicting_route(origin, mask)) != NULL ) {
390 1.1 brezak if (src != 0) {
391 1.1 brezak log(LOG_WARNING, 0,
392 1.1 brezak "%s reports a conflicting origin (%s) and mask (%08x)",
393 1.1 brezak inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
394 1.1 brezak return;
395 1.1 brezak }
396 1.1 brezak else {
397 1.1 brezak r = prev_r->rt_next;
398 1.1 brezak log(LOG_WARNING, 0,
399 1.1 brezak "deleting route with conflicting origin (%s), mask (%08x)",
400 1.1 brezak inet_fmt(r->rt_origin, s1), ntohl(r->rt_originmask));
401 1.1 brezak
402 1.1 brezak if (r->rt_metric != UNREACHABLE) {
403 1.1 brezak k_del_route(r);
404 1.1 brezak }
405 1.1 brezak discard_route(prev_r);
406 1.1 brezak if (rtp == r) rtp = prev_r;
407 1.1 brezak }
408 1.1 brezak }
409 1.1 brezak
410 1.1 brezak /*
411 1.1 brezak * OK, create the new routing entry. 'rtp' will be left pointing
412 1.1 brezak * to the new entry.
413 1.1 brezak */
414 1.1 brezak create_route(origin, mask);
415 1.1 brezak
416 1.1 brezak rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
417 1.1 brezak }
418 1.1 brezak
419 1.1 brezak /*
420 1.1 brezak * We now have a routing entry for the reported origin. Update it?
421 1.1 brezak */
422 1.1 brezak r = rtp;
423 1.1 brezak if (r->rt_metric == UNREACHABLE) {
424 1.1 brezak /*
425 1.1 brezak * The routing entry is for a formerly-unreachable or new origin.
426 1.1 brezak * If the report claims reachability, update the entry to use
427 1.1 brezak * the reported route.
428 1.1 brezak */
429 1.1 brezak if (adj_metric == UNREACHABLE)
430 1.1 brezak return;
431 1.1 brezak
432 1.1 brezak r->rt_parent = vifi;
433 1.1 brezak init_children_and_leaves(r, vifi);
434 1.1 brezak k_add_route(r);
435 1.1 brezak r->rt_gateway = src;
436 1.1 brezak r->rt_timer = 0;
437 1.1 brezak r->rt_metric = adj_metric;
438 1.1 brezak r->rt_flags |= RTF_CHANGED;
439 1.1 brezak routes_changed = TRUE;
440 1.1 brezak }
441 1.1 brezak else if (src == r->rt_gateway) {
442 1.1 brezak /*
443 1.1 brezak * The report has come either from the interface directly-connected
444 1.1 brezak * to the origin subnet (src and r->rt_gateway both equal zero) or
445 1.1 brezak * from the gateway we have chosen as the best first-hop gateway back
446 1.1 brezak * towards the origin (src and r->rt_gateway not equal zero). Reset
447 1.1 brezak * the route timer and, if the reported metric has changed, update
448 1.1 brezak * our entry accordingly.
449 1.1 brezak */
450 1.1 brezak r->rt_timer = 0;
451 1.1 brezak if (adj_metric == r->rt_metric)
452 1.1 brezak return;
453 1.1 brezak
454 1.1 brezak if (adj_metric == UNREACHABLE) {
455 1.1 brezak k_del_route(r);
456 1.1 brezak r->rt_timer = ROUTE_EXPIRE_TIME;
457 1.1 brezak }
458 1.1 brezak else if (adj_metric < r->rt_metric) {
459 1.1 brezak if (init_children_and_leaves(r, vifi)) {
460 1.1 brezak k_update_route(r);
461 1.1 brezak }
462 1.1 brezak }
463 1.1 brezak r->rt_metric = adj_metric;
464 1.1 brezak r->rt_flags |= RTF_CHANGED;
465 1.1 brezak routes_changed = TRUE;
466 1.1 brezak }
467 1.1 brezak else if (src == 0 ||
468 1.1 brezak (r->rt_gateway != 0 &&
469 1.1 brezak (adj_metric < r->rt_metric ||
470 1.1 brezak (adj_metric == r->rt_metric &&
471 1.1 brezak r->rt_timer >= ROUTE_SWITCH_TIME)))) {
472 1.1 brezak /*
473 1.1 brezak * The report is for an origin we consider reachable; the report
474 1.1 brezak * comes either from one of our own interfaces or from a gateway
475 1.1 brezak * other than the one we have chosen as the best first-hop gateway
476 1.1 brezak * back towards the origin. If the source of the update is one of
477 1.1 brezak * our own interfaces, or if the origin is not a directly-connected
478 1.1 brezak * subnet and the reported metric for that origin is better than
479 1.1 brezak * what our routing entry says, update the entry to use the new
480 1.1 brezak * gateway and metric. We also switch gateways if the reported
481 1.1 brezak * metric is the same as the one in the route entry and the gateway
482 1.1 brezak * associated with the route entry has not been heard from recently.
483 1.1 brezak * Did you get all that?
484 1.1 brezak */
485 1.1 brezak if (r->rt_parent != vifi || adj_metric < r->rt_metric) {
486 1.1 brezak r->rt_parent = vifi;
487 1.1 brezak if (init_children_and_leaves(r, vifi)) {
488 1.1 brezak k_update_route(r);
489 1.1 brezak }
490 1.1 brezak }
491 1.1 brezak r->rt_gateway = src;
492 1.1 brezak r->rt_timer = 0;
493 1.1 brezak r->rt_metric = adj_metric;
494 1.1 brezak r->rt_flags |= RTF_CHANGED;
495 1.1 brezak routes_changed = TRUE;
496 1.1 brezak }
497 1.1 brezak else if (vifi != r->rt_parent) {
498 1.1 brezak /*
499 1.1 brezak * The report came from a vif other than the route's parent vif.
500 1.1 brezak * Update the children and leaf info, if necessary.
501 1.1 brezak */
502 1.1 brezak if (VIFM_ISSET(vifi, r->rt_children)) {
503 1.1 brezak /*
504 1.1 brezak * Vif is a child vif for this route.
505 1.1 brezak */
506 1.1 brezak if (metric < r->rt_metric ||
507 1.1 brezak (metric == r->rt_metric &&
508 1.1 brezak ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
509 1.1 brezak /*
510 1.1 brezak * Neighbor has lower metric to origin (or has same metric
511 1.1 brezak * and lower IP address) -- it becomes the dominant router,
512 1.1 brezak * and vif is no longer a child for me.
513 1.1 brezak */
514 1.1 brezak VIFM_CLR(vifi, r->rt_children);
515 1.1 brezak VIFM_CLR(vifi, r->rt_leaves);
516 1.1 brezak r->rt_dominants [vifi] = src;
517 1.1 brezak r->rt_subordinates[vifi] = 0;
518 1.1 brezak r->rt_leaf_timers [vifi] = 0;
519 1.1 brezak k_update_route(r);
520 1.1 brezak }
521 1.1 brezak else if (metric > UNREACHABLE) { /* "poisoned reverse" */
522 1.1 brezak /*
523 1.1 brezak * Neighbor considers this vif to be on path to route's
524 1.1 brezak * origin; if no subordinate recorded, record this neighbor
525 1.1 brezak * as subordinate and clear the leaf flag.
526 1.1 brezak */
527 1.1 brezak if (r->rt_subordinates[vifi] == 0) {
528 1.1 brezak VIFM_CLR(vifi, r->rt_leaves);
529 1.1 brezak r->rt_subordinates[vifi] = src;
530 1.1 brezak r->rt_leaf_timers [vifi] = 0;
531 1.1 brezak k_update_route(r);
532 1.1 brezak }
533 1.1 brezak }
534 1.1 brezak else if (src == r->rt_subordinates[vifi]) {
535 1.1 brezak /*
536 1.1 brezak * Current subordinate no longer considers this vif to be on
537 1.1 brezak * path to route's origin; it is no longer a subordinate
538 1.1 brezak * router, and we set the leaf confirmation timer to give
539 1.1 brezak * us time to hear from other subordinates.
540 1.1 brezak */
541 1.1 brezak r->rt_subordinates[vifi] = 0;
542 1.1 brezak if (uvifs[vifi].uv_neighbors == NULL ||
543 1.1 brezak uvifs[vifi].uv_neighbors->al_next == NULL) {
544 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
545 1.1 brezak k_update_route(r);
546 1.1 brezak }
547 1.1 brezak else {
548 1.1 brezak r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME;
549 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
550 1.1 brezak }
551 1.1 brezak }
552 1.1 brezak
553 1.1 brezak }
554 1.1 brezak else if (src == r->rt_dominants[vifi] &&
555 1.1 brezak (metric > r->rt_metric ||
556 1.1 brezak (metric == r->rt_metric &&
557 1.1 brezak ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
558 1.1 brezak /*
559 1.1 brezak * Current dominant no longer has a lower metric to origin
560 1.1 brezak * (or same metric and lower IP address); we adopt the vif
561 1.1 brezak * as our own child.
562 1.1 brezak */
563 1.1 brezak VIFM_SET(vifi, r->rt_children);
564 1.1 brezak r->rt_dominants [vifi] = 0;
565 1.1 brezak if (metric > UNREACHABLE) {
566 1.1 brezak r->rt_subordinates[vifi] = src;
567 1.1 brezak }
568 1.1 brezak else if (uvifs[vifi].uv_neighbors == NULL ||
569 1.1 brezak uvifs[vifi].uv_neighbors->al_next == NULL) {
570 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
571 1.1 brezak }
572 1.1 brezak else {
573 1.1 brezak r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
574 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
575 1.1 brezak }
576 1.1 brezak k_update_route(r);
577 1.1 brezak }
578 1.1 brezak }
579 1.1 brezak }
580 1.1 brezak
581 1.1 brezak
582 1.1 brezak /*
583 1.1 brezak * On every timer interrupt, advance the timer in each routing entry.
584 1.1 brezak */
585 1.1 brezak void age_routes()
586 1.1 brezak {
587 1.1 brezak register struct rtentry *r;
588 1.1 brezak register struct rtentry *prev_r;
589 1.1 brezak register vifi_t vifi;
590 1.1 brezak
591 1.1 brezak for (prev_r = (struct rtentry *)&routing_table, r = routing_table;
592 1.1 brezak r != NULL;
593 1.1 brezak prev_r = r, r = r->rt_next) {
594 1.1 brezak
595 1.1 brezak if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) {
596 1.1 brezak /*
597 1.1 brezak * Route is still good; see if any leaf timers need to be
598 1.1 brezak * advanced.
599 1.1 brezak */
600 1.1 brezak if (r->rt_flags & RTF_LEAF_TIMING) {
601 1.1 brezak r->rt_flags &= ~RTF_LEAF_TIMING;
602 1.1 brezak for (vifi = 0; vifi < numvifs; ++vifi) {
603 1.1 brezak if (r->rt_leaf_timers[vifi] != 0) {
604 1.1 brezak /*
605 1.1 brezak * Unlike other timers, leaf timers decrement.
606 1.1 brezak */
607 1.1 brezak if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){
608 1.1 brezak VIFM_SET(vifi, r->rt_leaves);
609 1.1 brezak k_update_route(r);
610 1.1 brezak }
611 1.1 brezak else {
612 1.1 brezak r->rt_flags |= RTF_LEAF_TIMING;
613 1.1 brezak }
614 1.1 brezak }
615 1.1 brezak }
616 1.1 brezak }
617 1.1 brezak }
618 1.1 brezak else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
619 1.1 brezak /*
620 1.1 brezak * Time to garbage-collect the route entry.
621 1.1 brezak */
622 1.1 brezak discard_route(prev_r);
623 1.1 brezak r = prev_r;
624 1.1 brezak }
625 1.1 brezak else if (r->rt_metric != UNREACHABLE) {
626 1.1 brezak /*
627 1.1 brezak * Time to expire the route entry. If the gateway is zero,
628 1.1 brezak * i.e., it is a route to a directly-connected subnet, just
629 1.1 brezak * set the timer back to zero; such routes expire only when
630 1.1 brezak * the interface to the subnet goes down.
631 1.1 brezak */
632 1.1 brezak if (r->rt_gateway == 0) {
633 1.1 brezak r->rt_timer = 0;
634 1.1 brezak }
635 1.1 brezak else {
636 1.1 brezak k_del_route(r);
637 1.1 brezak r->rt_metric = UNREACHABLE;
638 1.1 brezak r->rt_flags |= RTF_CHANGED;
639 1.1 brezak routes_changed = TRUE;
640 1.1 brezak }
641 1.1 brezak }
642 1.1 brezak }
643 1.1 brezak }
644 1.1 brezak
645 1.1 brezak
646 1.1 brezak /*
647 1.1 brezak * Mark all routes as unreachable. This function is called only from
648 1.1 brezak * hup() in preparation for informing all neighbors that we are going
649 1.1 brezak * off the air. For consistency, we ought also to delete all reachable
650 1.1 brezak * route entries from the kernel, but since we are about to exit we rely
651 1.1 brezak * on the kernel to do its own cleanup -- no point in making all those
652 1.1 brezak * expensive kernel calls now.
653 1.1 brezak */
654 1.1 brezak void expire_all_routes()
655 1.1 brezak {
656 1.1 brezak register struct rtentry *r;
657 1.1 brezak
658 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
659 1.1 brezak r->rt_metric = UNREACHABLE;
660 1.1 brezak r->rt_flags |= RTF_CHANGED;
661 1.1 brezak routes_changed = TRUE;
662 1.1 brezak }
663 1.1 brezak }
664 1.1 brezak
665 1.1 brezak
666 1.1 brezak /*
667 1.1 brezak * Process an incoming neighbor probe message.
668 1.1 brezak */
669 1.1 brezak void accept_probe(src, dst)
670 1.1 brezak u_long src, dst;
671 1.1 brezak {
672 1.1 brezak vifi_t vifi;
673 1.1 brezak
674 1.1 brezak if ((vifi = find_vif(src, dst)) == NO_VIF) {
675 1.1 brezak log(LOG_INFO, 0,
676 1.1 brezak "ignoring probe from non-neighbor %s", inet_fmt(src, s1));
677 1.1 brezak return;
678 1.1 brezak }
679 1.1 brezak
680 1.1 brezak if (!update_neighbor(vifi, src, DVMRP_PROBE))
681 1.1 brezak return;
682 1.1 brezak
683 1.1 brezak report(ALL_ROUTES, vifi, src);
684 1.1 brezak }
685 1.1 brezak
686 1.1 brezak
687 1.1 brezak /*
688 1.1 brezak * Process an incoming route report message.
689 1.1 brezak */
690 1.1 brezak void accept_report(src, dst, p, datalen)
691 1.1 brezak u_long src, dst;
692 1.1 brezak register char *p;
693 1.1 brezak register int datalen;
694 1.1 brezak {
695 1.1 brezak vifi_t vifi;
696 1.1 brezak register int width, i;
697 1.1 brezak int metric;
698 1.1 brezak u_long mask;
699 1.1 brezak u_long origin;
700 1.1 brezak
701 1.1 brezak if ((vifi = find_vif(src, dst)) == NO_VIF) {
702 1.1 brezak log(LOG_INFO, 0,
703 1.1 brezak "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
704 1.1 brezak return;
705 1.1 brezak }
706 1.1 brezak
707 1.1 brezak if (!update_neighbor(vifi, src, DVMRP_REPORT))
708 1.1 brezak return;
709 1.1 brezak
710 1.1 brezak start_route_updates();
711 1.1 brezak
712 1.1 brezak while (datalen > 0) { /* Loop through per-mask lists. */
713 1.1 brezak
714 1.1 brezak if (datalen < 3) {
715 1.1 brezak log(LOG_WARNING, 0,
716 1.1 brezak "received truncated route report from %s", inet_fmt(src, s1));
717 1.1 brezak return;
718 1.1 brezak }
719 1.1 brezak ((char *)&mask)[0] = 0xff; width = 1;
720 1.1 brezak if ((((char *)&mask)[1] = *p++) != 0) width = 2;
721 1.1 brezak if ((((char *)&mask)[2] = *p++) != 0) width = 3;
722 1.1 brezak if ((((char *)&mask)[3] = *p++) != 0) width = 4;
723 1.1 brezak datalen -= 3;
724 1.1 brezak
725 1.1 brezak do { /* Loop through (origin, metric) pairs */
726 1.1 brezak
727 1.1 brezak if (datalen < width + 1) {
728 1.1 brezak log(LOG_WARNING, 0,
729 1.1 brezak "received truncated route report from %s", inet_fmt(src, s1));
730 1.1 brezak return;
731 1.1 brezak }
732 1.1 brezak origin = 0;
733 1.1 brezak for (i = 0; i < width; ++i)
734 1.1 brezak ((char *)&origin)[i] = *p++;
735 1.1 brezak metric = *p++;
736 1.1 brezak datalen -= width + 1;
737 1.1 brezak
738 1.1 brezak update_route(origin, mask, (metric & 0x7f), src, vifi);
739 1.1 brezak
740 1.1 brezak } while (!(metric & 0x80));
741 1.1 brezak }
742 1.1 brezak
743 1.1 brezak if (routes_changed && !delay_change_reports)
744 1.1 brezak report_to_all_neighbors(CHANGED_ROUTES);
745 1.1 brezak }
746 1.1 brezak
747 1.1 brezak
748 1.1 brezak /*
749 1.1 brezak * Send a route report message to destination 'dst', via virtual interface
750 1.1 brezak * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
751 1.1 brezak */
752 1.1 brezak void report(which_routes, vifi, dst)
753 1.1 brezak int which_routes;
754 1.1 brezak vifi_t vifi;
755 1.1 brezak u_long dst;
756 1.1 brezak {
757 1.1 brezak register struct rtentry *r;
758 1.1 brezak register char *p;
759 1.1 brezak register int i;
760 1.1 brezak int datalen;
761 1.1 brezak int width;
762 1.1 brezak u_long mask;
763 1.1 brezak u_long src;
764 1.1 brezak
765 1.1 brezak src = uvifs[vifi].uv_lcl_addr;
766 1.1 brezak
767 1.1 brezak p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
768 1.1 brezak datalen = 0;
769 1.1 brezak mask = 0;
770 1.1 brezak
771 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
772 1.1 brezak
773 1.1 brezak if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
774 1.1 brezak continue;
775 1.1 brezak
776 1.1 brezak /*
777 1.1 brezak * If there is no room for this route in the current message,
778 1.1 brezak * send the message and start a new one.
779 1.1 brezak */
780 1.1 brezak if (datalen + ((r->rt_originmask == mask) ?
781 1.1 brezak (width + 1) :
782 1.1 brezak (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
783 1.1 brezak *(p-1) |= 0x80;
784 1.1 brezak send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
785 1.1 brezak htonl(MROUTED_LEVEL), datalen);
786 1.1 brezak
787 1.1 brezak p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
788 1.1 brezak datalen = 0;
789 1.1 brezak mask = 0;
790 1.1 brezak }
791 1.1 brezak
792 1.1 brezak if(r->rt_originmask != mask) {
793 1.1 brezak mask = r->rt_originmask;
794 1.1 brezak width = r->rt_originwidth;
795 1.1 brezak if (datalen != 0) *(p-1) |= 0x80;
796 1.1 brezak *p++ = ((char *)&mask)[1];
797 1.1 brezak *p++ = ((char *)&mask)[2];
798 1.1 brezak *p++ = ((char *)&mask)[3];
799 1.1 brezak datalen += 3;
800 1.1 brezak }
801 1.1 brezak
802 1.1 brezak for (i = 0; i < width; ++i)
803 1.1 brezak *p++ = ((char *)&(r->rt_origin))[i];
804 1.1 brezak
805 1.1 brezak *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
806 1.1 brezak (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
807 1.1 brezak (char)(r->rt_metric);
808 1.1 brezak
809 1.1 brezak datalen += width + 1;
810 1.1 brezak }
811 1.1 brezak
812 1.1 brezak if (datalen != 0) {
813 1.1 brezak *(p-1) |= 0x80;
814 1.1 brezak send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
815 1.1 brezak htonl(MROUTED_LEVEL), datalen);
816 1.1 brezak }
817 1.1 brezak }
818 1.1 brezak
819 1.1 brezak
820 1.1 brezak /*
821 1.1 brezak * Send a route report message to all neighboring routers.
822 1.1 brezak * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
823 1.1 brezak */
824 1.1 brezak void report_to_all_neighbors(which_routes)
825 1.1 brezak int which_routes;
826 1.1 brezak {
827 1.1 brezak register vifi_t vifi;
828 1.1 brezak register struct uvif *v;
829 1.1 brezak register struct rtentry *r;
830 1.1 brezak int routes_changed_before;
831 1.1 brezak
832 1.1 brezak /*
833 1.1 brezak * Remember the state of the global routes_changed flag before
834 1.1 brezak * generating the reports, and clear the flag.
835 1.1 brezak */
836 1.1 brezak routes_changed_before = routes_changed;
837 1.1 brezak routes_changed = FALSE;
838 1.1 brezak
839 1.1 brezak
840 1.1 brezak for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
841 1.1 brezak if (v->uv_neighbors != NULL) {
842 1.1 brezak report(which_routes, vifi,
843 1.1 brezak (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
844 1.1 brezak : dvmrp_group);
845 1.1 brezak }
846 1.1 brezak }
847 1.1 brezak
848 1.1 brezak /*
849 1.1 brezak * If there were changed routes before we sent the reports AND
850 1.1 brezak * if no new changes occurred while sending the reports, clear
851 1.1 brezak * the change flags in the individual route entries. If changes
852 1.1 brezak * did occur while sending the reports, new reports will be
853 1.1 brezak * generated at the next timer interrupt.
854 1.1 brezak */
855 1.1 brezak if (routes_changed_before && !routes_changed) {
856 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
857 1.1 brezak r->rt_flags &= ~RTF_CHANGED;
858 1.1 brezak }
859 1.1 brezak }
860 1.1 brezak
861 1.1 brezak /*
862 1.1 brezak * Set a flag to inhibit further reports of changed routes until the
863 1.1 brezak * next timer interrupt. This is to alleviate update storms.
864 1.1 brezak */
865 1.1 brezak delay_change_reports = TRUE;
866 1.1 brezak }
867 1.1 brezak
868 1.1 brezak
869 1.1 brezak /*
870 1.1 brezak * Print the contents of the routing table on file 'fp'.
871 1.1 brezak */
872 1.1 brezak void dump_routes(fp)
873 1.1 brezak FILE *fp;
874 1.1 brezak {
875 1.1 brezak register struct rtentry *r;
876 1.1 brezak register int i;
877 1.1 brezak
878 1.1 brezak fprintf(fp,
879 1.1 brezak "Multicast Routing Table (%u %s)\n%s",
880 1.1 brezak nroutes, (nroutes == 1) ? "entry" : "entries",
881 1.1 brezak " Origin-Subnet From-Gateway Metric In-Vif Out-Vifs\n");
882 1.1 brezak
883 1.1 brezak for (r = routing_table; r != NULL; r = r->rt_next) {
884 1.1 brezak
885 1.1 brezak fprintf(fp, " %-15s %-15s ",
886 1.1 brezak inet_fmts(r->rt_origin, r->rt_originmask, s1),
887 1.1 brezak (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
888 1.1 brezak
889 1.1 brezak fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
890 1.1 brezak r->rt_metric);
891 1.1 brezak
892 1.1 brezak fprintf(fp, "%7u ",
893 1.1 brezak r->rt_parent);
894 1.1 brezak
895 1.1 brezak for (i = 0; i < numvifs; ++i) {
896 1.1 brezak if (VIFM_ISSET(i, r->rt_children)) {
897 1.1 brezak fprintf(fp, " %u%c",
898 1.1 brezak i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
899 1.1 brezak }
900 1.1 brezak }
901 1.1 brezak fprintf(fp, "\n");
902 1.1 brezak }
903 1.1 brezak fprintf(fp, "\n");
904 1.1 brezak }
905