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