Static: Support for multiple routes with the same network
Add support for proper handling of multiple routes with the same network to the static protocol. Routes are distinguished by internal index, which is assigned automatically (sequentially for routes within each network). Having different route preference or igp_metric attribute is optional.
This commit is contained in:
parent
df65d519d6
commit
3347aaafec
3 changed files with 91 additions and 18 deletions
|
@ -4938,8 +4938,15 @@ default route to prevent routing loops).
|
||||||
|
|
||||||
<p>There are three classes of definitions in Static protocol configuration --
|
<p>There are three classes of definitions in Static protocol configuration --
|
||||||
global options, static route definitions, and per-route options. Usually, the
|
global options, static route definitions, and per-route options. Usually, the
|
||||||
definition of the protocol contains mainly a list of static routes.
|
definition of the protocol contains mainly a list of static routes. Static
|
||||||
Static routes have no specific attributes.
|
routes have no specific attributes, but <ref id="rta-igp-metric" name="igp_metric">
|
||||||
|
attribute is used to compare static routes with the same preference.
|
||||||
|
|
||||||
|
<p>The list of static routes may contain multiple routes for the same network
|
||||||
|
(usually, but not necessary, distinquished by <cf/preference/ or <cf/igp_metric/),
|
||||||
|
but only routes of the same network type are allowed, as the static protocol
|
||||||
|
has just one channel. E.g., to have both IPv4 and IPv6 static routes, define two
|
||||||
|
static protocols, each with appropriate routes and channel.
|
||||||
|
|
||||||
<p>Global options:
|
<p>Global options:
|
||||||
|
|
||||||
|
|
|
@ -49,11 +49,14 @@
|
||||||
|
|
||||||
static linpool *static_lp;
|
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 void
|
||||||
static_announce_rte(struct static_proto *p, struct static_route *r)
|
static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||||
{
|
{
|
||||||
rta *a = allocz(RTA_MAX_SIZE);
|
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->source = RTS_STATIC;
|
||||||
a->scope = SCOPE_UNIVERSE;
|
a->scope = SCOPE_UNIVERSE;
|
||||||
a->dest = r->dest;
|
a->dest = r->dest;
|
||||||
|
@ -105,7 +108,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||||
if (r->cmds)
|
if (r->cmds)
|
||||||
f_eval_rte(r->cmds, &e, static_lp);
|
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;
|
r->state = SRS_CLEAN;
|
||||||
|
|
||||||
if (r->cmds)
|
if (r->cmds)
|
||||||
|
@ -117,7 +120,7 @@ withdraw:
|
||||||
if (r->state == SRS_DOWN)
|
if (r->state == SRS_DOWN)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rte_update(&p->p, r->net, NULL);
|
rte_update2(p->p.main_channel, r->net, NULL, a->src);
|
||||||
r->state = SRS_DOWN;
|
r->state = SRS_DOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +253,7 @@ static void
|
||||||
static_remove_rte(struct static_proto *p, struct static_route *r)
|
static_remove_rte(struct static_proto *p, struct static_route *r)
|
||||||
{
|
{
|
||||||
if (r->state)
|
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);
|
static_reset_rte(p, r);
|
||||||
}
|
}
|
||||||
|
@ -354,11 +357,22 @@ static_bfd_notify(struct bfd_request *req)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 void
|
||||||
static_postconfig(struct proto_config *CF)
|
static_postconfig(struct proto_config *CF)
|
||||||
|
@ -382,6 +396,8 @@ static_postconfig(struct proto_config *CF)
|
||||||
WALK_LIST(r, cf->routes)
|
WALK_LIST(r, cf->routes)
|
||||||
if (r->net && (r->net->type != CF->net_type))
|
if (r->net && (r->net->type != CF->net_type))
|
||||||
cf_error("Route %N incompatible with channel type", r->net);
|
cf_error("Route %N incompatible with channel type", r->net);
|
||||||
|
|
||||||
|
static_index_routes(cf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct proto *
|
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->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
|
||||||
|
|
||||||
P->neigh_notify = static_neigh_notify;
|
P->neigh_notify = static_neigh_notify;
|
||||||
|
P->rte_better = static_rte_better;
|
||||||
P->rte_mergable = static_rte_mergable;
|
P->rte_mergable = static_rte_mergable;
|
||||||
|
|
||||||
if (cf->igp_table_ip4)
|
if (cf->igp_table_ip4)
|
||||||
|
@ -463,7 +480,7 @@ static_cleanup(struct proto *P)
|
||||||
static void
|
static void
|
||||||
static_dump_rte(struct static_route *r)
|
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->dest == RTD_UNICAST)
|
||||||
if (r->iface && ipa_zero(r->via))
|
if (r->iface && ipa_zero(r->via))
|
||||||
debug("dev %s\n", r->iface->name);
|
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 )
|
#define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL )
|
||||||
|
|
||||||
static inline int
|
static inline int srt_equal(const struct static_route *a, const struct static_route *b)
|
||||||
static_cmp_rte(const void *X, const void *Y)
|
{ 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 srt_compare(*(const struct static_route * const *)A,
|
||||||
return net_compare(x->net, y->net);
|
*(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
|
static int
|
||||||
|
@ -519,7 +573,7 @@ static_reconfigure(struct proto *P, struct proto_config *CF)
|
||||||
|
|
||||||
/* Reconfigure initial matching sequence */
|
/* Reconfigure initial matching sequence */
|
||||||
for (or = HEAD(o->routes), nr = HEAD(n->routes);
|
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))
|
or = NODE_NEXT(or), nr = NODE_NEXT(nr))
|
||||||
static_reconfigure_rte(p, or, 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))
|
for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2))
|
||||||
nrbuf[i] = nr2;
|
nrbuf[i] = nr2;
|
||||||
|
|
||||||
qsort(orbuf, ornum, sizeof(struct static_route *), static_cmp_rte);
|
qsort(orbuf, ornum, sizeof(struct static_route *), srt_compare_qsort);
|
||||||
qsort(nrbuf, nrnum, sizeof(struct static_route *), static_cmp_rte);
|
qsort(nrbuf, nrnum, sizeof(struct static_route *), srt_compare_qsort);
|
||||||
|
|
||||||
while ((orpos < ornum) && (nrpos < nrnum))
|
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)
|
if (x < 0)
|
||||||
static_remove_rte(p, orbuf[orpos++]);
|
static_remove_rte(p, orbuf[orpos++]);
|
||||||
else if (x > 0)
|
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 void
|
||||||
static_show_rt(struct static_route *r)
|
static_show_rt(struct static_route *r)
|
||||||
{
|
{
|
||||||
|
@ -665,5 +729,6 @@ struct protocol proto_static = {
|
||||||
.shutdown = static_shutdown,
|
.shutdown = static_shutdown,
|
||||||
.cleanup = static_cleanup,
|
.cleanup = static_cleanup,
|
||||||
.reconfigure = static_reconfigure,
|
.reconfigure = static_reconfigure,
|
||||||
.copy_config = static_copy_config
|
.copy_config = static_copy_config,
|
||||||
|
.get_route_info = static_get_route_info,
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ struct static_route {
|
||||||
struct static_route *mp_head; /* First nexthop of this route */
|
struct static_route *mp_head; /* First nexthop of this route */
|
||||||
struct static_route *mp_next; /* Nexthops for multipath routes */
|
struct static_route *mp_next; /* Nexthops for multipath routes */
|
||||||
struct f_line *cmds; /* List of commands for setting attributes */
|
struct f_line *cmds; /* List of commands for setting attributes */
|
||||||
|
uint index; /* Distinguish different routes with same net */
|
||||||
byte dest; /* Destination type (RTD_*) */
|
byte dest; /* Destination type (RTD_*) */
|
||||||
byte state; /* State of route announcement (SRS_*) */
|
byte state; /* State of route announcement (SRS_*) */
|
||||||
byte active; /* Next hop is active (nbr/iface/BFD available) */
|
byte active; /* Next hop is active (nbr/iface/BFD available) */
|
||||||
|
|
Loading…
Reference in a new issue