Annotation of embedaddon/bird/sysdep/unix/krt.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD -- UNIX Kernel Synchronization
! 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: Kernel synchronization
! 11: *
! 12: * This system dependent module implements the Kernel and Device protocol,
! 13: * that is synchronization of interface lists and routing tables with the
! 14: * OS kernel.
! 15: *
! 16: * The whole kernel synchronization is a bit messy and touches some internals
! 17: * of the routing table engine, because routing table maintenance is a typical
! 18: * example of the proverbial compatibility between different Unices and we want
! 19: * to keep the overhead of our KRT business as low as possible and avoid maintaining
! 20: * a local routing table copy.
! 21: *
! 22: * The kernel syncer can work in three different modes (according to system config header):
! 23: * Either with a single routing table and single KRT protocol [traditional UNIX]
! 24: * or with many routing tables and separate KRT protocols for all of them
! 25: * or with many routing tables, but every scan including all tables, so we start
! 26: * separate KRT protocols which cooperate with each other [Linux].
! 27: * In this case, we keep only a single scan timer.
! 28: *
! 29: * We use FIB node flags in the routing table to keep track of route
! 30: * synchronization status. We also attach temporary &rte's to the routing table,
! 31: * but it cannot do any harm to the rest of BIRD since table synchronization is
! 32: * an atomic process.
! 33: *
! 34: * When starting up, we cheat by looking if there is another
! 35: * KRT instance to be initialized later and performing table scan
! 36: * only once for all the instances.
! 37: *
! 38: * The code uses OS-dependent parts for kernel updates and scans. These parts are
! 39: * in more specific sysdep directories (e.g. sysdep/linux) in functions krt_sys_*
! 40: * and kif_sys_* (and some others like krt_replace_rte()) and krt-sys.h header file.
! 41: * This is also used for platform specific protocol options and route attributes.
! 42: *
! 43: * There was also an old code that used traditional UNIX ioctls for these tasks.
! 44: * It was unmaintained and later removed. For reference, see sysdep/krt-* files
! 45: * in commit 396dfa9042305f62da1f56589c4b98fac57fc2f6
! 46: */
! 47:
! 48: /*
! 49: * If you are brave enough, continue now. You cannot say you haven't been warned.
! 50: */
! 51:
! 52: #undef LOCAL_DEBUG
! 53:
! 54: #include "nest/bird.h"
! 55: #include "nest/iface.h"
! 56: #include "nest/route.h"
! 57: #include "nest/protocol.h"
! 58: #include "filter/filter.h"
! 59: #include "lib/timer.h"
! 60: #include "conf/conf.h"
! 61: #include "lib/string.h"
! 62:
! 63: #include "unix.h"
! 64: #include "krt.h"
! 65:
! 66: /*
! 67: * Global resources
! 68: */
! 69:
! 70: pool *krt_pool;
! 71: static linpool *krt_filter_lp;
! 72: static list krt_proto_list;
! 73:
! 74: void
! 75: krt_io_init(void)
! 76: {
! 77: krt_pool = rp_new(&root_pool, "Kernel Syncer");
! 78: krt_filter_lp = lp_new(krt_pool, 4080);
! 79: init_list(&krt_proto_list);
! 80: krt_sys_io_init();
! 81: }
! 82:
! 83: /*
! 84: * Interfaces
! 85: */
! 86:
! 87: struct kif_proto *kif_proto;
! 88: static struct kif_config *kif_cf;
! 89: static timer *kif_scan_timer;
! 90: static bird_clock_t kif_last_shot;
! 91:
! 92: static void
! 93: kif_scan(timer *t)
! 94: {
! 95: struct kif_proto *p = t->data;
! 96:
! 97: KRT_TRACE(p, D_EVENTS, "Scanning interfaces");
! 98: kif_last_shot = now;
! 99: kif_do_scan(p);
! 100: }
! 101:
! 102: static void
! 103: kif_force_scan(void)
! 104: {
! 105: if (kif_proto && kif_last_shot + 2 < now)
! 106: {
! 107: kif_scan(kif_scan_timer);
! 108: tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
! 109: }
! 110: }
! 111:
! 112: void
! 113: kif_request_scan(void)
! 114: {
! 115: if (kif_proto && kif_scan_timer->expires > now)
! 116: tm_start(kif_scan_timer, 1);
! 117: }
! 118:
! 119: static inline int
! 120: prefer_addr(struct ifa *a, struct ifa *b)
! 121: {
! 122: int sa = a->scope > SCOPE_LINK;
! 123: int sb = b->scope > SCOPE_LINK;
! 124:
! 125: if (sa < sb)
! 126: return 0;
! 127: else if (sa > sb)
! 128: return 1;
! 129: else
! 130: return ipa_compare(a->ip, b->ip) < 0;
! 131: }
! 132:
! 133: static inline struct ifa *
! 134: find_preferred_ifa(struct iface *i, ip_addr prefix, ip_addr mask)
! 135: {
! 136: struct ifa *a, *b = NULL;
! 137:
! 138: WALK_LIST(a, i->addrs)
! 139: {
! 140: if (!(a->flags & IA_SECONDARY) &&
! 141: ipa_equal(ipa_and(a->ip, mask), prefix) &&
! 142: (!b || prefer_addr(a, b)))
! 143: b = a;
! 144: }
! 145:
! 146: return b;
! 147: }
! 148:
! 149: struct ifa *
! 150: kif_choose_primary(struct iface *i)
! 151: {
! 152: struct kif_config *cf = (struct kif_config *) (kif_proto->p.cf);
! 153: struct kif_primary_item *it;
! 154: struct ifa *a;
! 155:
! 156: WALK_LIST(it, cf->primary)
! 157: {
! 158: if (!it->pattern || patmatch(it->pattern, i->name))
! 159: if (a = find_preferred_ifa(i, it->prefix, ipa_mkmask(it->pxlen)))
! 160: return a;
! 161: }
! 162:
! 163: if (a = kif_get_primary_ip(i))
! 164: return a;
! 165:
! 166: return find_preferred_ifa(i, IPA_NONE, IPA_NONE);
! 167: }
! 168:
! 169:
! 170: static struct proto *
! 171: kif_init(struct proto_config *c)
! 172: {
! 173: struct kif_proto *p = proto_new(c, sizeof(struct kif_proto));
! 174:
! 175: kif_sys_init(p);
! 176: return &p->p;
! 177: }
! 178:
! 179: static int
! 180: kif_start(struct proto *P)
! 181: {
! 182: struct kif_proto *p = (struct kif_proto *) P;
! 183:
! 184: kif_proto = p;
! 185: kif_sys_start(p);
! 186:
! 187: /* Start periodic interface scanning */
! 188: kif_scan_timer = tm_new(P->pool);
! 189: kif_scan_timer->hook = kif_scan;
! 190: kif_scan_timer->data = p;
! 191: kif_scan_timer->recurrent = KIF_CF->scan_time;
! 192: kif_scan(kif_scan_timer);
! 193: tm_start(kif_scan_timer, KIF_CF->scan_time);
! 194:
! 195: return PS_UP;
! 196: }
! 197:
! 198: static int
! 199: kif_shutdown(struct proto *P)
! 200: {
! 201: struct kif_proto *p = (struct kif_proto *) P;
! 202:
! 203: tm_stop(kif_scan_timer);
! 204: kif_sys_shutdown(p);
! 205: kif_proto = NULL;
! 206:
! 207: return PS_DOWN;
! 208: }
! 209:
! 210: static int
! 211: kif_reconfigure(struct proto *p, struct proto_config *new)
! 212: {
! 213: struct kif_config *o = (struct kif_config *) p->cf;
! 214: struct kif_config *n = (struct kif_config *) new;
! 215:
! 216: if (!kif_sys_reconfigure((struct kif_proto *) p, n, o))
! 217: return 0;
! 218:
! 219: if (o->scan_time != n->scan_time)
! 220: {
! 221: tm_stop(kif_scan_timer);
! 222: kif_scan_timer->recurrent = n->scan_time;
! 223: kif_scan(kif_scan_timer);
! 224: tm_start(kif_scan_timer, n->scan_time);
! 225: }
! 226:
! 227: if (!EMPTY_LIST(o->primary) || !EMPTY_LIST(n->primary))
! 228: {
! 229: /* This is hack, we have to update a configuration
! 230: * to the new value just now, because it is used
! 231: * for recalculation of primary addresses.
! 232: */
! 233: p->cf = new;
! 234:
! 235: ifa_recalc_all_primary_addresses();
! 236: }
! 237:
! 238: return 1;
! 239: }
! 240:
! 241:
! 242: static void
! 243: kif_preconfig(struct protocol *P UNUSED, struct config *c)
! 244: {
! 245: kif_cf = NULL;
! 246: kif_sys_preconfig(c);
! 247: }
! 248:
! 249: struct proto_config *
! 250: kif_init_config(int class)
! 251: {
! 252: if (kif_cf)
! 253: cf_error("Kernel device protocol already defined");
! 254:
! 255: kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
! 256: kif_cf->scan_time = 60;
! 257: init_list(&kif_cf->primary);
! 258:
! 259: kif_sys_init_config(kif_cf);
! 260: return (struct proto_config *) kif_cf;
! 261: }
! 262:
! 263: static void
! 264: kif_copy_config(struct proto_config *dest, struct proto_config *src)
! 265: {
! 266: struct kif_config *d = (struct kif_config *) dest;
! 267: struct kif_config *s = (struct kif_config *) src;
! 268:
! 269: /* Shallow copy of everything (just scan_time currently) */
! 270: proto_copy_rest(dest, src, sizeof(struct kif_config));
! 271:
! 272: /* Copy primary addr list */
! 273: cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
! 274:
! 275: /* Fix sysdep parts */
! 276: kif_sys_copy_config(d, s);
! 277: }
! 278:
! 279:
! 280: struct protocol proto_unix_iface = {
! 281: .name = "Device",
! 282: .template = "device%d",
! 283: .preference = DEF_PREF_DIRECT,
! 284: .config_size = sizeof(struct kif_config),
! 285: .preconfig = kif_preconfig,
! 286: .init = kif_init,
! 287: .start = kif_start,
! 288: .shutdown = kif_shutdown,
! 289: .reconfigure = kif_reconfigure,
! 290: .copy_config = kif_copy_config
! 291: };
! 292:
! 293: /*
! 294: * Tracing of routes
! 295: */
! 296:
! 297: static inline void
! 298: krt_trace_in(struct krt_proto *p, rte *e, char *msg)
! 299: {
! 300: if (p->p.debug & D_PACKETS)
! 301: log(L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg);
! 302: }
! 303:
! 304: static inline void
! 305: krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg)
! 306: {
! 307: if (p->p.debug & D_PACKETS)
! 308: log_rl(f, L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg);
! 309: }
! 310:
! 311: /*
! 312: * Inherited Routes
! 313: */
! 314:
! 315: #ifdef KRT_ALLOW_LEARN
! 316:
! 317: static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
! 318:
! 319: /*
! 320: * krt_same_key() specifies what (aside from the net) is the key in
! 321: * kernel routing tables. It should be OS-dependent, this is for
! 322: * Linux. It is important for asynchronous alien updates, because a
! 323: * positive update is implicitly a negative one for any old route with
! 324: * the same key.
! 325: */
! 326:
! 327: static inline int
! 328: krt_same_key(rte *a, rte *b)
! 329: {
! 330: return a->u.krt.metric == b->u.krt.metric;
! 331: }
! 332:
! 333: static inline int
! 334: krt_uptodate(rte *a, rte *b)
! 335: {
! 336: if (a->attrs != b->attrs)
! 337: return 0;
! 338:
! 339: if (a->u.krt.proto != b->u.krt.proto)
! 340: return 0;
! 341:
! 342: return 1;
! 343: }
! 344:
! 345: static void
! 346: krt_learn_announce_update(struct krt_proto *p, rte *e)
! 347: {
! 348: net *n = e->net;
! 349: rta *aa = rta_clone(e->attrs);
! 350: rte *ee = rte_get_temp(aa);
! 351: net *nn = net_get(p->p.table, n->n.prefix, n->n.pxlen);
! 352: ee->net = nn;
! 353: ee->pflags = 0;
! 354: ee->pref = p->p.preference;
! 355: ee->u.krt = e->u.krt;
! 356: rte_update(&p->p, nn, ee);
! 357: }
! 358:
! 359: static void
! 360: krt_learn_announce_delete(struct krt_proto *p, net *n)
! 361: {
! 362: n = net_find(p->p.table, n->n.prefix, n->n.pxlen);
! 363: rte_update(&p->p, n, NULL);
! 364: }
! 365:
! 366: /* Called when alien route is discovered during scan */
! 367: static void
! 368: krt_learn_scan(struct krt_proto *p, rte *e)
! 369: {
! 370: net *n0 = e->net;
! 371: net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
! 372: rte *m, **mm;
! 373:
! 374: e->attrs = rta_lookup(e->attrs);
! 375:
! 376: for(mm=&n->routes; m = *mm; mm=&m->next)
! 377: if (krt_same_key(m, e))
! 378: break;
! 379: if (m)
! 380: {
! 381: if (krt_uptodate(m, e))
! 382: {
! 383: krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
! 384: rte_free(e);
! 385: m->u.krt.seen = 1;
! 386: }
! 387: else
! 388: {
! 389: krt_trace_in(p, e, "[alien] updated");
! 390: *mm = m->next;
! 391: rte_free(m);
! 392: m = NULL;
! 393: }
! 394: }
! 395: else
! 396: krt_trace_in(p, e, "[alien] created");
! 397: if (!m)
! 398: {
! 399: e->next = n->routes;
! 400: n->routes = e;
! 401: e->u.krt.seen = 1;
! 402: }
! 403: }
! 404:
! 405: static void
! 406: krt_learn_prune(struct krt_proto *p)
! 407: {
! 408: struct fib *fib = &p->krt_table.fib;
! 409: struct fib_iterator fit;
! 410:
! 411: KRT_TRACE(p, D_EVENTS, "Pruning inherited routes");
! 412:
! 413: FIB_ITERATE_INIT(&fit, fib);
! 414: again:
! 415: FIB_ITERATE_START(fib, &fit, f)
! 416: {
! 417: net *n = (net *) f;
! 418: rte *e, **ee, *best, **pbest, *old_best;
! 419:
! 420: /*
! 421: * Note that old_best may be NULL even if there was an old best route in
! 422: * the previous step, because it might be replaced in krt_learn_scan().
! 423: * But in that case there is a new valid best route.
! 424: */
! 425:
! 426: old_best = NULL;
! 427: best = NULL;
! 428: pbest = NULL;
! 429: ee = &n->routes;
! 430: while (e = *ee)
! 431: {
! 432: if (e->u.krt.best)
! 433: old_best = e;
! 434:
! 435: if (!e->u.krt.seen)
! 436: {
! 437: *ee = e->next;
! 438: rte_free(e);
! 439: continue;
! 440: }
! 441:
! 442: if (!best || best->u.krt.metric > e->u.krt.metric)
! 443: {
! 444: best = e;
! 445: pbest = ee;
! 446: }
! 447:
! 448: e->u.krt.seen = 0;
! 449: e->u.krt.best = 0;
! 450: ee = &e->next;
! 451: }
! 452: if (!n->routes)
! 453: {
! 454: DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
! 455: if (old_best)
! 456: krt_learn_announce_delete(p, n);
! 457:
! 458: FIB_ITERATE_PUT(&fit, f);
! 459: fib_delete(fib, f);
! 460: goto again;
! 461: }
! 462:
! 463: best->u.krt.best = 1;
! 464: *pbest = best->next;
! 465: best->next = n->routes;
! 466: n->routes = best;
! 467:
! 468: if ((best != old_best) || p->reload)
! 469: {
! 470: DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
! 471: krt_learn_announce_update(p, best);
! 472: }
! 473: else
! 474: DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
! 475: }
! 476: FIB_ITERATE_END(f);
! 477:
! 478: p->reload = 0;
! 479: }
! 480:
! 481: static void
! 482: krt_learn_async(struct krt_proto *p, rte *e, int new)
! 483: {
! 484: net *n0 = e->net;
! 485: net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
! 486: rte *g, **gg, *best, **bestp, *old_best;
! 487:
! 488: e->attrs = rta_lookup(e->attrs);
! 489:
! 490: old_best = n->routes;
! 491: for(gg=&n->routes; g = *gg; gg = &g->next)
! 492: if (krt_same_key(g, e))
! 493: break;
! 494: if (new)
! 495: {
! 496: if (g)
! 497: {
! 498: if (krt_uptodate(g, e))
! 499: {
! 500: krt_trace_in(p, e, "[alien async] same");
! 501: rte_free(e);
! 502: return;
! 503: }
! 504: krt_trace_in(p, e, "[alien async] updated");
! 505: *gg = g->next;
! 506: rte_free(g);
! 507: }
! 508: else
! 509: krt_trace_in(p, e, "[alien async] created");
! 510:
! 511: e->next = n->routes;
! 512: n->routes = e;
! 513: }
! 514: else if (!g)
! 515: {
! 516: krt_trace_in(p, e, "[alien async] delete failed");
! 517: rte_free(e);
! 518: return;
! 519: }
! 520: else
! 521: {
! 522: krt_trace_in(p, e, "[alien async] removed");
! 523: *gg = g->next;
! 524: rte_free(e);
! 525: rte_free(g);
! 526: }
! 527: best = n->routes;
! 528: bestp = &n->routes;
! 529: for(gg=&n->routes; g=*gg; gg=&g->next)
! 530: {
! 531: if (best->u.krt.metric > g->u.krt.metric)
! 532: {
! 533: best = g;
! 534: bestp = gg;
! 535: }
! 536:
! 537: g->u.krt.best = 0;
! 538: }
! 539:
! 540: if (best)
! 541: {
! 542: best->u.krt.best = 1;
! 543: *bestp = best->next;
! 544: best->next = n->routes;
! 545: n->routes = best;
! 546: }
! 547:
! 548: if (best != old_best)
! 549: {
! 550: DBG("krt_learn_async: distributing change\n");
! 551: if (best)
! 552: krt_learn_announce_update(p, best);
! 553: else
! 554: krt_learn_announce_delete(p, n);
! 555: }
! 556: }
! 557:
! 558: static void
! 559: krt_learn_init(struct krt_proto *p)
! 560: {
! 561: if (KRT_CF->learn)
! 562: rt_setup(p->p.pool, &p->krt_table, "Inherited", NULL);
! 563: }
! 564:
! 565: static void
! 566: krt_dump(struct proto *P)
! 567: {
! 568: struct krt_proto *p = (struct krt_proto *) P;
! 569:
! 570: if (!KRT_CF->learn)
! 571: return;
! 572: debug("KRT: Table of inheritable routes\n");
! 573: rt_dump(&p->krt_table);
! 574: }
! 575:
! 576: static void
! 577: krt_dump_attrs(rte *e)
! 578: {
! 579: debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto);
! 580: }
! 581:
! 582: #endif
! 583:
! 584: /*
! 585: * Routes
! 586: */
! 587:
! 588: static void
! 589: krt_flush_routes(struct krt_proto *p)
! 590: {
! 591: struct rtable *t = p->p.table;
! 592:
! 593: KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
! 594: FIB_WALK(&t->fib, f)
! 595: {
! 596: net *n = (net *) f;
! 597: rte *e = n->routes;
! 598: if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED))
! 599: {
! 600: /* FIXME: this does not work if gw is changed in export filter */
! 601: krt_replace_rte(p, e->net, NULL, e, NULL);
! 602: n->n.flags &= ~KRF_INSTALLED;
! 603: }
! 604: }
! 605: FIB_WALK_END;
! 606: }
! 607:
! 608: static struct rte *
! 609: krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
! 610: {
! 611: struct announce_hook *ah = p->p.main_ahook;
! 612: struct filter *filter = ah->out_filter;
! 613: rte *rt;
! 614:
! 615: if (p->p.accept_ra_types == RA_MERGED)
! 616: return rt_export_merged(ah, net, rt_free, tmpa, krt_filter_lp, 1);
! 617:
! 618: rt = net->routes;
! 619: *rt_free = NULL;
! 620:
! 621: if (!rte_is_valid(rt))
! 622: return NULL;
! 623:
! 624: if (filter == FILTER_REJECT)
! 625: return NULL;
! 626:
! 627: struct proto *src = rt->attrs->src->proto;
! 628: *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, krt_filter_lp) : NULL;
! 629:
! 630: /* We could run krt_import_control() here, but it is already handled by KRF_INSTALLED */
! 631:
! 632: if (filter == FILTER_ACCEPT)
! 633: goto accept;
! 634:
! 635: if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) > F_ACCEPT)
! 636: goto reject;
! 637:
! 638:
! 639: accept:
! 640: if (rt != net->routes)
! 641: *rt_free = rt;
! 642: return rt;
! 643:
! 644: reject:
! 645: if (rt != net->routes)
! 646: rte_free(rt);
! 647: return NULL;
! 648: }
! 649:
! 650: static int
! 651: krt_same_dest(rte *k, rte *e)
! 652: {
! 653: rta *ka = k->attrs, *ea = e->attrs;
! 654:
! 655: if (ka->dest != ea->dest)
! 656: return 0;
! 657: switch (ka->dest)
! 658: {
! 659: case RTD_ROUTER:
! 660: return ipa_equal(ka->gw, ea->gw);
! 661: case RTD_DEVICE:
! 662: return !strcmp(ka->iface->name, ea->iface->name);
! 663: case RTD_MULTIPATH:
! 664: return mpnh_same(ka->nexthops, ea->nexthops);
! 665: default:
! 666: return 1;
! 667: }
! 668: }
! 669:
! 670: /*
! 671: * This gets called back when the low-level scanning code discovers a route.
! 672: * We expect that the route is a temporary rte and its attributes are uncached.
! 673: */
! 674:
! 675: void
! 676: krt_got_route(struct krt_proto *p, rte *e)
! 677: {
! 678: net *net = e->net;
! 679: int verdict;
! 680:
! 681: #ifdef KRT_ALLOW_LEARN
! 682: switch (e->u.krt.src)
! 683: {
! 684: case KRT_SRC_KERNEL:
! 685: verdict = KRF_IGNORE;
! 686: goto sentenced;
! 687:
! 688: case KRT_SRC_REDIRECT:
! 689: verdict = KRF_DELETE;
! 690: goto sentenced;
! 691:
! 692: case KRT_SRC_ALIEN:
! 693: if (KRT_CF->learn)
! 694: krt_learn_scan(p, e);
! 695: else
! 696: {
! 697: krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored");
! 698: rte_free(e);
! 699: }
! 700: return;
! 701: }
! 702: #endif
! 703: /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
! 704:
! 705: if (net->n.flags & KRF_VERDICT_MASK)
! 706: {
! 707: /* Route to this destination was already seen. Strange, but it happens... */
! 708: krt_trace_in(p, e, "already seen");
! 709: rte_free(e);
! 710: return;
! 711: }
! 712:
! 713: if (!p->ready)
! 714: {
! 715: /* We wait for the initial feed to have correct KRF_INSTALLED flag */
! 716: verdict = KRF_IGNORE;
! 717: goto sentenced;
! 718: }
! 719:
! 720: if (net->n.flags & KRF_INSTALLED)
! 721: {
! 722: rte *new, *rt_free;
! 723: ea_list *tmpa;
! 724:
! 725: new = krt_export_net(p, net, &rt_free, &tmpa);
! 726:
! 727: /* TODO: There also may be changes in route eattrs, we ignore that for now. */
! 728:
! 729: if (!new)
! 730: verdict = KRF_DELETE;
! 731: else if ((net->n.flags & KRF_SYNC_ERROR) || !krt_same_dest(e, new))
! 732: verdict = KRF_UPDATE;
! 733: else
! 734: verdict = KRF_SEEN;
! 735:
! 736: if (rt_free)
! 737: rte_free(rt_free);
! 738:
! 739: lp_flush(krt_filter_lp);
! 740: }
! 741: else
! 742: verdict = KRF_DELETE;
! 743:
! 744: sentenced:
! 745: krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]);
! 746: net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
! 747: if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
! 748: {
! 749: /* Get a cached copy of attributes and temporarily link the route */
! 750: rta *a = e->attrs;
! 751: a->source = RTS_DUMMY;
! 752: e->attrs = rta_lookup(a);
! 753: e->next = net->routes;
! 754: net->routes = e;
! 755: }
! 756: else
! 757: rte_free(e);
! 758: }
! 759:
! 760: static void
! 761: krt_prune(struct krt_proto *p)
! 762: {
! 763: struct rtable *t = p->p.table;
! 764:
! 765: KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
! 766: FIB_WALK(&t->fib, f)
! 767: {
! 768: net *n = (net *) f;
! 769: int verdict = f->flags & KRF_VERDICT_MASK;
! 770: rte *new, *old, *rt_free = NULL;
! 771: ea_list *tmpa = NULL;
! 772:
! 773: if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
! 774: {
! 775: /* Get a dummy route from krt_got_route() */
! 776: old = n->routes;
! 777: n->routes = old->next;
! 778: }
! 779: else
! 780: old = NULL;
! 781:
! 782: if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
! 783: {
! 784: /* We have to run export filter to get proper 'new' route */
! 785: new = krt_export_net(p, n, &rt_free, &tmpa);
! 786:
! 787: if (!new)
! 788: verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
! 789: else
! 790: tmpa = ea_append(tmpa, new->attrs->eattrs);
! 791: }
! 792: else
! 793: new = NULL;
! 794:
! 795: switch (verdict)
! 796: {
! 797: case KRF_CREATE:
! 798: if (new && (f->flags & KRF_INSTALLED))
! 799: {
! 800: krt_trace_in(p, new, "reinstalling");
! 801: krt_replace_rte(p, n, new, NULL, tmpa);
! 802: }
! 803: break;
! 804: case KRF_SEEN:
! 805: case KRF_IGNORE:
! 806: /* Nothing happens */
! 807: break;
! 808: case KRF_UPDATE:
! 809: krt_trace_in(p, new, "updating");
! 810: krt_replace_rte(p, n, new, old, tmpa);
! 811: break;
! 812: case KRF_DELETE:
! 813: krt_trace_in(p, old, "deleting");
! 814: krt_replace_rte(p, n, NULL, old, NULL);
! 815: break;
! 816: default:
! 817: bug("krt_prune: invalid route status");
! 818: }
! 819:
! 820: if (old)
! 821: rte_free(old);
! 822: if (rt_free)
! 823: rte_free(rt_free);
! 824: lp_flush(krt_filter_lp);
! 825: f->flags &= ~KRF_VERDICT_MASK;
! 826: }
! 827: FIB_WALK_END;
! 828:
! 829: #ifdef KRT_ALLOW_LEARN
! 830: if (KRT_CF->learn)
! 831: krt_learn_prune(p);
! 832: #endif
! 833:
! 834: if (p->ready)
! 835: p->initialized = 1;
! 836: }
! 837:
! 838: void
! 839: krt_got_route_async(struct krt_proto *p, rte *e, int new)
! 840: {
! 841: net *net = e->net;
! 842:
! 843: switch (e->u.krt.src)
! 844: {
! 845: case KRT_SRC_BIRD:
! 846: ASSERT(0); /* Should be filtered by the back end */
! 847:
! 848: case KRT_SRC_REDIRECT:
! 849: if (new)
! 850: {
! 851: krt_trace_in(p, e, "[redirect] deleting");
! 852: krt_replace_rte(p, net, NULL, e, NULL);
! 853: }
! 854: /* If !new, it is probably echo of our deletion */
! 855: break;
! 856:
! 857: #ifdef KRT_ALLOW_LEARN
! 858: case KRT_SRC_ALIEN:
! 859: if (KRT_CF->learn)
! 860: {
! 861: krt_learn_async(p, e, new);
! 862: return;
! 863: }
! 864: #endif
! 865: }
! 866: rte_free(e);
! 867: }
! 868:
! 869: /*
! 870: * Periodic scanning
! 871: */
! 872:
! 873:
! 874: #ifdef CONFIG_ALL_TABLES_AT_ONCE
! 875:
! 876: static timer *krt_scan_timer;
! 877: static int krt_scan_count;
! 878:
! 879: static void
! 880: krt_scan(timer *t UNUSED)
! 881: {
! 882: struct krt_proto *p;
! 883:
! 884: kif_force_scan();
! 885:
! 886: /* We need some node to decide whether to print the debug messages or not */
! 887: p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
! 888: KRT_TRACE(p, D_EVENTS, "Scanning routing table");
! 889:
! 890: krt_do_scan(NULL);
! 891:
! 892: void *q;
! 893: WALK_LIST(q, krt_proto_list)
! 894: {
! 895: p = SKIP_BACK(struct krt_proto, krt_node, q);
! 896: krt_prune(p);
! 897: }
! 898: }
! 899:
! 900: static void
! 901: krt_scan_timer_start(struct krt_proto *p)
! 902: {
! 903: if (!krt_scan_count)
! 904: krt_scan_timer = tm_new_set(krt_pool, krt_scan, NULL, 0, KRT_CF->scan_time);
! 905:
! 906: krt_scan_count++;
! 907:
! 908: tm_start(krt_scan_timer, 1);
! 909: }
! 910:
! 911: static void
! 912: krt_scan_timer_stop(struct krt_proto *p UNUSED)
! 913: {
! 914: krt_scan_count--;
! 915:
! 916: if (!krt_scan_count)
! 917: {
! 918: rfree(krt_scan_timer);
! 919: krt_scan_timer = NULL;
! 920: }
! 921: }
! 922:
! 923: static void
! 924: krt_scan_timer_kick(struct krt_proto *p UNUSED)
! 925: {
! 926: tm_start(krt_scan_timer, 0);
! 927: }
! 928:
! 929: #else
! 930:
! 931: static void
! 932: krt_scan(timer *t)
! 933: {
! 934: struct krt_proto *p = t->data;
! 935:
! 936: kif_force_scan();
! 937:
! 938: KRT_TRACE(p, D_EVENTS, "Scanning routing table");
! 939: krt_do_scan(p);
! 940: krt_prune(p);
! 941: }
! 942:
! 943: static void
! 944: krt_scan_timer_start(struct krt_proto *p)
! 945: {
! 946: p->scan_timer = tm_new_set(p->p.pool, krt_scan, p, 0, KRT_CF->scan_time);
! 947: tm_start(p->scan_timer, 1);
! 948: }
! 949:
! 950: static void
! 951: krt_scan_timer_stop(struct krt_proto *p)
! 952: {
! 953: tm_stop(p->scan_timer);
! 954: }
! 955:
! 956: static void
! 957: krt_scan_timer_kick(struct krt_proto *p)
! 958: {
! 959: tm_start(p->scan_timer, 0);
! 960: }
! 961:
! 962: #endif
! 963:
! 964:
! 965:
! 966:
! 967: /*
! 968: * Updates
! 969: */
! 970:
! 971: static struct ea_list *
! 972: krt_make_tmp_attrs(rte *rt, struct linpool *pool)
! 973: {
! 974: struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
! 975:
! 976: l->next = NULL;
! 977: l->flags = EALF_SORTED;
! 978: l->count = 2;
! 979:
! 980: l->attrs[0].id = EA_KRT_SOURCE;
! 981: l->attrs[0].flags = 0;
! 982: l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
! 983: l->attrs[0].u.data = rt->u.krt.proto;
! 984:
! 985: l->attrs[1].id = EA_KRT_METRIC;
! 986: l->attrs[1].flags = 0;
! 987: l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
! 988: l->attrs[1].u.data = rt->u.krt.metric;
! 989:
! 990: return l;
! 991: }
! 992:
! 993: static void
! 994: krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
! 995: {
! 996: /* EA_KRT_SOURCE is read-only */
! 997: rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0);
! 998: }
! 999:
! 1000: static int
! 1001: krt_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
! 1002: {
! 1003: struct krt_proto *p = (struct krt_proto *) P;
! 1004: rte *e = *new;
! 1005:
! 1006: if (e->attrs->src->proto == P)
! 1007: {
! 1008: #ifdef CONFIG_SINGLE_ROUTE
! 1009: /*
! 1010: * Implicit withdraw - when the imported kernel route becomes the best one,
! 1011: * we know that the previous one exported to the kernel was already removed,
! 1012: * but if we processed the update as usual, we would send withdraw to the
! 1013: * kernel, which would remove the new imported route instead.
! 1014: *
! 1015: * We will remove KRT_INSTALLED flag, which stops such withdraw to be
! 1016: * processed in krt_rt_notify() and krt_replace_rte().
! 1017: */
! 1018: if (e == e->net->routes)
! 1019: e->net->n.flags &= ~KRF_INSTALLED;
! 1020: #endif
! 1021: return -1;
! 1022: }
! 1023:
! 1024: if (!KRT_CF->devroutes &&
! 1025: (e->attrs->dest == RTD_DEVICE) &&
! 1026: (e->attrs->source != RTS_STATIC_DEVICE))
! 1027: return -1;
! 1028:
! 1029: if (!krt_capable(e))
! 1030: return -1;
! 1031:
! 1032: return 0;
! 1033: }
! 1034:
! 1035: static void
! 1036: krt_rt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
! 1037: rte *new, rte *old, struct ea_list *eattrs)
! 1038: {
! 1039: struct krt_proto *p = (struct krt_proto *) P;
! 1040:
! 1041: if (config->shutdown)
! 1042: return;
! 1043: if (!(net->n.flags & KRF_INSTALLED))
! 1044: old = NULL;
! 1045: if (new)
! 1046: net->n.flags |= KRF_INSTALLED;
! 1047: else
! 1048: net->n.flags &= ~KRF_INSTALLED;
! 1049: if (p->initialized) /* Before first scan we don't touch the routes */
! 1050: krt_replace_rte(p, net, new, old, eattrs);
! 1051: }
! 1052:
! 1053: static void
! 1054: krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED)
! 1055: {
! 1056: struct krt_proto *p = (struct krt_proto *) P;
! 1057:
! 1058: /*
! 1059: * When interface went down, we should remove routes to it. In the ideal world,
! 1060: * OS kernel would send us route removal notifications in such cases, but we
! 1061: * cannot rely on it as it is often not true. E.g. Linux kernel removes related
! 1062: * routes when an interface went down, but it does not notify userspace about
! 1063: * that. To be sure, we just schedule a scan to ensure synchronization.
! 1064: */
! 1065:
! 1066: if ((flags & IF_CHANGE_DOWN) && KRT_CF->learn)
! 1067: krt_scan_timer_kick(p);
! 1068: }
! 1069:
! 1070: static int
! 1071: krt_reload_routes(struct proto *P)
! 1072: {
! 1073: struct krt_proto *p = (struct krt_proto *) P;
! 1074:
! 1075: /* Although we keep learned routes in krt_table, we rather schedule a scan */
! 1076:
! 1077: if (KRT_CF->learn)
! 1078: {
! 1079: p->reload = 1;
! 1080: krt_scan_timer_kick(p);
! 1081: }
! 1082:
! 1083: return 1;
! 1084: }
! 1085:
! 1086: static void
! 1087: krt_feed_end(struct proto *P)
! 1088: {
! 1089: struct krt_proto *p = (struct krt_proto *) P;
! 1090:
! 1091: p->ready = 1;
! 1092: krt_scan_timer_kick(p);
! 1093: }
! 1094:
! 1095:
! 1096: static int
! 1097: krt_rte_same(rte *a, rte *b)
! 1098: {
! 1099: /* src is always KRT_SRC_ALIEN and type is irrelevant */
! 1100: return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
! 1101: }
! 1102:
! 1103:
! 1104: /*
! 1105: * Protocol glue
! 1106: */
! 1107:
! 1108: struct krt_config *krt_cf;
! 1109:
! 1110: static struct proto *
! 1111: krt_init(struct proto_config *C)
! 1112: {
! 1113: struct krt_proto *p = proto_new(C, sizeof(struct krt_proto));
! 1114: struct krt_config *c = (struct krt_config *) C;
! 1115:
! 1116: p->p.accept_ra_types = c->merge_paths ? RA_MERGED : RA_OPTIMAL;
! 1117: p->p.merge_limit = c->merge_paths;
! 1118: p->p.import_control = krt_import_control;
! 1119: p->p.rt_notify = krt_rt_notify;
! 1120: p->p.if_notify = krt_if_notify;
! 1121: p->p.reload_routes = krt_reload_routes;
! 1122: p->p.feed_end = krt_feed_end;
! 1123: p->p.make_tmp_attrs = krt_make_tmp_attrs;
! 1124: p->p.store_tmp_attrs = krt_store_tmp_attrs;
! 1125: p->p.rte_same = krt_rte_same;
! 1126:
! 1127: krt_sys_init(p);
! 1128: return &p->p;
! 1129: }
! 1130:
! 1131: static int
! 1132: krt_start(struct proto *P)
! 1133: {
! 1134: struct krt_proto *p = (struct krt_proto *) P;
! 1135:
! 1136: add_tail(&krt_proto_list, &p->krt_node);
! 1137:
! 1138: #ifdef KRT_ALLOW_LEARN
! 1139: krt_learn_init(p);
! 1140: #endif
! 1141:
! 1142: if (!krt_sys_start(p))
! 1143: {
! 1144: rem_node(&p->krt_node);
! 1145: return PS_START;
! 1146: }
! 1147:
! 1148: krt_scan_timer_start(p);
! 1149:
! 1150: if (P->gr_recovery && KRT_CF->graceful_restart)
! 1151: P->gr_wait = 1;
! 1152:
! 1153: return PS_UP;
! 1154: }
! 1155:
! 1156: static int
! 1157: krt_shutdown(struct proto *P)
! 1158: {
! 1159: struct krt_proto *p = (struct krt_proto *) P;
! 1160:
! 1161: krt_scan_timer_stop(p);
! 1162:
! 1163: /* FIXME we should flush routes even when persist during reconfiguration */
! 1164: if (p->initialized && !KRT_CF->persist)
! 1165: krt_flush_routes(p);
! 1166:
! 1167: p->ready = 0;
! 1168: p->initialized = 0;
! 1169:
! 1170: if (p->p.proto_state == PS_START)
! 1171: return PS_DOWN;
! 1172:
! 1173: krt_sys_shutdown(p);
! 1174: rem_node(&p->krt_node);
! 1175:
! 1176: return PS_DOWN;
! 1177: }
! 1178:
! 1179: static int
! 1180: krt_reconfigure(struct proto *p, struct proto_config *new)
! 1181: {
! 1182: struct krt_config *o = (struct krt_config *) p->cf;
! 1183: struct krt_config *n = (struct krt_config *) new;
! 1184:
! 1185: if (!krt_sys_reconfigure((struct krt_proto *) p, n, o))
! 1186: return 0;
! 1187:
! 1188: /* persist, graceful restart need not be the same */
! 1189: return o->scan_time == n->scan_time && o->learn == n->learn &&
! 1190: o->devroutes == n->devroutes && o->merge_paths == n->merge_paths;
! 1191: }
! 1192:
! 1193: static void
! 1194: krt_preconfig(struct protocol *P UNUSED, struct config *c)
! 1195: {
! 1196: krt_cf = NULL;
! 1197: krt_sys_preconfig(c);
! 1198: }
! 1199:
! 1200: static void
! 1201: krt_postconfig(struct proto_config *C)
! 1202: {
! 1203: struct krt_config *c = (struct krt_config *) C;
! 1204:
! 1205: #ifdef CONFIG_ALL_TABLES_AT_ONCE
! 1206: if (krt_cf->scan_time != c->scan_time)
! 1207: cf_error("All kernel syncers must use the same table scan interval");
! 1208: #endif
! 1209:
! 1210: if (C->table->krt_attached)
! 1211: cf_error("Kernel syncer (%s) already attached to table %s", C->table->krt_attached->name, C->table->name);
! 1212: C->table->krt_attached = C;
! 1213: krt_sys_postconfig(c);
! 1214: }
! 1215:
! 1216: struct proto_config *
! 1217: krt_init_config(int class)
! 1218: {
! 1219: #ifndef CONFIG_MULTIPLE_TABLES
! 1220: if (krt_cf)
! 1221: cf_error("Kernel protocol already defined");
! 1222: #endif
! 1223:
! 1224: krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
! 1225: krt_cf->scan_time = 60;
! 1226:
! 1227: krt_sys_init_config(krt_cf);
! 1228: return (struct proto_config *) krt_cf;
! 1229: }
! 1230:
! 1231: static void
! 1232: krt_copy_config(struct proto_config *dest, struct proto_config *src)
! 1233: {
! 1234: struct krt_config *d = (struct krt_config *) dest;
! 1235: struct krt_config *s = (struct krt_config *) src;
! 1236:
! 1237: /* Shallow copy of everything */
! 1238: proto_copy_rest(dest, src, sizeof(struct krt_config));
! 1239:
! 1240: /* Fix sysdep parts */
! 1241: krt_sys_copy_config(d, s);
! 1242: }
! 1243:
! 1244: static int
! 1245: krt_get_attr(eattr *a, byte *buf, int buflen)
! 1246: {
! 1247: switch (a->id)
! 1248: {
! 1249: case EA_KRT_SOURCE:
! 1250: bsprintf(buf, "source");
! 1251: return GA_NAME;
! 1252:
! 1253: case EA_KRT_METRIC:
! 1254: bsprintf(buf, "metric");
! 1255: return GA_NAME;
! 1256:
! 1257: default:
! 1258: return krt_sys_get_attr(a, buf, buflen);
! 1259: }
! 1260: }
! 1261:
! 1262:
! 1263: struct protocol proto_unix_kernel = {
! 1264: .name = "Kernel",
! 1265: .template = "kernel%d",
! 1266: .attr_class = EAP_KRT,
! 1267: .preference = DEF_PREF_INHERITED,
! 1268: .config_size = sizeof(struct krt_config),
! 1269: .preconfig = krt_preconfig,
! 1270: .postconfig = krt_postconfig,
! 1271: .init = krt_init,
! 1272: .start = krt_start,
! 1273: .shutdown = krt_shutdown,
! 1274: .reconfigure = krt_reconfigure,
! 1275: .copy_config = krt_copy_config,
! 1276: .get_attr = krt_get_attr,
! 1277: #ifdef KRT_ALLOW_LEARN
! 1278: .dump = krt_dump,
! 1279: .dump_attrs = krt_dump_attrs,
! 1280: #endif
! 1281: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>