Annotation of embedaddon/bird/proto/static/static.c, revision 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>