Annotation of embedaddon/bird/proto/static/static.c, revision 1.1.1.1
1.1 misho 1: /*
2: * BIRD -- Static Route Generator
3: *
4: * (c) 1998--2000 Martin Mares <mj@ucw.cz>
5: *
6: * Can be freely distributed and used under the terms of the GNU GPL.
7: */
8:
9: /**
10: * DOC: Static
11: *
12: * The Static protocol is implemented in a straightforward way. It keeps
13: * two lists of static routes: one containing interface routes and one
14: * holding the remaining ones. Interface routes are inserted and removed according
15: * to interface events received from the core via the if_notify() hook. Routes
16: * pointing to a neighboring router use a sticky node in the neighbor cache
17: * to be notified about gaining or losing the neighbor. Special
18: * routes like black holes or rejects are inserted all the time.
19: *
20: * Multipath routes are tricky. Because these routes depends on
21: * several neighbors we need to integrate that to the neighbor
22: * notification handling, we use dummy static_route nodes, one for
23: * each nexthop. Therefore, a multipath route consists of a master
24: * static_route node (of dest RTD_MULTIPATH), which specifies prefix
25: * and is used in most circumstances, and a list of dummy static_route
26: * nodes (of dest RTD_NONE), which stores info about nexthops and are
27: * connected to neighbor entries and neighbor notifications. Dummy
28: * nodes are chained using mp_next, they aren't in other_routes list,
29: * and abuse some fields (masklen, if_name) for other purposes.
30: *
31: * The only other thing worth mentioning is that when asked for reconfiguration,
32: * Static not only compares the two configurations, but it also calculates
33: * difference between the lists of static routes and it just inserts the
34: * newly added routes and removes the obsolete ones.
35: */
36:
37: #undef LOCAL_DEBUG
38:
39: #include "nest/bird.h"
40: #include "nest/iface.h"
41: #include "nest/protocol.h"
42: #include "nest/route.h"
43: #include "nest/cli.h"
44: #include "conf/conf.h"
45: #include "filter/filter.h"
46: #include "lib/string.h"
47: #include "lib/alloca.h"
48:
49: #include "static.h"
50:
51: static linpool *static_lp;
52:
53: static inline rtable *
54: p_igp_table(struct proto *p)
55: {
56: struct static_config *cf = (void *) p->cf;
57: return cf->igp_table ? cf->igp_table->table : p->table;
58: }
59:
60: static void
61: static_install(struct proto *p, struct static_route *r, struct iface *ifa)
62: {
63: net *n;
64: rta a;
65: rte *e;
66:
67: if (r->installed > 0)
68: return;
69:
70: DBG("Installing static route %I/%d, rtd=%d\n", r->net, r->masklen, r->dest);
71: bzero(&a, sizeof(a));
72: a.src = p->main_source;
73: a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
74: a.scope = SCOPE_UNIVERSE;
75: a.cast = RTC_UNICAST;
76: a.dest = r->dest;
77: a.gw = r->via;
78: a.iface = ifa;
79:
80: if (r->dest == RTD_MULTIPATH)
81: {
82: struct static_route *r2;
83: struct mpnh *nhs = NULL;
84:
85: for (r2 = r->mp_next; r2; r2 = r2->mp_next)
86: if (r2->installed)
87: {
88: struct mpnh *nh = alloca(sizeof(struct mpnh));
89: nh->gw = r2->via;
90: nh->iface = r2->neigh->iface;
91: nh->weight = r2->masklen; /* really */
92: mpnh_insert(&nhs, nh);
93: }
94:
95: /* There is at least one nexthop */
96: if (!nhs->next)
97: {
98: /* Fallback to unipath route for exactly one nexthop */
99: a.dest = RTD_ROUTER;
100: a.gw = nhs->gw;
101: a.iface = nhs->iface;
102: }
103: else
104: a.nexthops = nhs;
105: }
106:
107: if (r->dest == RTDX_RECURSIVE)
108: rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
109:
110: /* We skip rta_lookup() here */
111:
112: n = net_get(p->table, r->net, r->masklen);
113: e = rte_get_temp(&a);
114: e->net = n;
115: e->pflags = 0;
116:
117: if (r->cmds)
118: f_eval_rte(r->cmds, &e, static_lp);
119:
120: rte_update(p, n, e);
121: r->installed = 1;
122:
123: if (r->cmds)
124: lp_flush(static_lp);
125: }
126:
127: static void
128: static_remove(struct proto *p, struct static_route *r)
129: {
130: net *n;
131:
132: if (!r->installed)
133: return;
134:
135: DBG("Removing static route %I/%d via %I\n", r->net, r->masklen, r->via);
136: n = net_find(p->table, r->net, r->masklen);
137: rte_update(p, n, NULL);
138: r->installed = 0;
139: }
140:
141: static void
142: static_bfd_notify(struct bfd_request *req);
143:
144: static void
145: static_update_bfd(struct proto *p, struct static_route *r)
146: {
147: struct neighbor *nb = r->neigh;
148: int bfd_up = (nb->scope > 0) && r->use_bfd;
149:
150: if (bfd_up && !r->bfd_req)
151: {
152: // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
153: r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
154: static_bfd_notify, r);
155: }
156:
157: if (!bfd_up && r->bfd_req)
158: {
159: rfree(r->bfd_req);
160: r->bfd_req = NULL;
161: }
162: }
163:
164: static int
165: static_decide(struct static_config *cf, struct static_route *r)
166: {
167: /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
168: the route also have to be valid (r->neigh != NULL) */
169:
170: if (r->neigh->scope < 0)
171: return 0;
172:
173: if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
174: return 0;
175:
176: if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
177: return 0;
178:
179: return 1;
180: }
181:
182:
183: static void
184: static_add(struct proto *p, struct static_config *cf, struct static_route *r)
185: {
186: DBG("static_add(%I/%d,%d)\n", r->net, r->masklen, r->dest);
187: switch (r->dest)
188: {
189: case RTD_ROUTER:
190: {
191: struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
192: if (n)
193: {
194: r->chain = n->data;
195: n->data = r;
196: r->neigh = n;
197:
198: static_update_bfd(p, r);
199: if (static_decide(cf, r))
200: static_install(p, r, n->iface);
201: else
202: static_remove(p, r);
203: }
204: else
205: {
206: log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
207: static_remove(p, r);
208: }
209: break;
210: }
211:
212: case RTD_DEVICE:
213: break;
214:
215: case RTD_MULTIPATH:
216: {
217: int count = 0;
218: struct static_route *r2;
219:
220: for (r2 = r->mp_next; r2; r2 = r2->mp_next)
221: {
222: struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
223: if (n)
224: {
225: r2->chain = n->data;
226: n->data = r2;
227: r2->neigh = n;
228:
229: static_update_bfd(p, r2);
230: r2->installed = static_decide(cf, r2);
231: count += r2->installed;
232: }
233: else
234: {
235: log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
236: r2->installed = 0;
237: }
238: }
239:
240: if (count)
241: static_install(p, r, NULL);
242: else
243: static_remove(p, r);
244: break;
245: }
246:
247: default:
248: static_install(p, r, NULL);
249: }
250: }
251:
252: static void
253: static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
254: {
255: struct static_route *r2;
256:
257: if (r->bfd_req)
258: {
259: rfree(r->bfd_req);
260: r->bfd_req = NULL;
261: }
262:
263: if (r->dest == RTD_MULTIPATH)
264: for (r2 = r->mp_next; r2; r2 = r2->mp_next)
265: if (r2->bfd_req)
266: {
267: rfree(r2->bfd_req);
268: r2->bfd_req = NULL;
269: }
270: }
271:
272: static int
273: static_start(struct proto *p)
274: {
275: struct static_config *cf = (void *) p->cf;
276: struct static_route *r;
277:
278: DBG("Static: take off!\n");
279:
280: if (!static_lp)
281: static_lp = lp_new(&root_pool, 1008);
282:
283: if (cf->igp_table)
284: rt_lock_table(cf->igp_table->table);
285:
286: /* We have to go UP before routes could be installed */
287: proto_notify_state(p, PS_UP);
288:
289: WALK_LIST(r, cf->other_routes)
290: static_add(p, cf, r);
291: return PS_UP;
292: }
293:
294: static int
295: static_shutdown(struct proto *p)
296: {
297: struct static_config *cf = (void *) p->cf;
298: struct static_route *r;
299:
300: /* Just reset the flag, the routes will be flushed by the nest */
301: WALK_LIST(r, cf->iface_routes)
302: r->installed = 0;
303: WALK_LIST(r, cf->other_routes)
304: {
305: static_rte_cleanup(p, r);
306: r->installed = 0;
307: }
308:
309: return PS_DOWN;
310: }
311:
312: static void
313: static_cleanup(struct proto *p)
314: {
315: struct static_config *cf = (void *) p->cf;
316:
317: if (cf->igp_table)
318: rt_unlock_table(cf->igp_table->table);
319: }
320:
321: static void
322: static_update_rte(struct proto *p, struct static_route *r)
323: {
324: switch (r->dest)
325: {
326: case RTD_ROUTER:
327: if (static_decide((struct static_config *) p->cf, r))
328: static_install(p, r, r->neigh->iface);
329: else
330: static_remove(p, r);
331: break;
332:
333: case RTD_NONE: /* a part of multipath route */
334: {
335: int decision = static_decide((struct static_config *) p->cf, r);
336: if (decision == r->installed)
337: break; /* no change */
338: r->installed = decision;
339:
340: struct static_route *r1, *r2;
341: int count = 0;
342: r1 = (void *) r->if_name; /* really */
343: for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
344: count += r2->installed;
345:
346: if (count)
347: {
348: /* Set of nexthops changed - force reinstall */
349: r1->installed = 0;
350: static_install(p, r1, NULL);
351: }
352: else
353: static_remove(p, r1);
354:
355: break;
356: }
357: }
358: }
359:
360: static void
361: static_neigh_notify(struct neighbor *n)
362: {
363: struct proto *p = n->proto;
364: struct static_route *r;
365:
366: DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
367: for(r=n->data; r; r=r->chain)
368: {
369: static_update_bfd(p, r);
370: static_update_rte(p, r);
371: }
372: }
373:
374: static void
375: static_bfd_notify(struct bfd_request *req)
376: {
377: struct static_route *r = req->data;
378: struct proto *p = r->neigh->proto;
379:
380: // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
381:
382: static_update_rte(p, r);
383: }
384:
385: static void
386: static_dump_rt(struct static_route *r)
387: {
388: debug("%-1I/%2d: ", r->net, r->masklen);
389: switch (r->dest)
390: {
391: case RTD_ROUTER:
392: debug("via %I\n", r->via);
393: break;
394: case RTD_DEVICE:
395: debug("dev %s\n", r->if_name);
396: break;
397: default:
398: debug("rtd %d\n", r->dest);
399: break;
400: }
401: }
402:
403: static void
404: static_dump(struct proto *p)
405: {
406: struct static_config *c = (void *) p->cf;
407: struct static_route *r;
408:
409: debug("Independent static routes:\n");
410: WALK_LIST(r, c->other_routes)
411: static_dump_rt(r);
412: debug("Device static routes:\n");
413: WALK_LIST(r, c->iface_routes)
414: static_dump_rt(r);
415: }
416:
417: static void
418: static_if_notify(struct proto *p, unsigned flags, struct iface *i)
419: {
420: struct static_route *r;
421: struct static_config *c = (void *) p->cf;
422:
423: if (flags & IF_CHANGE_UP)
424: {
425: WALK_LIST(r, c->iface_routes)
426: if (!strcmp(r->if_name, i->name))
427: static_install(p, r, i);
428: }
429: else if (flags & IF_CHANGE_DOWN)
430: {
431: WALK_LIST(r, c->iface_routes)
432: if (!strcmp(r->if_name, i->name))
433: static_remove(p, r);
434: }
435: }
436:
437: int
438: static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
439: {
440: return 1;
441: }
442:
443: void
444: static_init_config(struct static_config *c)
445: {
446: init_list(&c->iface_routes);
447: init_list(&c->other_routes);
448: }
449:
450: static struct proto *
451: static_init(struct proto_config *c)
452: {
453: struct proto *p = proto_new(c, sizeof(struct proto));
454:
455: p->neigh_notify = static_neigh_notify;
456: p->if_notify = static_if_notify;
457: p->rte_mergable = static_rte_mergable;
458:
459: return p;
460: }
461:
462: static inline int
463: static_same_net(struct static_route *x, struct static_route *y)
464: {
465: return ipa_equal(x->net, y->net) && (x->masklen == y->masklen);
466: }
467:
468: static inline int
469: static_same_dest(struct static_route *x, struct static_route *y)
470: {
471: if (x->dest != y->dest)
472: return 0;
473:
474: switch (x->dest)
475: {
476: case RTD_ROUTER:
477: return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
478:
479: case RTD_DEVICE:
480: return !strcmp(x->if_name, y->if_name);
481:
482: case RTD_MULTIPATH:
483: for (x = x->mp_next, y = y->mp_next;
484: x && y;
485: x = x->mp_next, y = y->mp_next)
486: if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd))
487: return 0;
488: return !x && !y;
489:
490: case RTDX_RECURSIVE:
491: return ipa_equal(x->via, y->via);
492:
493: default:
494: return 1;
495: }
496: }
497:
498: static inline int
499: static_same_rte(struct static_route *x, struct static_route *y)
500: {
501: return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
502: }
503:
504:
505: static void
506: static_match(struct proto *p, struct static_route *r, struct static_config *n)
507: {
508: struct static_route *t;
509:
510: /*
511: * For given old route *r we find whether a route to the same
512: * network is also in the new route list. In that case, we keep the
513: * route and possibly update the route later if destination changed.
514: * Otherwise, we remove the route.
515: */
516:
517: if (r->neigh)
518: r->neigh->data = NULL;
519:
520: WALK_LIST(t, n->iface_routes)
521: if (static_same_net(r, t))
522: goto found;
523:
524: WALK_LIST(t, n->other_routes)
525: if (static_same_net(r, t))
526: goto found;
527:
528: static_remove(p, r);
529: return;
530:
531: found:
532: /* If destination is different, force reinstall */
533: if ((r->installed > 0) && !static_same_rte(r, t))
534: t->installed = -1;
535: else
536: t->installed = r->installed;
537: }
538:
539: static inline rtable *
540: cf_igp_table(struct static_config *cf)
541: {
542: return cf->igp_table ? cf->igp_table->table : NULL;
543: }
544:
545: static int
546: static_reconfigure(struct proto *p, struct proto_config *new)
547: {
548: struct static_config *o = (void *) p->cf;
549: struct static_config *n = (void *) new;
550: struct static_route *r;
551:
552: if (cf_igp_table(o) != cf_igp_table(n))
553: return 0;
554:
555: /* Delete all obsolete routes and reset neighbor entries */
556: WALK_LIST(r, o->iface_routes)
557: static_match(p, r, n);
558: WALK_LIST(r, o->other_routes)
559: static_match(p, r, n);
560:
561: /* Now add all new routes, those not changed will be ignored by static_install() */
562: WALK_LIST(r, n->iface_routes)
563: {
564: struct iface *ifa;
565: if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
566: static_install(p, r, ifa);
567: }
568: WALK_LIST(r, n->other_routes)
569: static_add(p, n, r);
570:
571: WALK_LIST(r, o->other_routes)
572: static_rte_cleanup(p, r);
573:
574: return 1;
575: }
576:
577: static void
578: static_copy_routes(list *dlst, list *slst)
579: {
580: struct static_route *dr, *sr;
581:
582: init_list(dlst);
583: WALK_LIST(sr, *slst)
584: {
585: /* copy one route */
586: dr = cfg_alloc(sizeof(struct static_route));
587: memcpy(dr, sr, sizeof(struct static_route));
588:
589: /* This fn is supposed to be called on fresh src routes, which have 'live'
590: fields (like .chain, .neigh or .installed) zero, so no need to zero them */
591:
592: /* We need to copy multipath chain, because there are backptrs in 'if_name' */
593: if (dr->dest == RTD_MULTIPATH)
594: {
595: struct static_route *md, *ms, **mp_last;
596:
597: mp_last = &(dr->mp_next);
598: for (ms = sr->mp_next; ms; ms = ms->mp_next)
599: {
600: md = cfg_alloc(sizeof(struct static_route));
601: memcpy(md, ms, sizeof(struct static_route));
602: md->if_name = (void *) dr; /* really */
603:
604: *mp_last = md;
605: mp_last = &(md->mp_next);
606: }
607: *mp_last = NULL;
608: }
609:
610: add_tail(dlst, (node *) dr);
611: }
612: }
613:
614: static void
615: static_copy_config(struct proto_config *dest, struct proto_config *src)
616: {
617: struct static_config *d = (struct static_config *) dest;
618: struct static_config *s = (struct static_config *) src;
619:
620: /* Shallow copy of everything */
621: proto_copy_rest(dest, src, sizeof(struct static_config));
622:
623: /* Copy route lists */
624: static_copy_routes(&d->iface_routes, &s->iface_routes);
625: static_copy_routes(&d->other_routes, &s->other_routes);
626: }
627:
628:
629: struct protocol proto_static = {
630: .name = "Static",
631: .template = "static%d",
632: .preference = DEF_PREF_STATIC,
633: .config_size = sizeof(struct static_config),
634: .init = static_init,
635: .dump = static_dump,
636: .start = static_start,
637: .shutdown = static_shutdown,
638: .cleanup = static_cleanup,
639: .reconfigure = static_reconfigure,
640: .copy_config = static_copy_config
641: };
642:
643: static void
644: static_show_rt(struct static_route *r)
645: {
646: byte via[STD_ADDRESS_P_LENGTH + 16];
647:
648: switch (r->dest)
649: {
650: case RTD_ROUTER: bsprintf(via, "via %I%J", r->via, r->via_if); break;
651: case RTD_DEVICE: bsprintf(via, "dev %s", r->if_name); break;
652: case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
653: case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
654: case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
655: case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
656: case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
657: default: bsprintf(via, "???");
658: }
659: cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via,
660: r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
661:
662: struct static_route *r2;
663: if (r->dest == RTD_MULTIPATH)
664: for (r2 = r->mp_next; r2; r2 = r2->mp_next)
665: cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
666: r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
667: }
668:
669: void
670: static_show(struct proto *P)
671: {
672: struct static_config *c = (void *) P->cf;
673: struct static_route *r;
674:
675: WALK_LIST(r, c->other_routes)
676: static_show_rt(r);
677: WALK_LIST(r, c->iface_routes)
678: static_show_rt(r);
679: cli_msg(0, "");
680: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>