diff --git a/doc/bird.sgml b/doc/bird.sgml index 9d1047a8..05216c4a 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -4938,8 +4938,15 @@ default route to prevent routing loops).

There are three classes of definitions in Static protocol configuration -- global options, static route definitions, and per-route options. Usually, the -definition of the protocol contains mainly a list of static routes. -Static routes have no specific attributes. +definition of the protocol contains mainly a list of static routes. Static +routes have no specific attributes, but +attribute is used to compare static routes with the same preference. + +

The list of static routes may contain multiple routes for the same network +(usually, but not necessary, distinquished by Global options: diff --git a/proto/static/static.c b/proto/static/static.c index 16a981ed..7b22d49b 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -49,11 +49,14 @@ static linpool *static_lp; +static inline struct rte_src * static_get_source(struct static_proto *p, uint i) +{ return i ? rt_get_source(&p->p, i) : p->p.main_source; } + static void static_announce_rte(struct static_proto *p, struct static_route *r) { rta *a = allocz(RTA_MAX_SIZE); - a->src = p->p.main_source; + a->src = static_get_source(p, r->index); a->source = RTS_STATIC; a->scope = SCOPE_UNIVERSE; a->dest = r->dest; @@ -105,7 +108,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r) if (r->cmds) f_eval_rte(r->cmds, &e, static_lp); - rte_update(&p->p, r->net, e); + rte_update2(p->p.main_channel, r->net, e, a->src); r->state = SRS_CLEAN; if (r->cmds) @@ -117,7 +120,7 @@ withdraw: if (r->state == SRS_DOWN) return; - rte_update(&p->p, r->net, NULL); + rte_update2(p->p.main_channel, r->net, NULL, a->src); r->state = SRS_DOWN; } @@ -250,7 +253,7 @@ static void static_remove_rte(struct static_proto *p, struct static_route *r) { if (r->state) - rte_update(&p->p, r->net, NULL); + rte_update2(p->p.main_channel, r->net, NULL, static_get_source(p, r->index)); static_reset_rte(p, r); } @@ -354,11 +357,22 @@ static_bfd_notify(struct bfd_request *req) } static int -static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED) +static_rte_better(rte *new, rte *old) { - return 1; + u32 n = ea_get_int(new->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); + u32 o = ea_get_int(old->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); + return n < o; } +static int +static_rte_mergable(rte *pri, rte *sec) +{ + u32 a = ea_get_int(pri->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); + u32 b = ea_get_int(sec->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); + return a == b; +} + +static void static_index_routes(struct static_config *cf); static void static_postconfig(struct proto_config *CF) @@ -382,6 +396,8 @@ static_postconfig(struct proto_config *CF) WALK_LIST(r, cf->routes) if (r->net && (r->net->type != CF->net_type)) cf_error("Route %N incompatible with channel type", r->net); + + static_index_routes(cf); } static struct proto * @@ -394,6 +410,7 @@ static_init(struct proto_config *CF) P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); P->neigh_notify = static_neigh_notify; + P->rte_better = static_rte_better; P->rte_mergable = static_rte_mergable; if (cf->igp_table_ip4) @@ -463,7 +480,7 @@ static_cleanup(struct proto *P) static void static_dump_rte(struct static_route *r) { - debug("%-1N: ", r->net); + debug("%-1N (%u): ", r->net, r->index); if (r->dest == RTD_UNICAST) if (r->iface && ipa_zero(r->via)) debug("dev %s\n", r->iface->name); @@ -486,11 +503,48 @@ static_dump(struct proto *P) #define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL ) -static inline int -static_cmp_rte(const void *X, const void *Y) +static inline int srt_equal(const struct static_route *a, const struct static_route *b) +{ return net_equal(a->net, b->net) && (a->index == b->index); } + +static inline int srt_compare(const struct static_route *a, const struct static_route *b) +{ return net_compare(a->net, b->net) ?: uint_cmp(a->index, b->index); } + +static inline int srt_compare_qsort(const void *A, const void *B) { - struct static_route *x = *(void **)X, *y = *(void **)Y; - return net_compare(x->net, y->net); + return srt_compare(*(const struct static_route * const *)A, + *(const struct static_route * const *)B); +} + +static void +static_index_routes(struct static_config *cf) +{ + struct static_route *rt, **buf; + uint num, i, v; + + num = list_length(&cf->routes); + buf = xmalloc(num * sizeof(void *)); + + /* Initialize with sequential indexes to ensure stable sorting */ + i = 0; + WALK_LIST(rt, cf->routes) + { + buf[i] = rt; + rt->index = i++; + } + + qsort(buf, num, sizeof(struct static_route *), srt_compare_qsort); + + /* Compute proper indexes - sequential for routes with same network */ + for (i = 0, v = 0, rt = NULL; i < num; i++, v++) + { + if (rt && !net_equal(buf[i]->net, rt->net)) + v = 0; + + rt = buf[i]; + rt->index = v; + } + + xfree(buf); } static int @@ -519,7 +573,7 @@ static_reconfigure(struct proto *P, struct proto_config *CF) /* Reconfigure initial matching sequence */ for (or = HEAD(o->routes), nr = HEAD(n->routes); - NODE_VALID(or) && NODE_VALID(nr) && net_equal(or->net, nr->net); + NODE_VALID(or) && NODE_VALID(nr) && srt_equal(or, nr); or = NODE_NEXT(or), nr = NODE_NEXT(nr)) static_reconfigure_rte(p, or, nr); @@ -545,12 +599,12 @@ static_reconfigure(struct proto *P, struct proto_config *CF) for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2)) nrbuf[i] = nr2; - qsort(orbuf, ornum, sizeof(struct static_route *), static_cmp_rte); - qsort(nrbuf, nrnum, sizeof(struct static_route *), static_cmp_rte); + qsort(orbuf, ornum, sizeof(struct static_route *), srt_compare_qsort); + qsort(nrbuf, nrnum, sizeof(struct static_route *), srt_compare_qsort); while ((orpos < ornum) && (nrpos < nrnum)) { - int x = net_compare(orbuf[orpos]->net, nrbuf[nrpos]->net); + int x = srt_compare(orbuf[orpos], nrbuf[nrpos]); if (x < 0) static_remove_rte(p, orbuf[orpos++]); else if (x > 0) @@ -602,6 +656,16 @@ static_copy_config(struct proto_config *dest, struct proto_config *src) } } +static void +static_get_route_info(rte *rte, byte *buf) +{ + eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC); + if (a) + buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data); + else + buf += bsprintf(buf, " (%d)", rte->pref); +} + static void static_show_rt(struct static_route *r) { @@ -665,5 +729,6 @@ struct protocol proto_static = { .shutdown = static_shutdown, .cleanup = static_cleanup, .reconfigure = static_reconfigure, - .copy_config = static_copy_config + .copy_config = static_copy_config, + .get_route_info = static_get_route_info, }; diff --git a/proto/static/static.h b/proto/static/static.h index f736996c..af714b72 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -40,6 +40,7 @@ struct static_route { struct static_route *mp_head; /* First nexthop of this route */ struct static_route *mp_next; /* Nexthops for multipath routes */ struct f_line *cmds; /* List of commands for setting attributes */ + uint index; /* Distinguish different routes with same net */ byte dest; /* Destination type (RTD_*) */ byte state; /* State of route announcement (SRS_*) */ byte active; /* Next hop is active (nbr/iface/BFD available) */