Return to static.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / static |
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: }