Annotation of embedaddon/bird/nest/rt-table.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD -- Routing Tables
! 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: Routing tables
! 11: *
! 12: * Routing tables are probably the most important structures BIRD uses. They
! 13: * hold all the information about known networks, the associated routes and
! 14: * their attributes.
! 15: *
! 16: * There are multiple routing tables (a primary one together with any
! 17: * number of secondary ones if requested by the configuration). Each table
! 18: * is basically a FIB containing entries describing the individual
! 19: * destination networks. For each network (represented by structure &net),
! 20: * there is a one-way linked list of route entries (&rte), the first entry
! 21: * on the list being the best one (i.e., the one we currently use
! 22: * for routing), the order of the other ones is undetermined.
! 23: *
! 24: * The &rte contains information specific to the route (preference, protocol
! 25: * metrics, time of last modification etc.) and a pointer to a &rta structure
! 26: * (see the route attribute module for a precise explanation) holding the
! 27: * remaining route attributes which are expected to be shared by multiple
! 28: * routes in order to conserve memory.
! 29: */
! 30:
! 31: #undef LOCAL_DEBUG
! 32:
! 33: #include "nest/bird.h"
! 34: #include "nest/route.h"
! 35: #include "nest/protocol.h"
! 36: #include "nest/cli.h"
! 37: #include "nest/iface.h"
! 38: #include "lib/resource.h"
! 39: #include "lib/event.h"
! 40: #include "lib/string.h"
! 41: #include "conf/conf.h"
! 42: #include "filter/filter.h"
! 43: #include "lib/string.h"
! 44: #include "lib/alloca.h"
! 45:
! 46: pool *rt_table_pool;
! 47:
! 48: static slab *rte_slab;
! 49: static linpool *rte_update_pool;
! 50:
! 51: static list routing_tables;
! 52:
! 53: static byte *rt_format_via(rte *e);
! 54: static void rt_free_hostcache(rtable *tab);
! 55: static void rt_notify_hostcache(rtable *tab, net *net);
! 56: static void rt_update_hostcache(rtable *tab);
! 57: static void rt_next_hop_update(rtable *tab);
! 58: static inline int rt_prune_table(rtable *tab);
! 59: static inline void rt_schedule_gc(rtable *tab);
! 60: static inline void rt_schedule_prune(rtable *tab);
! 61:
! 62:
! 63: static inline struct ea_list *
! 64: make_tmp_attrs(struct rte *rt, struct linpool *pool)
! 65: {
! 66: struct ea_list *(*mta)(struct rte *rt, struct linpool *pool);
! 67: mta = rt->attrs->src->proto->make_tmp_attrs;
! 68: return mta ? mta(rt, pool) : NULL;
! 69: }
! 70:
! 71: /* Like fib_route(), but skips empty net entries */
! 72: static net *
! 73: net_route(rtable *tab, ip_addr a, int len)
! 74: {
! 75: ip_addr a0;
! 76: net *n;
! 77:
! 78: while (len >= 0)
! 79: {
! 80: a0 = ipa_and(a, ipa_mkmask(len));
! 81: n = fib_find(&tab->fib, &a0, len);
! 82: if (n && rte_is_valid(n->routes))
! 83: return n;
! 84: len--;
! 85: }
! 86: return NULL;
! 87: }
! 88:
! 89: static void
! 90: rte_init(struct fib_node *N)
! 91: {
! 92: net *n = (net *) N;
! 93:
! 94: N->flags = 0;
! 95: n->routes = NULL;
! 96: }
! 97:
! 98: /**
! 99: * rte_find - find a route
! 100: * @net: network node
! 101: * @src: route source
! 102: *
! 103: * The rte_find() function returns a route for destination @net
! 104: * which is from route source @src.
! 105: */
! 106: rte *
! 107: rte_find(net *net, struct rte_src *src)
! 108: {
! 109: rte *e = net->routes;
! 110:
! 111: while (e && e->attrs->src != src)
! 112: e = e->next;
! 113: return e;
! 114: }
! 115:
! 116: /**
! 117: * rte_get_temp - get a temporary &rte
! 118: * @a: attributes to assign to the new route (a &rta; in case it's
! 119: * un-cached, rte_update() will create a cached copy automatically)
! 120: *
! 121: * Create a temporary &rte and bind it with the attributes @a.
! 122: * Also set route preference to the default preference set for
! 123: * the protocol.
! 124: */
! 125: rte *
! 126: rte_get_temp(rta *a)
! 127: {
! 128: rte *e = sl_alloc(rte_slab);
! 129:
! 130: e->attrs = a;
! 131: e->flags = 0;
! 132: e->pref = a->src->proto->preference;
! 133: return e;
! 134: }
! 135:
! 136: rte *
! 137: rte_do_cow(rte *r)
! 138: {
! 139: rte *e = sl_alloc(rte_slab);
! 140:
! 141: memcpy(e, r, sizeof(rte));
! 142: e->attrs = rta_clone(r->attrs);
! 143: e->flags = 0;
! 144: return e;
! 145: }
! 146:
! 147: /**
! 148: * rte_cow_rta - get a private writable copy of &rte with writable &rta
! 149: * @r: a route entry to be copied
! 150: * @lp: a linpool from which to allocate &rta
! 151: *
! 152: * rte_cow_rta() takes a &rte and prepares it and associated &rta for
! 153: * modification. There are three possibilities: First, both &rte and &rta are
! 154: * private copies, in that case they are returned unchanged. Second, &rte is
! 155: * private copy, but &rta is cached, in that case &rta is duplicated using
! 156: * rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case
! 157: * both structures are duplicated by rte_do_cow() and rta_do_cow().
! 158: *
! 159: * Note that in the second case, cached &rta loses one reference, while private
! 160: * copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs,
! 161: * nexthops, ...) with it. To work properly, original shared &rta should have
! 162: * another reference during the life of created private copy.
! 163: *
! 164: * Result: a pointer to the new writable &rte with writable &rta.
! 165: */
! 166: rte *
! 167: rte_cow_rta(rte *r, linpool *lp)
! 168: {
! 169: if (!rta_is_cached(r->attrs))
! 170: return r;
! 171:
! 172: rte *e = rte_cow(r);
! 173: rta *a = rta_do_cow(r->attrs, lp);
! 174: rta_free(e->attrs);
! 175: e->attrs = a;
! 176: return e;
! 177: }
! 178:
! 179: static int /* Actually better or at least as good as */
! 180: rte_better(rte *new, rte *old)
! 181: {
! 182: int (*better)(rte *, rte *);
! 183:
! 184: if (!rte_is_valid(old))
! 185: return 1;
! 186: if (!rte_is_valid(new))
! 187: return 0;
! 188:
! 189: if (new->pref > old->pref)
! 190: return 1;
! 191: if (new->pref < old->pref)
! 192: return 0;
! 193: if (new->attrs->src->proto->proto != old->attrs->src->proto->proto)
! 194: {
! 195: /*
! 196: * If the user has configured protocol preferences, so that two different protocols
! 197: * have the same preference, try to break the tie by comparing addresses. Not too
! 198: * useful, but keeps the ordering of routes unambiguous.
! 199: */
! 200: return new->attrs->src->proto->proto > old->attrs->src->proto->proto;
! 201: }
! 202: if (better = new->attrs->src->proto->rte_better)
! 203: return better(new, old);
! 204: return 0;
! 205: }
! 206:
! 207: static int
! 208: rte_mergable(rte *pri, rte *sec)
! 209: {
! 210: int (*mergable)(rte *, rte *);
! 211:
! 212: if (!rte_is_valid(pri) || !rte_is_valid(sec))
! 213: return 0;
! 214:
! 215: if (pri->pref != sec->pref)
! 216: return 0;
! 217:
! 218: if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto)
! 219: return 0;
! 220:
! 221: if (mergable = pri->attrs->src->proto->rte_mergable)
! 222: return mergable(pri, sec);
! 223:
! 224: return 0;
! 225: }
! 226:
! 227: static void
! 228: rte_trace(struct proto *p, rte *e, int dir, char *msg)
! 229: {
! 230: log(L_TRACE "%s %c %s %I/%d %s", p->name, dir, msg, e->net->n.prefix, e->net->n.pxlen, rt_format_via(e));
! 231: }
! 232:
! 233: static inline void
! 234: rte_trace_in(uint flag, struct proto *p, rte *e, char *msg)
! 235: {
! 236: if (p->debug & flag)
! 237: rte_trace(p, e, '>', msg);
! 238: }
! 239:
! 240: static inline void
! 241: rte_trace_out(uint flag, struct proto *p, rte *e, char *msg)
! 242: {
! 243: if (p->debug & flag)
! 244: rte_trace(p, e, '<', msg);
! 245: }
! 246:
! 247: static rte *
! 248: export_filter_(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
! 249: {
! 250: struct proto *p = ah->proto;
! 251: struct filter *filter = ah->out_filter;
! 252: struct proto_stats *stats = ah->stats;
! 253: ea_list *tmpb = NULL;
! 254: rte *rt;
! 255: int v;
! 256:
! 257: rt = rt0;
! 258: *rt_free = NULL;
! 259:
! 260: if (!tmpa)
! 261: tmpa = &tmpb;
! 262:
! 263: *tmpa = make_tmp_attrs(rt, pool);
! 264:
! 265: v = p->import_control ? p->import_control(p, &rt, tmpa, pool) : 0;
! 266: if (v < 0)
! 267: {
! 268: if (silent)
! 269: goto reject;
! 270:
! 271: stats->exp_updates_rejected++;
! 272: if (v == RIC_REJECT)
! 273: rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
! 274: goto reject;
! 275: }
! 276: if (v > 0)
! 277: {
! 278: if (!silent)
! 279: rte_trace_out(D_FILTERS, p, rt, "forced accept by protocol");
! 280: goto accept;
! 281: }
! 282:
! 283: v = filter && ((filter == FILTER_REJECT) ||
! 284: (f_run(filter, &rt, tmpa, pool, FF_FORCE_TMPATTR) > F_ACCEPT));
! 285: if (v)
! 286: {
! 287: if (silent)
! 288: goto reject;
! 289:
! 290: stats->exp_updates_filtered++;
! 291: rte_trace_out(D_FILTERS, p, rt, "filtered out");
! 292: goto reject;
! 293: }
! 294:
! 295: accept:
! 296: if (rt != rt0)
! 297: *rt_free = rt;
! 298: return rt;
! 299:
! 300: reject:
! 301: /* Discard temporary rte */
! 302: if (rt != rt0)
! 303: rte_free(rt);
! 304: return NULL;
! 305: }
! 306:
! 307: static inline rte *
! 308: export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
! 309: {
! 310: return export_filter_(ah, rt0, rt_free, tmpa, rte_update_pool, silent);
! 311: }
! 312:
! 313: static void
! 314: do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
! 315: {
! 316: struct proto *p = ah->proto;
! 317: struct proto_stats *stats = ah->stats;
! 318:
! 319:
! 320: /*
! 321: * First, apply export limit.
! 322: *
! 323: * Export route limits has several problems. Because exp_routes
! 324: * counter is reset before refeed, we don't really know whether
! 325: * limit is breached and whether the update is new or not. Therefore
! 326: * the number of really exported routes may exceed the limit
! 327: * temporarily (routes exported before and new routes in refeed).
! 328: *
! 329: * Minor advantage is that if the limit is decreased and refeed is
! 330: * requested, the number of exported routes really decrease.
! 331: *
! 332: * Second problem is that with export limits, we don't know whether
! 333: * old was really exported (it might be blocked by limit). When a
! 334: * withdraw is exported, we announce it even when the previous
! 335: * update was blocked. This is not a big issue, but the same problem
! 336: * is in updating exp_routes counter. Therefore, to be consistent in
! 337: * increases and decreases of exp_routes, we count exported routes
! 338: * regardless of blocking by limits.
! 339: *
! 340: * Similar problem is in handling updates - when a new route is
! 341: * received and blocking is active, the route would be blocked, but
! 342: * when an update for the route will be received later, the update
! 343: * would be propagated (as old != NULL). Therefore, we have to block
! 344: * also non-new updates (contrary to import blocking).
! 345: */
! 346:
! 347: struct proto_limit *l = ah->out_limit;
! 348: if (l && new)
! 349: {
! 350: if ((!old || refeed) && (stats->exp_routes >= l->limit))
! 351: proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
! 352:
! 353: if (l->state == PLS_BLOCKED)
! 354: {
! 355: stats->exp_routes++; /* see note above */
! 356: stats->exp_updates_rejected++;
! 357: rte_trace_out(D_FILTERS, p, new, "rejected [limit]");
! 358: new = NULL;
! 359:
! 360: if (!old)
! 361: return;
! 362: }
! 363: }
! 364:
! 365:
! 366: if (new)
! 367: stats->exp_updates_accepted++;
! 368: else
! 369: stats->exp_withdraws_accepted++;
! 370:
! 371: /* Hack: We do not decrease exp_routes during refeed, we instead
! 372: reset exp_routes at the start of refeed. */
! 373: if (new)
! 374: stats->exp_routes++;
! 375: if (old && !refeed)
! 376: stats->exp_routes--;
! 377:
! 378: if (p->debug & D_ROUTES)
! 379: {
! 380: if (new && old)
! 381: rte_trace_out(D_ROUTES, p, new, "replaced");
! 382: else if (new)
! 383: rte_trace_out(D_ROUTES, p, new, "added");
! 384: else if (old)
! 385: rte_trace_out(D_ROUTES, p, old, "removed");
! 386: }
! 387: if (!new)
! 388: p->rt_notify(p, ah->table, net, NULL, old, NULL);
! 389: else if (tmpa)
! 390: {
! 391: ea_list *t = tmpa;
! 392: while (t->next)
! 393: t = t->next;
! 394: t->next = new->attrs->eattrs;
! 395: p->rt_notify(p, ah->table, net, new, old, tmpa);
! 396: t->next = NULL;
! 397: }
! 398: else
! 399: p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
! 400: }
! 401:
! 402: static void
! 403: rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int refeed)
! 404: {
! 405: struct proto *p = ah->proto;
! 406: struct proto_stats *stats = ah->stats;
! 407:
! 408: rte *new = new0;
! 409: rte *old = old0;
! 410: rte *new_free = NULL;
! 411: rte *old_free = NULL;
! 412: ea_list *tmpa = NULL;
! 413:
! 414: if (new)
! 415: stats->exp_updates_received++;
! 416: else
! 417: stats->exp_withdraws_received++;
! 418:
! 419: /*
! 420: * This is a tricky part - we don't know whether route 'old' was
! 421: * exported to protocol 'p' or was filtered by the export filter.
! 422: * We try to run the export filter to know this to have a correct
! 423: * value in 'old' argument of rte_update (and proper filter value)
! 424: *
! 425: * FIXME - this is broken because 'configure soft' may change
! 426: * filters but keep routes. Refeed is expected to be called after
! 427: * change of the filters and with old == new, therefore we do not
! 428: * even try to run the filter on an old route, This may lead to
! 429: * 'spurious withdraws' but ensure that there are no 'missing
! 430: * withdraws'.
! 431: *
! 432: * This is not completely safe as there is a window between
! 433: * reconfiguration and the end of refeed - if a newly filtered
! 434: * route disappears during this period, proper withdraw is not
! 435: * sent (because old would be also filtered) and the route is
! 436: * not refeeded (because it disappeared before that).
! 437: */
! 438:
! 439: if (new)
! 440: new = export_filter(ah, new, &new_free, &tmpa, 0);
! 441:
! 442: if (old && !refeed)
! 443: old = export_filter(ah, old, &old_free, NULL, 1);
! 444:
! 445: if (!new && !old)
! 446: {
! 447: /*
! 448: * As mentioned above, 'old' value may be incorrect in some race conditions.
! 449: * We generally ignore it with the exception of withdraw to pipe protocol.
! 450: * In that case we rather propagate unfiltered withdraws regardless of
! 451: * export filters to ensure that when a protocol is flushed, its routes are
! 452: * removed from all tables. Possible spurious unfiltered withdraws are not
! 453: * problem here as they are ignored if there is no corresponding route at
! 454: * the other end of the pipe. We directly call rt_notify() hook instead of
! 455: * do_rt_notify() to avoid logging and stat counters.
! 456: */
! 457:
! 458: #ifdef CONFIG_PIPE
! 459: if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto))
! 460: p->rt_notify(p, ah->table, net, NULL, old0, NULL);
! 461: #endif
! 462:
! 463: return;
! 464: }
! 465:
! 466: do_rt_notify(ah, net, new, old, tmpa, refeed);
! 467:
! 468: /* Discard temporary rte's */
! 469: if (new_free)
! 470: rte_free(new_free);
! 471: if (old_free)
! 472: rte_free(old_free);
! 473: }
! 474:
! 475: static void
! 476: rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed)
! 477: {
! 478: // struct proto *p = ah->proto;
! 479: struct proto_stats *stats = ah->stats;
! 480:
! 481: rte *r;
! 482: rte *new_best = NULL;
! 483: rte *old_best = NULL;
! 484: rte *new_free = NULL;
! 485: rte *old_free = NULL;
! 486: ea_list *tmpa = NULL;
! 487:
! 488: /* Used to track whether we met old_changed position. If before_old is NULL
! 489: old_changed was the first and we met it implicitly before current best route. */
! 490: int old_meet = old_changed && !before_old;
! 491:
! 492: /* Note that before_old is either NULL or valid (not rejected) route.
! 493: If old_changed is valid, before_old have to be too. If old changed route
! 494: was not valid, caller must use NULL for both old_changed and before_old. */
! 495:
! 496: if (new_changed)
! 497: stats->exp_updates_received++;
! 498: else
! 499: stats->exp_withdraws_received++;
! 500:
! 501: /* First, find the new_best route - first accepted by filters */
! 502: for (r=net->routes; rte_is_valid(r); r=r->next)
! 503: {
! 504: if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
! 505: break;
! 506:
! 507: /* Note if we walked around the position of old_changed route */
! 508: if (r == before_old)
! 509: old_meet = 1;
! 510: }
! 511:
! 512: /*
! 513: * Second, handle the feed case. That means we do not care for
! 514: * old_best. It is NULL for feed, and the new_best for refeed.
! 515: * For refeed, there is a hack similar to one in rt_notify_basic()
! 516: * to ensure withdraws in case of changed filters
! 517: */
! 518: if (feed)
! 519: {
! 520: if (feed == 2) /* refeed */
! 521: old_best = new_best ? new_best :
! 522: (rte_is_valid(net->routes) ? net->routes : NULL);
! 523: else
! 524: old_best = NULL;
! 525:
! 526: if (!new_best && !old_best)
! 527: return;
! 528:
! 529: goto found;
! 530: }
! 531:
! 532: /*
! 533: * Now, we find the old_best route. Generally, it is the same as the
! 534: * new_best, unless new_best is the same as new_changed or
! 535: * old_changed is accepted before new_best.
! 536: *
! 537: * There are four cases:
! 538: *
! 539: * - We would find and accept old_changed before new_best, therefore
! 540: * old_changed is old_best. In remaining cases we suppose this
! 541: * is not true.
! 542: *
! 543: * - We found no new_best, therefore there is also no old_best and
! 544: * we ignore this withdraw.
! 545: *
! 546: * - We found new_best different than new_changed, therefore
! 547: * old_best is the same as new_best and we ignore this update.
! 548: *
! 549: * - We found new_best the same as new_changed, therefore it cannot
! 550: * be old_best and we have to continue search for old_best.
! 551: */
! 552:
! 553: /* First case */
! 554: if (old_meet)
! 555: if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
! 556: goto found;
! 557:
! 558: /* Second case */
! 559: if (!new_best)
! 560: return;
! 561:
! 562: /* Third case, we use r instead of new_best, because export_filter() could change it */
! 563: if (r != new_changed)
! 564: {
! 565: if (new_free)
! 566: rte_free(new_free);
! 567: return;
! 568: }
! 569:
! 570: /* Fourth case */
! 571: for (r=r->next; rte_is_valid(r); r=r->next)
! 572: {
! 573: if (old_best = export_filter(ah, r, &old_free, NULL, 1))
! 574: goto found;
! 575:
! 576: if (r == before_old)
! 577: if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
! 578: goto found;
! 579: }
! 580:
! 581: /* Implicitly, old_best is NULL and new_best is non-NULL */
! 582:
! 583: found:
! 584: do_rt_notify(ah, net, new_best, old_best, tmpa, (feed == 2));
! 585:
! 586: /* Discard temporary rte's */
! 587: if (new_free)
! 588: rte_free(new_free);
! 589: if (old_free)
! 590: rte_free(old_free);
! 591: }
! 592:
! 593:
! 594: static struct mpnh *
! 595: mpnh_merge_rta(struct mpnh *nhs, rta *a, linpool *pool, int max)
! 596: {
! 597: struct mpnh nh = { .gw = a->gw, .iface = a->iface };
! 598: struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh;
! 599: return mpnh_merge(nhs, nh2, 1, 0, max, pool);
! 600: }
! 601:
! 602: rte *
! 603: rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
! 604: {
! 605: // struct proto *p = ah->proto;
! 606: struct mpnh *nhs = NULL;
! 607: rte *best0, *best, *rt0, *rt, *tmp;
! 608:
! 609: best0 = net->routes;
! 610: *rt_free = NULL;
! 611:
! 612: if (!rte_is_valid(best0))
! 613: return NULL;
! 614:
! 615: best = export_filter_(ah, best0, rt_free, tmpa, pool, silent);
! 616:
! 617: if (!best || !rte_is_reachable(best))
! 618: return best;
! 619:
! 620: for (rt0 = best0->next; rt0; rt0 = rt0->next)
! 621: {
! 622: if (!rte_mergable(best0, rt0))
! 623: continue;
! 624:
! 625: rt = export_filter_(ah, rt0, &tmp, NULL, pool, 1);
! 626:
! 627: if (!rt)
! 628: continue;
! 629:
! 630: if (rte_is_reachable(rt))
! 631: nhs = mpnh_merge_rta(nhs, rt->attrs, pool, ah->proto->merge_limit);
! 632:
! 633: if (tmp)
! 634: rte_free(tmp);
! 635: }
! 636:
! 637: if (nhs)
! 638: {
! 639: nhs = mpnh_merge_rta(nhs, best->attrs, pool, ah->proto->merge_limit);
! 640:
! 641: if (nhs->next)
! 642: {
! 643: best = rte_cow_rta(best, pool);
! 644: best->attrs->dest = RTD_MULTIPATH;
! 645: best->attrs->nexthops = nhs;
! 646: }
! 647: }
! 648:
! 649: if (best != best0)
! 650: *rt_free = best;
! 651:
! 652: return best;
! 653: }
! 654:
! 655:
! 656: static void
! 657: rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed,
! 658: rte *new_best, rte*old_best, int refeed)
! 659: {
! 660: // struct proto *p = ah->proto;
! 661:
! 662: rte *new_best_free = NULL;
! 663: rte *old_best_free = NULL;
! 664: rte *new_changed_free = NULL;
! 665: rte *old_changed_free = NULL;
! 666: ea_list *tmpa = NULL;
! 667:
! 668: /* We assume that all rte arguments are either NULL or rte_is_valid() */
! 669:
! 670: /* This check should be done by the caller */
! 671: if (!new_best && !old_best)
! 672: return;
! 673:
! 674: /* Check whether the change is relevant to the merged route */
! 675: if ((new_best == old_best) && !refeed)
! 676: {
! 677: new_changed = rte_mergable(new_best, new_changed) ?
! 678: export_filter(ah, new_changed, &new_changed_free, NULL, 1) : NULL;
! 679:
! 680: old_changed = rte_mergable(old_best, old_changed) ?
! 681: export_filter(ah, old_changed, &old_changed_free, NULL, 1) : NULL;
! 682:
! 683: if (!new_changed && !old_changed)
! 684: return;
! 685: }
! 686:
! 687: if (new_best)
! 688: ah->stats->exp_updates_received++;
! 689: else
! 690: ah->stats->exp_withdraws_received++;
! 691:
! 692: /* Prepare new merged route */
! 693: if (new_best)
! 694: new_best = rt_export_merged(ah, net, &new_best_free, &tmpa, rte_update_pool, 0);
! 695:
! 696: /* Prepare old merged route (without proper merged next hops) */
! 697: /* There are some issues with running filter on old route - see rt_notify_basic() */
! 698: if (old_best && !refeed)
! 699: old_best = export_filter(ah, old_best, &old_best_free, NULL, 1);
! 700:
! 701: if (new_best || old_best)
! 702: do_rt_notify(ah, net, new_best, old_best, tmpa, refeed);
! 703:
! 704: /* Discard temporary rte's */
! 705: if (new_best_free)
! 706: rte_free(new_best_free);
! 707: if (old_best_free)
! 708: rte_free(old_best_free);
! 709: if (new_changed_free)
! 710: rte_free(new_changed_free);
! 711: if (old_changed_free)
! 712: rte_free(old_changed_free);
! 713: }
! 714:
! 715:
! 716: /**
! 717: * rte_announce - announce a routing table change
! 718: * @tab: table the route has been added to
! 719: * @type: type of route announcement (RA_OPTIMAL or RA_ANY)
! 720: * @net: network in question
! 721: * @new: the new route to be announced
! 722: * @old: the previous route for the same network
! 723: * @new_best: the new best route for the same network
! 724: * @old_best: the previous best route for the same network
! 725: * @before_old: The previous route before @old for the same network.
! 726: * If @before_old is NULL @old was the first.
! 727: *
! 728: * This function gets a routing table update and announces it
! 729: * to all protocols that acccepts given type of route announcement
! 730: * and are connected to the same table by their announcement hooks.
! 731: *
! 732: * Route announcement of type %RA_OPTIMAL si generated when optimal
! 733: * route (in routing table @tab) changes. In that case @old stores the
! 734: * old optimal route.
! 735: *
! 736: * Route announcement of type %RA_ANY si generated when any route (in
! 737: * routing table @tab) changes In that case @old stores the old route
! 738: * from the same protocol.
! 739: *
! 740: * For each appropriate protocol, we first call its import_control()
! 741: * hook which performs basic checks on the route (each protocol has a
! 742: * right to veto or force accept of the route before any filter is
! 743: * asked) and adds default values of attributes specific to the new
! 744: * protocol (metrics, tags etc.). Then it consults the protocol's
! 745: * export filter and if it accepts the route, the rt_notify() hook of
! 746: * the protocol gets called.
! 747: */
! 748: static void
! 749: rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old,
! 750: rte *new_best, rte *old_best, rte *before_old)
! 751: {
! 752: if (!rte_is_valid(new))
! 753: new = NULL;
! 754:
! 755: if (!rte_is_valid(old))
! 756: old = before_old = NULL;
! 757:
! 758: if (!rte_is_valid(new_best))
! 759: new_best = NULL;
! 760:
! 761: if (!rte_is_valid(old_best))
! 762: old_best = NULL;
! 763:
! 764: if (!old && !new)
! 765: return;
! 766:
! 767: if (type == RA_OPTIMAL)
! 768: {
! 769: if (new)
! 770: new->attrs->src->proto->stats.pref_routes++;
! 771: if (old)
! 772: old->attrs->src->proto->stats.pref_routes--;
! 773:
! 774: if (tab->hostcache)
! 775: rt_notify_hostcache(tab, net);
! 776: }
! 777:
! 778: struct announce_hook *a;
! 779: WALK_LIST(a, tab->hooks)
! 780: {
! 781: ASSERT(a->proto->export_state != ES_DOWN);
! 782: if (a->proto->accept_ra_types == type)
! 783: if (type == RA_ACCEPTED)
! 784: rt_notify_accepted(a, net, new, old, before_old, 0);
! 785: else if (type == RA_MERGED)
! 786: rt_notify_merged(a, net, new, old, new_best, old_best, 0);
! 787: else
! 788: rt_notify_basic(a, net, new, old, 0);
! 789: }
! 790: }
! 791:
! 792: static inline int
! 793: rte_validate(rte *e)
! 794: {
! 795: int c;
! 796: net *n = e->net;
! 797:
! 798: if ((n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen))
! 799: {
! 800: log(L_WARN "Ignoring bogus prefix %I/%d received via %s",
! 801: n->n.prefix, n->n.pxlen, e->sender->proto->name);
! 802: return 0;
! 803: }
! 804:
! 805: c = ipa_classify_net(n->n.prefix);
! 806: if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
! 807: {
! 808: log(L_WARN "Ignoring bogus route %I/%d received via %s",
! 809: n->n.prefix, n->n.pxlen, e->sender->proto->name);
! 810: return 0;
! 811: }
! 812:
! 813: if ((e->attrs->dest == RTD_MULTIPATH) && !mpnh_is_sorted(e->attrs->nexthops))
! 814: {
! 815: log(L_WARN "Ignoring unsorted multipath route %I/%d received via %s",
! 816: n->n.prefix, n->n.pxlen, e->sender->proto->name);
! 817: return 0;
! 818: }
! 819:
! 820: return 1;
! 821: }
! 822:
! 823: /**
! 824: * rte_free - delete a &rte
! 825: * @e: &rte to be deleted
! 826: *
! 827: * rte_free() deletes the given &rte from the routing table it's linked to.
! 828: */
! 829: void
! 830: rte_free(rte *e)
! 831: {
! 832: if (rta_is_cached(e->attrs))
! 833: rta_free(e->attrs);
! 834: sl_free(rte_slab, e);
! 835: }
! 836:
! 837: static inline void
! 838: rte_free_quick(rte *e)
! 839: {
! 840: rta_free(e->attrs);
! 841: sl_free(rte_slab, e);
! 842: }
! 843:
! 844: static int
! 845: rte_same(rte *x, rte *y)
! 846: {
! 847: return
! 848: x->attrs == y->attrs &&
! 849: x->flags == y->flags &&
! 850: x->pflags == y->pflags &&
! 851: x->pref == y->pref &&
! 852: (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y));
! 853: }
! 854:
! 855: static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
! 856:
! 857: static void
! 858: rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
! 859: {
! 860: struct proto *p = ah->proto;
! 861: struct rtable *table = ah->table;
! 862: struct proto_stats *stats = ah->stats;
! 863: static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
! 864: rte *before_old = NULL;
! 865: rte *old_best = net->routes;
! 866: rte *old = NULL;
! 867: rte **k;
! 868:
! 869: k = &net->routes; /* Find and remove original route from the same protocol */
! 870: while (old = *k)
! 871: {
! 872: if (old->attrs->src == src)
! 873: {
! 874: /* If there is the same route in the routing table but from
! 875: * a different sender, then there are two paths from the
! 876: * source protocol to this routing table through transparent
! 877: * pipes, which is not allowed.
! 878: *
! 879: * We log that and ignore the route. If it is withdraw, we
! 880: * ignore it completely (there might be 'spurious withdraws',
! 881: * see FIXME in do_rte_announce())
! 882: */
! 883: if (old->sender->proto != p)
! 884: {
! 885: if (new)
! 886: {
! 887: log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %I/%d to table %s",
! 888: net->n.prefix, net->n.pxlen, table->name);
! 889: rte_free_quick(new);
! 890: }
! 891: return;
! 892: }
! 893:
! 894: if (new && rte_same(old, new))
! 895: {
! 896: /* No changes, ignore the new route */
! 897:
! 898: if (!rte_is_filtered(new))
! 899: {
! 900: stats->imp_updates_ignored++;
! 901: rte_trace_in(D_ROUTES, p, new, "ignored");
! 902: }
! 903:
! 904: rte_free_quick(new);
! 905: return;
! 906: }
! 907: *k = old->next;
! 908: break;
! 909: }
! 910: k = &old->next;
! 911: before_old = old;
! 912: }
! 913:
! 914: if (!old)
! 915: before_old = NULL;
! 916:
! 917: if (!old && !new)
! 918: {
! 919: stats->imp_withdraws_ignored++;
! 920: return;
! 921: }
! 922:
! 923: int new_ok = rte_is_ok(new);
! 924: int old_ok = rte_is_ok(old);
! 925:
! 926: struct proto_limit *l = ah->rx_limit;
! 927: if (l && !old && new)
! 928: {
! 929: u32 all_routes = stats->imp_routes + stats->filt_routes;
! 930:
! 931: if (all_routes >= l->limit)
! 932: proto_notify_limit(ah, l, PLD_RX, all_routes);
! 933:
! 934: if (l->state == PLS_BLOCKED)
! 935: {
! 936: /* In receive limit the situation is simple, old is NULL so
! 937: we just free new and exit like nothing happened */
! 938:
! 939: stats->imp_updates_ignored++;
! 940: rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
! 941: rte_free_quick(new);
! 942: return;
! 943: }
! 944: }
! 945:
! 946: l = ah->in_limit;
! 947: if (l && !old_ok && new_ok)
! 948: {
! 949: if (stats->imp_routes >= l->limit)
! 950: proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
! 951:
! 952: if (l->state == PLS_BLOCKED)
! 953: {
! 954: /* In import limit the situation is more complicated. We
! 955: shouldn't just drop the route, we should handle it like
! 956: it was filtered. We also have to continue the route
! 957: processing if old or new is non-NULL, but we should exit
! 958: if both are NULL as this case is probably assumed to be
! 959: already handled. */
! 960:
! 961: stats->imp_updates_ignored++;
! 962: rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
! 963:
! 964: if (ah->in_keep_filtered)
! 965: new->flags |= REF_FILTERED;
! 966: else
! 967: { rte_free_quick(new); new = NULL; }
! 968:
! 969: /* Note that old && !new could be possible when
! 970: ah->in_keep_filtered changed in the recent past. */
! 971:
! 972: if (!old && !new)
! 973: return;
! 974:
! 975: new_ok = 0;
! 976: goto skip_stats1;
! 977: }
! 978: }
! 979:
! 980: if (new_ok)
! 981: stats->imp_updates_accepted++;
! 982: else if (old_ok)
! 983: stats->imp_withdraws_accepted++;
! 984: else
! 985: stats->imp_withdraws_ignored++;
! 986:
! 987: skip_stats1:
! 988:
! 989: if (new)
! 990: rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
! 991: if (old)
! 992: rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--;
! 993:
! 994: if (table->config->sorted)
! 995: {
! 996: /* If routes are sorted, just insert new route to appropriate position */
! 997: if (new)
! 998: {
! 999: if (before_old && !rte_better(new, before_old))
! 1000: k = &before_old->next;
! 1001: else
! 1002: k = &net->routes;
! 1003:
! 1004: for (; *k; k=&(*k)->next)
! 1005: if (rte_better(new, *k))
! 1006: break;
! 1007:
! 1008: new->next = *k;
! 1009: *k = new;
! 1010: }
! 1011: }
! 1012: else
! 1013: {
! 1014: /* If routes are not sorted, find the best route and move it on
! 1015: the first position. There are several optimized cases. */
! 1016:
! 1017: if (src->proto->rte_recalculate && src->proto->rte_recalculate(table, net, new, old, old_best))
! 1018: goto do_recalculate;
! 1019:
! 1020: if (new && rte_better(new, old_best))
! 1021: {
! 1022: /* The first case - the new route is cleary optimal,
! 1023: we link it at the first position */
! 1024:
! 1025: new->next = net->routes;
! 1026: net->routes = new;
! 1027: }
! 1028: else if (old == old_best)
! 1029: {
! 1030: /* The second case - the old best route disappeared, we add the
! 1031: new route (if we have any) to the list (we don't care about
! 1032: position) and then we elect the new optimal route and relink
! 1033: that route at the first position and announce it. New optimal
! 1034: route might be NULL if there is no more routes */
! 1035:
! 1036: do_recalculate:
! 1037: /* Add the new route to the list */
! 1038: if (new)
! 1039: {
! 1040: new->next = net->routes;
! 1041: net->routes = new;
! 1042: }
! 1043:
! 1044: /* Find a new optimal route (if there is any) */
! 1045: if (net->routes)
! 1046: {
! 1047: rte **bp = &net->routes;
! 1048: for (k=&(*bp)->next; *k; k=&(*k)->next)
! 1049: if (rte_better(*k, *bp))
! 1050: bp = k;
! 1051:
! 1052: /* And relink it */
! 1053: rte *best = *bp;
! 1054: *bp = best->next;
! 1055: best->next = net->routes;
! 1056: net->routes = best;
! 1057: }
! 1058: }
! 1059: else if (new)
! 1060: {
! 1061: /* The third case - the new route is not better than the old
! 1062: best route (therefore old_best != NULL) and the old best
! 1063: route was not removed (therefore old_best == net->routes).
! 1064: We just link the new route after the old best route. */
! 1065:
! 1066: ASSERT(net->routes != NULL);
! 1067: new->next = net->routes->next;
! 1068: net->routes->next = new;
! 1069: }
! 1070: /* The fourth (empty) case - suboptimal route was removed, nothing to do */
! 1071: }
! 1072:
! 1073: if (new)
! 1074: new->lastmod = now;
! 1075:
! 1076: /* Log the route change */
! 1077: if (p->debug & D_ROUTES)
! 1078: {
! 1079: if (new_ok)
! 1080: rte_trace(p, new, '>', new == net->routes ? "added [best]" : "added");
! 1081: else if (old_ok)
! 1082: {
! 1083: if (old != old_best)
! 1084: rte_trace(p, old, '>', "removed");
! 1085: else if (rte_is_ok(net->routes))
! 1086: rte_trace(p, old, '>', "removed [replaced]");
! 1087: else
! 1088: rte_trace(p, old, '>', "removed [sole]");
! 1089: }
! 1090: }
! 1091:
! 1092: /* Propagate the route change */
! 1093: rte_announce(table, RA_ANY, net, new, old, NULL, NULL, NULL);
! 1094: if (net->routes != old_best)
! 1095: rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, NULL, NULL);
! 1096: if (table->config->sorted)
! 1097: rte_announce(table, RA_ACCEPTED, net, new, old, NULL, NULL, before_old);
! 1098: rte_announce(table, RA_MERGED, net, new, old, net->routes, old_best, NULL);
! 1099:
! 1100: if (!net->routes &&
! 1101: (table->gc_counter++ >= table->config->gc_max_ops) &&
! 1102: (table->gc_time + table->config->gc_min_time <= now))
! 1103: rt_schedule_gc(table);
! 1104:
! 1105: if (old_ok && p->rte_remove)
! 1106: p->rte_remove(net, old);
! 1107: if (new_ok && p->rte_insert)
! 1108: p->rte_insert(net, new);
! 1109:
! 1110: if (old)
! 1111: rte_free_quick(old);
! 1112: }
! 1113:
! 1114: static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
! 1115:
! 1116: static inline void
! 1117: rte_update_lock(void)
! 1118: {
! 1119: rte_update_nest_cnt++;
! 1120: }
! 1121:
! 1122: static inline void
! 1123: rte_update_unlock(void)
! 1124: {
! 1125: if (!--rte_update_nest_cnt)
! 1126: lp_flush(rte_update_pool);
! 1127: }
! 1128:
! 1129: static inline void
! 1130: rte_hide_dummy_routes(net *net, rte **dummy)
! 1131: {
! 1132: if (net->routes && net->routes->attrs->source == RTS_DUMMY)
! 1133: {
! 1134: *dummy = net->routes;
! 1135: net->routes = (*dummy)->next;
! 1136: }
! 1137: }
! 1138:
! 1139: static inline void
! 1140: rte_unhide_dummy_routes(net *net, rte **dummy)
! 1141: {
! 1142: if (*dummy)
! 1143: {
! 1144: (*dummy)->next = net->routes;
! 1145: net->routes = *dummy;
! 1146: }
! 1147: }
! 1148:
! 1149: /**
! 1150: * rte_update - enter a new update to a routing table
! 1151: * @table: table to be updated
! 1152: * @ah: pointer to table announce hook
! 1153: * @net: network node
! 1154: * @p: protocol submitting the update
! 1155: * @src: protocol originating the update
! 1156: * @new: a &rte representing the new route or %NULL for route removal.
! 1157: *
! 1158: * This function is called by the routing protocols whenever they discover
! 1159: * a new route or wish to update/remove an existing route. The right announcement
! 1160: * sequence is to build route attributes first (either un-cached with @aflags set
! 1161: * to zero or a cached one using rta_lookup(); in this case please note that
! 1162: * you need to increase the use count of the attributes yourself by calling
! 1163: * rta_clone()), call rte_get_temp() to obtain a temporary &rte, fill in all
! 1164: * the appropriate data and finally submit the new &rte by calling rte_update().
! 1165: *
! 1166: * @src specifies the protocol that originally created the route and the meaning
! 1167: * of protocol-dependent data of @new. If @new is not %NULL, @src have to be the
! 1168: * same value as @new->attrs->proto. @p specifies the protocol that called
! 1169: * rte_update(). In most cases it is the same protocol as @src. rte_update()
! 1170: * stores @p in @new->sender;
! 1171: *
! 1172: * When rte_update() gets any route, it automatically validates it (checks,
! 1173: * whether the network and next hop address are valid IP addresses and also
! 1174: * whether a normal routing protocol doesn't try to smuggle a host or link
! 1175: * scope route to the table), converts all protocol dependent attributes stored
! 1176: * in the &rte to temporary extended attributes, consults import filters of the
! 1177: * protocol to see if the route should be accepted and/or its attributes modified,
! 1178: * stores the temporary attributes back to the &rte.
! 1179: *
! 1180: * Now, having a "public" version of the route, we
! 1181: * automatically find any old route defined by the protocol @src
! 1182: * for network @n, replace it by the new one (or removing it if @new is %NULL),
! 1183: * recalculate the optimal route for this destination and finally broadcast
! 1184: * the change (if any) to all routing protocols by calling rte_announce().
! 1185: *
! 1186: * All memory used for attribute lists and other temporary allocations is taken
! 1187: * from a special linear pool @rte_update_pool and freed when rte_update()
! 1188: * finishes.
! 1189: */
! 1190:
! 1191: void
! 1192: rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
! 1193: {
! 1194: struct proto *p = ah->proto;
! 1195: struct proto_stats *stats = ah->stats;
! 1196: struct filter *filter = ah->in_filter;
! 1197: ea_list *tmpa = NULL;
! 1198: rte *dummy = NULL;
! 1199:
! 1200: rte_update_lock();
! 1201: if (new)
! 1202: {
! 1203: new->sender = ah;
! 1204:
! 1205: stats->imp_updates_received++;
! 1206: if (!rte_validate(new))
! 1207: {
! 1208: rte_trace_in(D_FILTERS, p, new, "invalid");
! 1209: stats->imp_updates_invalid++;
! 1210: goto drop;
! 1211: }
! 1212:
! 1213: if (filter == FILTER_REJECT)
! 1214: {
! 1215: stats->imp_updates_filtered++;
! 1216: rte_trace_in(D_FILTERS, p, new, "filtered out");
! 1217:
! 1218: if (! ah->in_keep_filtered)
! 1219: goto drop;
! 1220:
! 1221: /* new is a private copy, i could modify it */
! 1222: new->flags |= REF_FILTERED;
! 1223: }
! 1224: else
! 1225: {
! 1226: tmpa = make_tmp_attrs(new, rte_update_pool);
! 1227: if (filter && (filter != FILTER_REJECT))
! 1228: {
! 1229: ea_list *old_tmpa = tmpa;
! 1230: int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
! 1231: if (fr > F_ACCEPT)
! 1232: {
! 1233: stats->imp_updates_filtered++;
! 1234: rte_trace_in(D_FILTERS, p, new, "filtered out");
! 1235:
! 1236: if (! ah->in_keep_filtered)
! 1237: goto drop;
! 1238:
! 1239: new->flags |= REF_FILTERED;
! 1240: }
! 1241: if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
! 1242: src->proto->store_tmp_attrs(new, tmpa);
! 1243: }
! 1244: }
! 1245: if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
! 1246: new->attrs = rta_lookup(new->attrs);
! 1247: new->flags |= REF_COW;
! 1248: }
! 1249: else
! 1250: {
! 1251: stats->imp_withdraws_received++;
! 1252:
! 1253: if (!net || !src)
! 1254: {
! 1255: stats->imp_withdraws_ignored++;
! 1256: rte_update_unlock();
! 1257: return;
! 1258: }
! 1259: }
! 1260:
! 1261: recalc:
! 1262: rte_hide_dummy_routes(net, &dummy);
! 1263: rte_recalculate(ah, net, new, src);
! 1264: rte_unhide_dummy_routes(net, &dummy);
! 1265: rte_update_unlock();
! 1266: return;
! 1267:
! 1268: drop:
! 1269: rte_free(new);
! 1270: new = NULL;
! 1271: goto recalc;
! 1272: }
! 1273:
! 1274: /* Independent call to rte_announce(), used from next hop
! 1275: recalculation, outside of rte_update(). new must be non-NULL */
! 1276: static inline void
! 1277: rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old,
! 1278: rte *new_best, rte *old_best)
! 1279: {
! 1280: rte_update_lock();
! 1281: rte_announce(tab, type, net, new, old, new_best, old_best, NULL);
! 1282: rte_update_unlock();
! 1283: }
! 1284:
! 1285: static inline void
! 1286: rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
! 1287: {
! 1288: rte_update_lock();
! 1289: rte_recalculate(old->sender, old->net, NULL, old->attrs->src);
! 1290: rte_update_unlock();
! 1291: }
! 1292:
! 1293: /* Check rtable for best route to given net whether it would be exported do p */
! 1294: int
! 1295: rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter)
! 1296: {
! 1297: net *n = net_find(t, prefix, pxlen);
! 1298: rte *rt = n ? n->routes : NULL;
! 1299:
! 1300: if (!rte_is_valid(rt))
! 1301: return 0;
! 1302:
! 1303: rte_update_lock();
! 1304:
! 1305: /* Rest is stripped down export_filter() */
! 1306: ea_list *tmpa = make_tmp_attrs(rt, rte_update_pool);
! 1307: int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
! 1308: if (v == RIC_PROCESS)
! 1309: v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
! 1310:
! 1311: /* Discard temporary rte */
! 1312: if (rt != n->routes)
! 1313: rte_free(rt);
! 1314:
! 1315: rte_update_unlock();
! 1316:
! 1317: return v > 0;
! 1318: }
! 1319:
! 1320:
! 1321: /**
! 1322: * rt_refresh_begin - start a refresh cycle
! 1323: * @t: related routing table
! 1324: * @ah: related announce hook
! 1325: *
! 1326: * This function starts a refresh cycle for given routing table and announce
! 1327: * hook. The refresh cycle is a sequence where the protocol sends all its valid
! 1328: * routes to the routing table (by rte_update()). After that, all protocol
! 1329: * routes (more precisely routes with @ah as @sender) not sent during the
! 1330: * refresh cycle but still in the table from the past are pruned. This is
! 1331: * implemented by marking all related routes as stale by REF_STALE flag in
! 1332: * rt_refresh_begin(), then marking all related stale routes with REF_DISCARD
! 1333: * flag in rt_refresh_end() and then removing such routes in the prune loop.
! 1334: */
! 1335: void
! 1336: rt_refresh_begin(rtable *t, struct announce_hook *ah)
! 1337: {
! 1338: net *n;
! 1339: rte *e;
! 1340:
! 1341: FIB_WALK(&t->fib, fn)
! 1342: {
! 1343: n = (net *) fn;
! 1344: for (e = n->routes; e; e = e->next)
! 1345: if (e->sender == ah)
! 1346: e->flags |= REF_STALE;
! 1347: }
! 1348: FIB_WALK_END;
! 1349: }
! 1350:
! 1351: /**
! 1352: * rt_refresh_end - end a refresh cycle
! 1353: * @t: related routing table
! 1354: * @ah: related announce hook
! 1355: *
! 1356: * This function starts a refresh cycle for given routing table and announce
! 1357: * hook. See rt_refresh_begin() for description of refresh cycles.
! 1358: */
! 1359: void
! 1360: rt_refresh_end(rtable *t, struct announce_hook *ah)
! 1361: {
! 1362: int prune = 0;
! 1363: net *n;
! 1364: rte *e;
! 1365:
! 1366: FIB_WALK(&t->fib, fn)
! 1367: {
! 1368: n = (net *) fn;
! 1369: for (e = n->routes; e; e = e->next)
! 1370: if ((e->sender == ah) && (e->flags & REF_STALE))
! 1371: {
! 1372: e->flags |= REF_DISCARD;
! 1373: prune = 1;
! 1374: }
! 1375: }
! 1376: FIB_WALK_END;
! 1377:
! 1378: if (prune)
! 1379: rt_schedule_prune(t);
! 1380: }
! 1381:
! 1382:
! 1383: /**
! 1384: * rte_dump - dump a route
! 1385: * @e: &rte to be dumped
! 1386: *
! 1387: * This functions dumps contents of a &rte to debug output.
! 1388: */
! 1389: void
! 1390: rte_dump(rte *e)
! 1391: {
! 1392: net *n = e->net;
! 1393: debug("%-1I/%2d ", n->n.prefix, n->n.pxlen);
! 1394: debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod);
! 1395: rta_dump(e->attrs);
! 1396: if (e->attrs->src->proto->proto->dump_attrs)
! 1397: e->attrs->src->proto->proto->dump_attrs(e);
! 1398: debug("\n");
! 1399: }
! 1400:
! 1401: /**
! 1402: * rt_dump - dump a routing table
! 1403: * @t: routing table to be dumped
! 1404: *
! 1405: * This function dumps contents of a given routing table to debug output.
! 1406: */
! 1407: void
! 1408: rt_dump(rtable *t)
! 1409: {
! 1410: rte *e;
! 1411: net *n;
! 1412: struct announce_hook *a;
! 1413:
! 1414: debug("Dump of routing table <%s>\n", t->name);
! 1415: #ifdef DEBUGGING
! 1416: fib_check(&t->fib);
! 1417: #endif
! 1418: FIB_WALK(&t->fib, fn)
! 1419: {
! 1420: n = (net *) fn;
! 1421: for(e=n->routes; e; e=e->next)
! 1422: rte_dump(e);
! 1423: }
! 1424: FIB_WALK_END;
! 1425: WALK_LIST(a, t->hooks)
! 1426: debug("\tAnnounces routes to protocol %s\n", a->proto->name);
! 1427: debug("\n");
! 1428: }
! 1429:
! 1430: /**
! 1431: * rt_dump_all - dump all routing tables
! 1432: *
! 1433: * This function dumps contents of all routing tables to debug output.
! 1434: */
! 1435: void
! 1436: rt_dump_all(void)
! 1437: {
! 1438: rtable *t;
! 1439:
! 1440: WALK_LIST(t, routing_tables)
! 1441: rt_dump(t);
! 1442: }
! 1443:
! 1444: static inline void
! 1445: rt_schedule_prune(rtable *tab)
! 1446: {
! 1447: rt_mark_for_prune(tab);
! 1448: ev_schedule(tab->rt_event);
! 1449: }
! 1450:
! 1451: static inline void
! 1452: rt_schedule_gc(rtable *tab)
! 1453: {
! 1454: if (tab->gc_scheduled)
! 1455: return;
! 1456:
! 1457: tab->gc_scheduled = 1;
! 1458: ev_schedule(tab->rt_event);
! 1459: }
! 1460:
! 1461: static inline void
! 1462: rt_schedule_hcu(rtable *tab)
! 1463: {
! 1464: if (tab->hcu_scheduled)
! 1465: return;
! 1466:
! 1467: tab->hcu_scheduled = 1;
! 1468: ev_schedule(tab->rt_event);
! 1469: }
! 1470:
! 1471: static inline void
! 1472: rt_schedule_nhu(rtable *tab)
! 1473: {
! 1474: if (tab->nhu_state == 0)
! 1475: ev_schedule(tab->rt_event);
! 1476:
! 1477: /* state change 0->1, 2->3 */
! 1478: tab->nhu_state |= 1;
! 1479: }
! 1480:
! 1481:
! 1482: static void
! 1483: rt_prune_nets(rtable *tab)
! 1484: {
! 1485: struct fib_iterator fit;
! 1486: int ncnt = 0, ndel = 0;
! 1487:
! 1488: #ifdef DEBUGGING
! 1489: fib_check(&tab->fib);
! 1490: #endif
! 1491:
! 1492: FIB_ITERATE_INIT(&fit, &tab->fib);
! 1493: again:
! 1494: FIB_ITERATE_START(&tab->fib, &fit, f)
! 1495: {
! 1496: net *n = (net *) f;
! 1497: ncnt++;
! 1498: if (!n->routes) /* Orphaned FIB entry */
! 1499: {
! 1500: FIB_ITERATE_PUT(&fit, f);
! 1501: fib_delete(&tab->fib, f);
! 1502: ndel++;
! 1503: goto again;
! 1504: }
! 1505: }
! 1506: FIB_ITERATE_END(f);
! 1507: DBG("Pruned %d of %d networks\n", ndel, ncnt);
! 1508:
! 1509: tab->gc_counter = 0;
! 1510: tab->gc_time = now;
! 1511: tab->gc_scheduled = 0;
! 1512: }
! 1513:
! 1514: static void
! 1515: rt_event(void *ptr)
! 1516: {
! 1517: rtable *tab = ptr;
! 1518:
! 1519: if (tab->hcu_scheduled)
! 1520: rt_update_hostcache(tab);
! 1521:
! 1522: if (tab->nhu_state)
! 1523: rt_next_hop_update(tab);
! 1524:
! 1525: if (tab->prune_state)
! 1526: if (!rt_prune_table(tab))
! 1527: {
! 1528: /* Table prune unfinished */
! 1529: ev_schedule(tab->rt_event);
! 1530: return;
! 1531: }
! 1532:
! 1533: if (tab->gc_scheduled)
! 1534: {
! 1535: rt_prune_nets(tab);
! 1536: rt_prune_sources(); // FIXME this should be moved to independent event
! 1537: }
! 1538: }
! 1539:
! 1540: void
! 1541: rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf)
! 1542: {
! 1543: bzero(t, sizeof(*t));
! 1544: fib_init(&t->fib, p, sizeof(net), 0, rte_init);
! 1545: t->name = name;
! 1546: t->config = cf;
! 1547: init_list(&t->hooks);
! 1548: if (cf)
! 1549: {
! 1550: t->rt_event = ev_new(p);
! 1551: t->rt_event->hook = rt_event;
! 1552: t->rt_event->data = t;
! 1553: t->gc_time = now;
! 1554: }
! 1555: }
! 1556:
! 1557: /**
! 1558: * rt_init - initialize routing tables
! 1559: *
! 1560: * This function is called during BIRD startup. It initializes the
! 1561: * routing table module.
! 1562: */
! 1563: void
! 1564: rt_init(void)
! 1565: {
! 1566: rta_init();
! 1567: rt_table_pool = rp_new(&root_pool, "Routing tables");
! 1568: rte_update_pool = lp_new(rt_table_pool, 4080);
! 1569: rte_slab = sl_new(rt_table_pool, sizeof(rte));
! 1570: init_list(&routing_tables);
! 1571: }
! 1572:
! 1573:
! 1574: static int
! 1575: rt_prune_step(rtable *tab, int *limit)
! 1576: {
! 1577: struct fib_iterator *fit = &tab->prune_fit;
! 1578:
! 1579: DBG("Pruning route table %s\n", tab->name);
! 1580: #ifdef DEBUGGING
! 1581: fib_check(&tab->fib);
! 1582: #endif
! 1583:
! 1584: if (tab->prune_state == RPS_NONE)
! 1585: return 1;
! 1586:
! 1587: if (tab->prune_state == RPS_SCHEDULED)
! 1588: {
! 1589: FIB_ITERATE_INIT(fit, &tab->fib);
! 1590: tab->prune_state = RPS_RUNNING;
! 1591: }
! 1592:
! 1593: again:
! 1594: FIB_ITERATE_START(&tab->fib, fit, fn)
! 1595: {
! 1596: net *n = (net *) fn;
! 1597: rte *e;
! 1598:
! 1599: rescan:
! 1600: for (e=n->routes; e; e=e->next)
! 1601: if (e->sender->proto->flushing || (e->flags & REF_DISCARD))
! 1602: {
! 1603: if (*limit <= 0)
! 1604: {
! 1605: FIB_ITERATE_PUT(fit, fn);
! 1606: return 0;
! 1607: }
! 1608:
! 1609: rte_discard(e);
! 1610: (*limit)--;
! 1611:
! 1612: goto rescan;
! 1613: }
! 1614: if (!n->routes) /* Orphaned FIB entry */
! 1615: {
! 1616: FIB_ITERATE_PUT(fit, fn);
! 1617: fib_delete(&tab->fib, fn);
! 1618: goto again;
! 1619: }
! 1620: }
! 1621: FIB_ITERATE_END(fn);
! 1622:
! 1623: #ifdef DEBUGGING
! 1624: fib_check(&tab->fib);
! 1625: #endif
! 1626:
! 1627: tab->prune_state = RPS_NONE;
! 1628: return 1;
! 1629: }
! 1630:
! 1631: /**
! 1632: * rt_prune_table - prune a routing table
! 1633: * @tab: a routing table for pruning
! 1634: *
! 1635: * This function scans the routing table @tab and removes routes belonging to
! 1636: * flushing protocols, discarded routes and also stale network entries, in a
! 1637: * similar fashion like rt_prune_loop(). Returns 1 when all such routes are
! 1638: * pruned. Contrary to rt_prune_loop(), this function is not a part of the
! 1639: * protocol flushing loop, but it is called from rt_event() for just one routing
! 1640: * table.
! 1641: *
! 1642: * Note that rt_prune_table() and rt_prune_loop() share (for each table) the
! 1643: * prune state (@prune_state) and also the pruning iterator (@prune_fit).
! 1644: */
! 1645: static inline int
! 1646: rt_prune_table(rtable *tab)
! 1647: {
! 1648: int limit = 512;
! 1649: return rt_prune_step(tab, &limit);
! 1650: }
! 1651:
! 1652: /**
! 1653: * rt_prune_loop - prune routing tables
! 1654: *
! 1655: * The prune loop scans routing tables and removes routes belonging to flushing
! 1656: * protocols, discarded routes and also stale network entries. Returns 1 when
! 1657: * all such routes are pruned. It is a part of the protocol flushing loop.
! 1658: */
! 1659: int
! 1660: rt_prune_loop(void)
! 1661: {
! 1662: int limit = 512;
! 1663: rtable *t;
! 1664:
! 1665: WALK_LIST(t, routing_tables)
! 1666: if (! rt_prune_step(t, &limit))
! 1667: return 0;
! 1668:
! 1669: return 1;
! 1670: }
! 1671:
! 1672: void
! 1673: rt_preconfig(struct config *c)
! 1674: {
! 1675: struct symbol *s = cf_get_symbol("master");
! 1676:
! 1677: init_list(&c->tables);
! 1678: c->master_rtc = rt_new_table(s);
! 1679: }
! 1680:
! 1681:
! 1682: /*
! 1683: * Some functions for handing internal next hop updates
! 1684: * triggered by rt_schedule_nhu().
! 1685: */
! 1686:
! 1687: static inline int
! 1688: rta_next_hop_outdated(rta *a)
! 1689: {
! 1690: struct hostentry *he = a->hostentry;
! 1691:
! 1692: if (!he)
! 1693: return 0;
! 1694:
! 1695: if (!he->src)
! 1696: return a->dest != RTD_UNREACHABLE;
! 1697:
! 1698: return (a->iface != he->src->iface) || !ipa_equal(a->gw, he->gw) ||
! 1699: (a->dest != he->dest) || (a->igp_metric != he->igp_metric) ||
! 1700: !mpnh_same(a->nexthops, he->src->nexthops);
! 1701: }
! 1702:
! 1703: static inline void
! 1704: rta_apply_hostentry(rta *a, struct hostentry *he)
! 1705: {
! 1706: a->hostentry = he;
! 1707: a->iface = he->src ? he->src->iface : NULL;
! 1708: a->gw = he->gw;
! 1709: a->dest = he->dest;
! 1710: a->igp_metric = he->igp_metric;
! 1711: a->nexthops = he->src ? he->src->nexthops : NULL;
! 1712: }
! 1713:
! 1714: static inline rte *
! 1715: rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
! 1716: {
! 1717: rta a;
! 1718: memcpy(&a, old->attrs, sizeof(rta));
! 1719: rta_apply_hostentry(&a, old->attrs->hostentry);
! 1720: a.aflags = 0;
! 1721:
! 1722: rte *e = sl_alloc(rte_slab);
! 1723: memcpy(e, old, sizeof(rte));
! 1724: e->attrs = rta_lookup(&a);
! 1725:
! 1726: return e;
! 1727: }
! 1728:
! 1729: static inline int
! 1730: rt_next_hop_update_net(rtable *tab, net *n)
! 1731: {
! 1732: rte **k, *e, *new, *old_best, **new_best;
! 1733: int count = 0;
! 1734: int free_old_best = 0;
! 1735:
! 1736: old_best = n->routes;
! 1737: if (!old_best)
! 1738: return 0;
! 1739:
! 1740: for (k = &n->routes; e = *k; k = &e->next)
! 1741: if (rta_next_hop_outdated(e->attrs))
! 1742: {
! 1743: new = rt_next_hop_update_rte(tab, e);
! 1744: *k = new;
! 1745:
! 1746: rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL);
! 1747: rte_trace_in(D_ROUTES, new->sender->proto, new, "updated");
! 1748:
! 1749: /* Call a pre-comparison hook */
! 1750: /* Not really an efficient way to compute this */
! 1751: if (e->attrs->src->proto->rte_recalculate)
! 1752: e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL);
! 1753:
! 1754: if (e != old_best)
! 1755: rte_free_quick(e);
! 1756: else /* Freeing of the old best rte is postponed */
! 1757: free_old_best = 1;
! 1758:
! 1759: e = new;
! 1760: count++;
! 1761: }
! 1762:
! 1763: if (!count)
! 1764: return 0;
! 1765:
! 1766: /* Find the new best route */
! 1767: new_best = NULL;
! 1768: for (k = &n->routes; e = *k; k = &e->next)
! 1769: {
! 1770: if (!new_best || rte_better(e, *new_best))
! 1771: new_best = k;
! 1772: }
! 1773:
! 1774: /* Relink the new best route to the first position */
! 1775: new = *new_best;
! 1776: if (new != n->routes)
! 1777: {
! 1778: *new_best = new->next;
! 1779: new->next = n->routes;
! 1780: n->routes = new;
! 1781: }
! 1782:
! 1783: /* Announce the new best route */
! 1784: if (new != old_best)
! 1785: {
! 1786: rte_announce_i(tab, RA_OPTIMAL, n, new, old_best, NULL, NULL);
! 1787: rte_trace_in(D_ROUTES, new->sender->proto, new, "updated [best]");
! 1788: }
! 1789:
! 1790: /* FIXME: Better announcement of merged routes */
! 1791: rte_announce_i(tab, RA_MERGED, n, new, old_best, new, old_best);
! 1792:
! 1793: if (free_old_best)
! 1794: rte_free_quick(old_best);
! 1795:
! 1796: return count;
! 1797: }
! 1798:
! 1799: static void
! 1800: rt_next_hop_update(rtable *tab)
! 1801: {
! 1802: struct fib_iterator *fit = &tab->nhu_fit;
! 1803: int max_feed = 32;
! 1804:
! 1805: if (tab->nhu_state == 0)
! 1806: return;
! 1807:
! 1808: if (tab->nhu_state == 1)
! 1809: {
! 1810: FIB_ITERATE_INIT(fit, &tab->fib);
! 1811: tab->nhu_state = 2;
! 1812: }
! 1813:
! 1814: FIB_ITERATE_START(&tab->fib, fit, fn)
! 1815: {
! 1816: if (max_feed <= 0)
! 1817: {
! 1818: FIB_ITERATE_PUT(fit, fn);
! 1819: ev_schedule(tab->rt_event);
! 1820: return;
! 1821: }
! 1822: max_feed -= rt_next_hop_update_net(tab, (net *) fn);
! 1823: }
! 1824: FIB_ITERATE_END(fn);
! 1825:
! 1826: /* state change 2->0, 3->1 */
! 1827: tab->nhu_state &= 1;
! 1828:
! 1829: if (tab->nhu_state > 0)
! 1830: ev_schedule(tab->rt_event);
! 1831: }
! 1832:
! 1833:
! 1834: struct rtable_config *
! 1835: rt_new_table(struct symbol *s)
! 1836: {
! 1837: /* Hack that allows to 'redefine' the master table */
! 1838: if ((s->class == SYM_TABLE) && (s->def == new_config->master_rtc))
! 1839: return s->def;
! 1840:
! 1841: struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
! 1842:
! 1843: cf_define_symbol(s, SYM_TABLE, c);
! 1844: c->name = s->name;
! 1845: add_tail(&new_config->tables, &c->n);
! 1846: c->gc_max_ops = 1000;
! 1847: c->gc_min_time = 5;
! 1848: return c;
! 1849: }
! 1850:
! 1851: /**
! 1852: * rt_lock_table - lock a routing table
! 1853: * @r: routing table to be locked
! 1854: *
! 1855: * Lock a routing table, because it's in use by a protocol,
! 1856: * preventing it from being freed when it gets undefined in a new
! 1857: * configuration.
! 1858: */
! 1859: void
! 1860: rt_lock_table(rtable *r)
! 1861: {
! 1862: r->use_count++;
! 1863: }
! 1864:
! 1865: /**
! 1866: * rt_unlock_table - unlock a routing table
! 1867: * @r: routing table to be unlocked
! 1868: *
! 1869: * Unlock a routing table formerly locked by rt_lock_table(),
! 1870: * that is decrease its use count and delete it if it's scheduled
! 1871: * for deletion by configuration changes.
! 1872: */
! 1873: void
! 1874: rt_unlock_table(rtable *r)
! 1875: {
! 1876: if (!--r->use_count && r->deleted)
! 1877: {
! 1878: struct config *conf = r->deleted;
! 1879: DBG("Deleting routing table %s\n", r->name);
! 1880: r->config->table = NULL;
! 1881: if (r->hostcache)
! 1882: rt_free_hostcache(r);
! 1883: rem_node(&r->n);
! 1884: fib_free(&r->fib);
! 1885: rfree(r->rt_event);
! 1886: mb_free(r);
! 1887: config_del_obstacle(conf);
! 1888: }
! 1889: }
! 1890:
! 1891: /**
! 1892: * rt_commit - commit new routing table configuration
! 1893: * @new: new configuration
! 1894: * @old: original configuration or %NULL if it's boot time config
! 1895: *
! 1896: * Scan differences between @old and @new configuration and modify
! 1897: * the routing tables according to these changes. If @new defines a
! 1898: * previously unknown table, create it, if it omits a table existing
! 1899: * in @old, schedule it for deletion (it gets deleted when all protocols
! 1900: * disconnect from it by calling rt_unlock_table()), if it exists
! 1901: * in both configurations, leave it unchanged.
! 1902: */
! 1903: void
! 1904: rt_commit(struct config *new, struct config *old)
! 1905: {
! 1906: struct rtable_config *o, *r;
! 1907:
! 1908: DBG("rt_commit:\n");
! 1909: if (old)
! 1910: {
! 1911: WALK_LIST(o, old->tables)
! 1912: {
! 1913: rtable *ot = o->table;
! 1914: if (!ot->deleted)
! 1915: {
! 1916: struct symbol *sym = cf_find_symbol(new, o->name);
! 1917: if (sym && sym->class == SYM_TABLE && !new->shutdown)
! 1918: {
! 1919: DBG("\t%s: same\n", o->name);
! 1920: r = sym->def;
! 1921: r->table = ot;
! 1922: ot->name = r->name;
! 1923: ot->config = r;
! 1924: if (o->sorted != r->sorted)
! 1925: log(L_WARN "Reconfiguration of rtable sorted flag not implemented");
! 1926: }
! 1927: else
! 1928: {
! 1929: DBG("\t%s: deleted\n", o->name);
! 1930: ot->deleted = old;
! 1931: config_add_obstacle(old);
! 1932: rt_lock_table(ot);
! 1933: rt_unlock_table(ot);
! 1934: }
! 1935: }
! 1936: }
! 1937: }
! 1938:
! 1939: WALK_LIST(r, new->tables)
! 1940: if (!r->table)
! 1941: {
! 1942: rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
! 1943: DBG("\t%s: created\n", r->name);
! 1944: rt_setup(rt_table_pool, t, r->name, r);
! 1945: add_tail(&routing_tables, &t->n);
! 1946: r->table = t;
! 1947: }
! 1948: DBG("\tdone\n");
! 1949: }
! 1950:
! 1951: static inline void
! 1952: do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
! 1953: {
! 1954: rte_update_lock();
! 1955: if (type == RA_ACCEPTED)
! 1956: rt_notify_accepted(h, n, e, NULL, NULL, p->refeeding ? 2 : 1);
! 1957: else if (type == RA_MERGED)
! 1958: rt_notify_merged(h, n, NULL, NULL, e, p->refeeding ? e : NULL, p->refeeding);
! 1959: else
! 1960: rt_notify_basic(h, n, e, p->refeeding ? e : NULL, p->refeeding);
! 1961: rte_update_unlock();
! 1962: }
! 1963:
! 1964: /**
! 1965: * rt_feed_baby - advertise routes to a new protocol
! 1966: * @p: protocol to be fed
! 1967: *
! 1968: * This function performs one pass of advertisement of routes to a newly
! 1969: * initialized protocol. It's called by the protocol code as long as it
! 1970: * has something to do. (We avoid transferring all the routes in single
! 1971: * pass in order not to monopolize CPU time.)
! 1972: */
! 1973: int
! 1974: rt_feed_baby(struct proto *p)
! 1975: {
! 1976: struct announce_hook *h;
! 1977: struct fib_iterator *fit;
! 1978: int max_feed = 256;
! 1979:
! 1980: if (!p->feed_ahook) /* Need to initialize first */
! 1981: {
! 1982: if (!p->ahooks)
! 1983: return 1;
! 1984: DBG("Announcing routes to new protocol %s\n", p->name);
! 1985: p->feed_ahook = p->ahooks;
! 1986: fit = p->feed_iterator = mb_alloc(p->pool, sizeof(struct fib_iterator));
! 1987: goto next_hook;
! 1988: }
! 1989: fit = p->feed_iterator;
! 1990:
! 1991: again:
! 1992: h = p->feed_ahook;
! 1993: FIB_ITERATE_START(&h->table->fib, fit, fn)
! 1994: {
! 1995: net *n = (net *) fn;
! 1996: rte *e = n->routes;
! 1997: if (max_feed <= 0)
! 1998: {
! 1999: FIB_ITERATE_PUT(fit, fn);
! 2000: return 0;
! 2001: }
! 2002:
! 2003: /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
! 2004:
! 2005: if ((p->accept_ra_types == RA_OPTIMAL) ||
! 2006: (p->accept_ra_types == RA_ACCEPTED) ||
! 2007: (p->accept_ra_types == RA_MERGED))
! 2008: if (rte_is_valid(e))
! 2009: {
! 2010: if (p->export_state != ES_FEEDING)
! 2011: return 1; /* In the meantime, the protocol fell down. */
! 2012:
! 2013: do_feed_baby(p, p->accept_ra_types, h, n, e);
! 2014: max_feed--;
! 2015: }
! 2016:
! 2017: if (p->accept_ra_types == RA_ANY)
! 2018: for(e = n->routes; e; e = e->next)
! 2019: {
! 2020: if (p->export_state != ES_FEEDING)
! 2021: return 1; /* In the meantime, the protocol fell down. */
! 2022:
! 2023: if (!rte_is_valid(e))
! 2024: continue;
! 2025:
! 2026: do_feed_baby(p, RA_ANY, h, n, e);
! 2027: max_feed--;
! 2028: }
! 2029: }
! 2030: FIB_ITERATE_END(fn);
! 2031: p->feed_ahook = h->next;
! 2032: if (!p->feed_ahook)
! 2033: {
! 2034: mb_free(p->feed_iterator);
! 2035: p->feed_iterator = NULL;
! 2036: return 1;
! 2037: }
! 2038:
! 2039: next_hook:
! 2040: h = p->feed_ahook;
! 2041: FIB_ITERATE_INIT(fit, &h->table->fib);
! 2042: goto again;
! 2043: }
! 2044:
! 2045: /**
! 2046: * rt_feed_baby_abort - abort protocol feeding
! 2047: * @p: protocol
! 2048: *
! 2049: * This function is called by the protocol code when the protocol
! 2050: * stops or ceases to exist before the last iteration of rt_feed_baby()
! 2051: * has finished.
! 2052: */
! 2053: void
! 2054: rt_feed_baby_abort(struct proto *p)
! 2055: {
! 2056: if (p->feed_ahook)
! 2057: {
! 2058: /* Unlink the iterator and exit */
! 2059: fit_get(&p->feed_ahook->table->fib, p->feed_iterator);
! 2060: p->feed_ahook = NULL;
! 2061: }
! 2062: }
! 2063:
! 2064:
! 2065: static inline unsigned
! 2066: ptr_hash(void *ptr)
! 2067: {
! 2068: uintptr_t p = (uintptr_t) ptr;
! 2069: return p ^ (p << 8) ^ (p >> 16);
! 2070: }
! 2071:
! 2072: static inline unsigned
! 2073: hc_hash(ip_addr a, rtable *dep)
! 2074: {
! 2075: return (ipa_hash(a) ^ ptr_hash(dep)) & 0xffff;
! 2076: }
! 2077:
! 2078: static inline void
! 2079: hc_insert(struct hostcache *hc, struct hostentry *he)
! 2080: {
! 2081: uint k = he->hash_key >> hc->hash_shift;
! 2082: he->next = hc->hash_table[k];
! 2083: hc->hash_table[k] = he;
! 2084: }
! 2085:
! 2086: static inline void
! 2087: hc_remove(struct hostcache *hc, struct hostentry *he)
! 2088: {
! 2089: struct hostentry **hep;
! 2090: uint k = he->hash_key >> hc->hash_shift;
! 2091:
! 2092: for (hep = &hc->hash_table[k]; *hep != he; hep = &(*hep)->next);
! 2093: *hep = he->next;
! 2094: }
! 2095:
! 2096: #define HC_DEF_ORDER 10
! 2097: #define HC_HI_MARK *4
! 2098: #define HC_HI_STEP 2
! 2099: #define HC_HI_ORDER 16 /* Must be at most 16 */
! 2100: #define HC_LO_MARK /5
! 2101: #define HC_LO_STEP 2
! 2102: #define HC_LO_ORDER 10
! 2103:
! 2104: static void
! 2105: hc_alloc_table(struct hostcache *hc, unsigned order)
! 2106: {
! 2107: uint hsize = 1 << order;
! 2108: hc->hash_order = order;
! 2109: hc->hash_shift = 16 - order;
! 2110: hc->hash_max = (order >= HC_HI_ORDER) ? ~0U : (hsize HC_HI_MARK);
! 2111: hc->hash_min = (order <= HC_LO_ORDER) ? 0U : (hsize HC_LO_MARK);
! 2112:
! 2113: hc->hash_table = mb_allocz(rt_table_pool, hsize * sizeof(struct hostentry *));
! 2114: }
! 2115:
! 2116: static void
! 2117: hc_resize(struct hostcache *hc, unsigned new_order)
! 2118: {
! 2119: struct hostentry **old_table = hc->hash_table;
! 2120: struct hostentry *he, *hen;
! 2121: uint old_size = 1 << hc->hash_order;
! 2122: uint i;
! 2123:
! 2124: hc_alloc_table(hc, new_order);
! 2125: for (i = 0; i < old_size; i++)
! 2126: for (he = old_table[i]; he != NULL; he=hen)
! 2127: {
! 2128: hen = he->next;
! 2129: hc_insert(hc, he);
! 2130: }
! 2131: mb_free(old_table);
! 2132: }
! 2133:
! 2134: static struct hostentry *
! 2135: hc_new_hostentry(struct hostcache *hc, ip_addr a, ip_addr ll, rtable *dep, unsigned k)
! 2136: {
! 2137: struct hostentry *he = sl_alloc(hc->slab);
! 2138:
! 2139: he->addr = a;
! 2140: he->link = ll;
! 2141: he->tab = dep;
! 2142: he->hash_key = k;
! 2143: he->uc = 0;
! 2144: he->src = NULL;
! 2145:
! 2146: add_tail(&hc->hostentries, &he->ln);
! 2147: hc_insert(hc, he);
! 2148:
! 2149: hc->hash_items++;
! 2150: if (hc->hash_items > hc->hash_max)
! 2151: hc_resize(hc, hc->hash_order + HC_HI_STEP);
! 2152:
! 2153: return he;
! 2154: }
! 2155:
! 2156: static void
! 2157: hc_delete_hostentry(struct hostcache *hc, struct hostentry *he)
! 2158: {
! 2159: rta_free(he->src);
! 2160:
! 2161: rem_node(&he->ln);
! 2162: hc_remove(hc, he);
! 2163: sl_free(hc->slab, he);
! 2164:
! 2165: hc->hash_items--;
! 2166: if (hc->hash_items < hc->hash_min)
! 2167: hc_resize(hc, hc->hash_order - HC_LO_STEP);
! 2168: }
! 2169:
! 2170: static void
! 2171: rt_init_hostcache(rtable *tab)
! 2172: {
! 2173: struct hostcache *hc = mb_allocz(rt_table_pool, sizeof(struct hostcache));
! 2174: init_list(&hc->hostentries);
! 2175:
! 2176: hc->hash_items = 0;
! 2177: hc_alloc_table(hc, HC_DEF_ORDER);
! 2178: hc->slab = sl_new(rt_table_pool, sizeof(struct hostentry));
! 2179:
! 2180: hc->lp = lp_new(rt_table_pool, 1008);
! 2181: hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
! 2182:
! 2183: tab->hostcache = hc;
! 2184: }
! 2185:
! 2186: static void
! 2187: rt_free_hostcache(rtable *tab)
! 2188: {
! 2189: struct hostcache *hc = tab->hostcache;
! 2190:
! 2191: node *n;
! 2192: WALK_LIST(n, hc->hostentries)
! 2193: {
! 2194: struct hostentry *he = SKIP_BACK(struct hostentry, ln, n);
! 2195: rta_free(he->src);
! 2196:
! 2197: if (he->uc)
! 2198: log(L_ERR "Hostcache is not empty in table %s", tab->name);
! 2199: }
! 2200:
! 2201: rfree(hc->slab);
! 2202: rfree(hc->lp);
! 2203: mb_free(hc->hash_table);
! 2204: mb_free(hc);
! 2205: }
! 2206:
! 2207: static void
! 2208: rt_notify_hostcache(rtable *tab, net *net)
! 2209: {
! 2210: struct hostcache *hc = tab->hostcache;
! 2211:
! 2212: if (tab->hcu_scheduled)
! 2213: return;
! 2214:
! 2215: if (trie_match_prefix(hc->trie, net->n.prefix, net->n.pxlen))
! 2216: rt_schedule_hcu(tab);
! 2217: }
! 2218:
! 2219: static int
! 2220: if_local_addr(ip_addr a, struct iface *i)
! 2221: {
! 2222: struct ifa *b;
! 2223:
! 2224: WALK_LIST(b, i->addrs)
! 2225: if (ipa_equal(a, b->ip))
! 2226: return 1;
! 2227:
! 2228: return 0;
! 2229: }
! 2230:
! 2231: static u32
! 2232: rt_get_igp_metric(rte *rt)
! 2233: {
! 2234: eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
! 2235:
! 2236: if (ea)
! 2237: return ea->u.data;
! 2238:
! 2239: rta *a = rt->attrs;
! 2240:
! 2241: #ifdef CONFIG_OSPF
! 2242: if ((a->source == RTS_OSPF) ||
! 2243: (a->source == RTS_OSPF_IA) ||
! 2244: (a->source == RTS_OSPF_EXT1))
! 2245: return rt->u.ospf.metric1;
! 2246: #endif
! 2247:
! 2248: #ifdef CONFIG_RIP
! 2249: if (a->source == RTS_RIP)
! 2250: return rt->u.rip.metric;
! 2251: #endif
! 2252:
! 2253: /* Device routes */
! 2254: if ((a->dest != RTD_ROUTER) && (a->dest != RTD_MULTIPATH))
! 2255: return 0;
! 2256:
! 2257: return IGP_METRIC_UNKNOWN;
! 2258: }
! 2259:
! 2260: static int
! 2261: rt_update_hostentry(rtable *tab, struct hostentry *he)
! 2262: {
! 2263: rta *old_src = he->src;
! 2264: int pxlen = 0;
! 2265:
! 2266: /* Reset the hostentry */
! 2267: he->src = NULL;
! 2268: he->gw = IPA_NONE;
! 2269: he->dest = RTD_UNREACHABLE;
! 2270: he->igp_metric = 0;
! 2271:
! 2272: net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
! 2273: if (n)
! 2274: {
! 2275: rte *e = n->routes;
! 2276: rta *a = e->attrs;
! 2277: pxlen = n->n.pxlen;
! 2278:
! 2279: if (a->hostentry)
! 2280: {
! 2281: /* Recursive route should not depend on another recursive route */
! 2282: log(L_WARN "Next hop address %I resolvable through recursive route for %I/%d",
! 2283: he->addr, n->n.prefix, pxlen);
! 2284: goto done;
! 2285: }
! 2286:
! 2287: if (a->dest == RTD_DEVICE)
! 2288: {
! 2289: if (if_local_addr(he->addr, a->iface))
! 2290: {
! 2291: /* The host address is a local address, this is not valid */
! 2292: log(L_WARN "Next hop address %I is a local address of iface %s",
! 2293: he->addr, a->iface->name);
! 2294: goto done;
! 2295: }
! 2296:
! 2297: /* The host is directly reachable, use link as a gateway */
! 2298: he->gw = he->link;
! 2299: he->dest = RTD_ROUTER;
! 2300: }
! 2301: else
! 2302: {
! 2303: /* The host is reachable through some route entry */
! 2304: he->gw = a->gw;
! 2305: he->dest = a->dest;
! 2306: }
! 2307:
! 2308: he->src = rta_clone(a);
! 2309: he->igp_metric = rt_get_igp_metric(e);
! 2310: }
! 2311:
! 2312: done:
! 2313: /* Add a prefix range to the trie */
! 2314: trie_add_prefix(tab->hostcache->trie, he->addr, MAX_PREFIX_LENGTH, pxlen, MAX_PREFIX_LENGTH);
! 2315:
! 2316: rta_free(old_src);
! 2317: return old_src != he->src;
! 2318: }
! 2319:
! 2320: static void
! 2321: rt_update_hostcache(rtable *tab)
! 2322: {
! 2323: struct hostcache *hc = tab->hostcache;
! 2324: struct hostentry *he;
! 2325: node *n, *x;
! 2326:
! 2327: /* Reset the trie */
! 2328: lp_flush(hc->lp);
! 2329: hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
! 2330:
! 2331: WALK_LIST_DELSAFE(n, x, hc->hostentries)
! 2332: {
! 2333: he = SKIP_BACK(struct hostentry, ln, n);
! 2334: if (!he->uc)
! 2335: {
! 2336: hc_delete_hostentry(hc, he);
! 2337: continue;
! 2338: }
! 2339:
! 2340: if (rt_update_hostentry(tab, he))
! 2341: rt_schedule_nhu(he->tab);
! 2342: }
! 2343:
! 2344: tab->hcu_scheduled = 0;
! 2345: }
! 2346:
! 2347: static struct hostentry *
! 2348: rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
! 2349: {
! 2350: struct hostentry *he;
! 2351:
! 2352: if (!tab->hostcache)
! 2353: rt_init_hostcache(tab);
! 2354:
! 2355: uint k = hc_hash(a, dep);
! 2356: struct hostcache *hc = tab->hostcache;
! 2357: for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
! 2358: if (ipa_equal(he->addr, a) && (he->tab == dep))
! 2359: return he;
! 2360:
! 2361: he = hc_new_hostentry(hc, a, ll, dep, k);
! 2362: rt_update_hostentry(tab, he);
! 2363: return he;
! 2364: }
! 2365:
! 2366: void
! 2367: rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_addr *ll)
! 2368: {
! 2369: rta_apply_hostentry(a, rt_get_hostentry(tab, *gw, *ll, dep));
! 2370: }
! 2371:
! 2372:
! 2373: /*
! 2374: * CLI commands
! 2375: */
! 2376:
! 2377: static byte *
! 2378: rt_format_via(rte *e)
! 2379: {
! 2380: rta *a = e->attrs;
! 2381:
! 2382: /* Max text length w/o IP addr and interface name is 16 */
! 2383: static byte via[STD_ADDRESS_P_LENGTH+sizeof(a->iface->name)+16];
! 2384:
! 2385: switch (a->dest)
! 2386: {
! 2387: case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break;
! 2388: case RTD_DEVICE: bsprintf(via, "dev %s", a->iface->name); break;
! 2389: case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
! 2390: case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
! 2391: case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
! 2392: case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
! 2393: default: bsprintf(via, "???");
! 2394: }
! 2395: return via;
! 2396: }
! 2397:
! 2398: static void
! 2399: rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa)
! 2400: {
! 2401: byte from[STD_ADDRESS_P_LENGTH+8];
! 2402: byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
! 2403: rta *a = e->attrs;
! 2404: int primary = (e->net->routes == e);
! 2405: int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
! 2406: void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
! 2407: struct mpnh *nh;
! 2408:
! 2409: tm_format_datetime(tm, &config->tf_route, e->lastmod);
! 2410: if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw))
! 2411: bsprintf(from, " from %I", a->from);
! 2412: else
! 2413: from[0] = 0;
! 2414:
! 2415: get_route_info = a->src->proto->proto->get_route_info;
! 2416: if (get_route_info || d->verbose)
! 2417: {
! 2418: /* Need to normalize the extended attributes */
! 2419: ea_list *t = tmpa;
! 2420: t = ea_append(t, a->eattrs);
! 2421: tmpa = alloca(ea_scan(t));
! 2422: ea_merge(t, tmpa);
! 2423: ea_sort(tmpa);
! 2424: }
! 2425: if (get_route_info)
! 2426: get_route_info(e, info, tmpa);
! 2427: else
! 2428: bsprintf(info, " (%d)", e->pref);
! 2429: cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name,
! 2430: tm, from, primary ? (sync_error ? " !" : " *") : "", info);
! 2431: for (nh = a->nexthops; nh; nh = nh->next)
! 2432: cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
! 2433: if (d->verbose)
! 2434: rta_show(c, a, tmpa);
! 2435: }
! 2436:
! 2437: static void
! 2438: rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
! 2439: {
! 2440: rte *e, *ee;
! 2441: byte ia[STD_ADDRESS_P_LENGTH+8];
! 2442: struct ea_list *tmpa;
! 2443: struct announce_hook *a = NULL;
! 2444: int first = 1;
! 2445: int pass = 0;
! 2446:
! 2447: bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
! 2448:
! 2449: if (d->export_mode)
! 2450: {
! 2451: if (! d->export_protocol->rt_notify)
! 2452: return;
! 2453:
! 2454: a = proto_find_announce_hook(d->export_protocol, d->table);
! 2455: if (!a)
! 2456: return;
! 2457: }
! 2458:
! 2459: for (e = n->routes; e; e = e->next)
! 2460: {
! 2461: if (rte_is_filtered(e) != d->filtered)
! 2462: continue;
! 2463:
! 2464: d->rt_counter++;
! 2465: d->net_counter += first;
! 2466: first = 0;
! 2467:
! 2468: if (pass)
! 2469: continue;
! 2470:
! 2471: ee = e;
! 2472: rte_update_lock(); /* We use the update buffer for filtering */
! 2473: tmpa = make_tmp_attrs(e, rte_update_pool);
! 2474:
! 2475: /* Special case for merged export */
! 2476: if ((d->export_mode == RSEM_EXPORT) && (d->export_protocol->accept_ra_types == RA_MERGED))
! 2477: {
! 2478: rte *rt_free;
! 2479: e = rt_export_merged(a, n, &rt_free, &tmpa, rte_update_pool, 1);
! 2480: pass = 1;
! 2481:
! 2482: if (!e)
! 2483: { e = ee; goto skip; }
! 2484: }
! 2485: else if (d->export_mode)
! 2486: {
! 2487: struct proto *ep = d->export_protocol;
! 2488: int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0;
! 2489:
! 2490: if (ep->accept_ra_types == RA_OPTIMAL || ep->accept_ra_types == RA_MERGED)
! 2491: pass = 1;
! 2492:
! 2493: if (ic < 0)
! 2494: goto skip;
! 2495:
! 2496: if (d->export_mode > RSEM_PREEXPORT)
! 2497: {
! 2498: /*
! 2499: * FIXME - This shows what should be exported according to current
! 2500: * filters, but not what was really exported. 'configure soft'
! 2501: * command may change the export filter and do not update routes.
! 2502: */
! 2503: int do_export = (ic > 0) ||
! 2504: (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
! 2505:
! 2506: if (do_export != (d->export_mode == RSEM_EXPORT))
! 2507: goto skip;
! 2508:
! 2509: if ((d->export_mode == RSEM_EXPORT) && (ep->accept_ra_types == RA_ACCEPTED))
! 2510: pass = 1;
! 2511: }
! 2512: }
! 2513:
! 2514: if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
! 2515: goto skip;
! 2516:
! 2517: if (f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
! 2518: goto skip;
! 2519:
! 2520: d->show_counter++;
! 2521: if (d->stats < 2)
! 2522: rt_show_rte(c, ia, e, d, tmpa);
! 2523: ia[0] = 0;
! 2524:
! 2525: skip:
! 2526: if (e != ee)
! 2527: {
! 2528: rte_free(e);
! 2529: e = ee;
! 2530: }
! 2531: rte_update_unlock();
! 2532:
! 2533: if (d->primary_only)
! 2534: break;
! 2535: }
! 2536: }
! 2537:
! 2538: static void
! 2539: rt_show_cont(struct cli *c)
! 2540: {
! 2541: struct rt_show_data *d = c->rover;
! 2542: #ifdef DEBUGGING
! 2543: unsigned max = 4;
! 2544: #else
! 2545: unsigned max = 64;
! 2546: #endif
! 2547: struct fib *fib = &d->table->fib;
! 2548: struct fib_iterator *it = &d->fit;
! 2549:
! 2550: FIB_ITERATE_START(fib, it, f)
! 2551: {
! 2552: net *n = (net *) f;
! 2553: if (d->running_on_config && d->running_on_config != config)
! 2554: {
! 2555: cli_printf(c, 8004, "Stopped due to reconfiguration");
! 2556: goto done;
! 2557: }
! 2558: if (d->export_protocol && (d->export_protocol->export_state == ES_DOWN))
! 2559: {
! 2560: cli_printf(c, 8005, "Protocol is down");
! 2561: goto done;
! 2562: }
! 2563: if (!max--)
! 2564: {
! 2565: FIB_ITERATE_PUT(it, f);
! 2566: return;
! 2567: }
! 2568: rt_show_net(c, n, d);
! 2569: }
! 2570: FIB_ITERATE_END(f);
! 2571: if (d->stats)
! 2572: cli_printf(c, 14, "%d of %d routes for %d networks", d->show_counter, d->rt_counter, d->net_counter);
! 2573: else
! 2574: cli_printf(c, 0, "");
! 2575: done:
! 2576: c->cont = c->cleanup = NULL;
! 2577: }
! 2578:
! 2579: static void
! 2580: rt_show_cleanup(struct cli *c)
! 2581: {
! 2582: struct rt_show_data *d = c->rover;
! 2583:
! 2584: /* Unlink the iterator */
! 2585: fit_get(&d->table->fib, &d->fit);
! 2586: }
! 2587:
! 2588: void
! 2589: rt_show(struct rt_show_data *d)
! 2590: {
! 2591: net *n;
! 2592:
! 2593: /* Default is either a master table or a table related to a respective protocol */
! 2594: if (!d->table && d->export_protocol) d->table = d->export_protocol->table;
! 2595: if (!d->table && d->show_protocol) d->table = d->show_protocol->table;
! 2596: if (!d->table) d->table = config->master_rtc->table;
! 2597:
! 2598: /* Filtered routes are neither exported nor have sensible ordering */
! 2599: if (d->filtered && (d->export_mode || d->primary_only))
! 2600: cli_msg(0, "");
! 2601:
! 2602: if (d->pxlen == 256)
! 2603: {
! 2604: FIB_ITERATE_INIT(&d->fit, &d->table->fib);
! 2605: this_cli->cont = rt_show_cont;
! 2606: this_cli->cleanup = rt_show_cleanup;
! 2607: this_cli->rover = d;
! 2608: }
! 2609: else
! 2610: {
! 2611: if (d->show_for)
! 2612: n = net_route(d->table, d->prefix, d->pxlen);
! 2613: else
! 2614: n = net_find(d->table, d->prefix, d->pxlen);
! 2615:
! 2616: if (n)
! 2617: rt_show_net(this_cli, n, d);
! 2618:
! 2619: if (d->rt_counter)
! 2620: cli_msg(0, "");
! 2621: else
! 2622: cli_msg(8001, "Network not in table");
! 2623: }
! 2624: }
! 2625:
! 2626: /*
! 2627: * Documentation for functions declared inline in route.h
! 2628: */
! 2629: #if 0
! 2630:
! 2631: /**
! 2632: * net_find - find a network entry
! 2633: * @tab: a routing table
! 2634: * @addr: address of the network
! 2635: * @len: length of the network prefix
! 2636: *
! 2637: * net_find() looks up the given network in routing table @tab and
! 2638: * returns a pointer to its &net entry or %NULL if no such network
! 2639: * exists.
! 2640: */
! 2641: static inline net *net_find(rtable *tab, ip_addr addr, unsigned len)
! 2642: { DUMMY; }
! 2643:
! 2644: /**
! 2645: * net_get - obtain a network entry
! 2646: * @tab: a routing table
! 2647: * @addr: address of the network
! 2648: * @len: length of the network prefix
! 2649: *
! 2650: * net_get() looks up the given network in routing table @tab and
! 2651: * returns a pointer to its &net entry. If no such entry exists, it's
! 2652: * created.
! 2653: */
! 2654: static inline net *net_get(rtable *tab, ip_addr addr, unsigned len)
! 2655: { DUMMY; }
! 2656:
! 2657: /**
! 2658: * rte_cow - copy a route for writing
! 2659: * @r: a route entry to be copied
! 2660: *
! 2661: * rte_cow() takes a &rte and prepares it for modification. The exact action
! 2662: * taken depends on the flags of the &rte -- if it's a temporary entry, it's
! 2663: * just returned unchanged, else a new temporary entry with the same contents
! 2664: * is created.
! 2665: *
! 2666: * The primary use of this function is inside the filter machinery -- when
! 2667: * a filter wants to modify &rte contents (to change the preference or to
! 2668: * attach another set of attributes), it must ensure that the &rte is not
! 2669: * shared with anyone else (and especially that it isn't stored in any routing
! 2670: * table).
! 2671: *
! 2672: * Result: a pointer to the new writable &rte.
! 2673: */
! 2674: static inline rte * rte_cow(rte *r)
! 2675: { DUMMY; }
! 2676:
! 2677: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>