Multipath support for static protocol.
This commit is contained in:
parent
7e95c05d88
commit
9852f81064
3 changed files with 191 additions and 26 deletions
|
@ -13,11 +13,13 @@ CF_HDR
|
||||||
CF_DEFINES
|
CF_DEFINES
|
||||||
|
|
||||||
#define STATIC_CFG ((struct static_config *) this_proto)
|
#define STATIC_CFG ((struct static_config *) this_proto)
|
||||||
static struct static_route *this_srt;
|
static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
|
||||||
|
|
||||||
CF_DECLS
|
CF_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
|
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
|
||||||
|
CF_KEYWORDS(MULTIPATH, WEIGHT)
|
||||||
|
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -44,6 +46,25 @@ stat_route0: ROUTE prefix {
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
stat_multipath1:
|
||||||
|
VIA ipa {
|
||||||
|
last_srt_nh = this_srt_nh;
|
||||||
|
this_srt_nh = cfg_allocz(sizeof(struct static_route));
|
||||||
|
this_srt_nh->dest = RTD_NONE;
|
||||||
|
this_srt_nh->via = $2;
|
||||||
|
this_srt_nh->if_name = (void *) this_srt; /* really */
|
||||||
|
}
|
||||||
|
| stat_multipath1 WEIGHT expr {
|
||||||
|
this_srt_nh->masklen = $3 - 1; /* really */
|
||||||
|
if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
stat_multipath:
|
||||||
|
stat_multipath1 { this_srt->mp_next = this_srt_nh; }
|
||||||
|
| stat_multipath stat_multipath1 { last_srt_nh->mp_next = this_srt_nh; }
|
||||||
|
;
|
||||||
|
|
||||||
stat_route:
|
stat_route:
|
||||||
stat_route0 VIA ipa {
|
stat_route0 VIA ipa {
|
||||||
this_srt->dest = RTD_ROUTER;
|
this_srt->dest = RTD_ROUTER;
|
||||||
|
@ -55,6 +76,9 @@ stat_route:
|
||||||
rem_node(&this_srt->n);
|
rem_node(&this_srt->n);
|
||||||
add_tail(&STATIC_CFG->iface_routes, &this_srt->n);
|
add_tail(&STATIC_CFG->iface_routes, &this_srt->n);
|
||||||
}
|
}
|
||||||
|
| stat_route0 MULTIPATH stat_multipath {
|
||||||
|
this_srt->dest = RTD_MULTIPATH;
|
||||||
|
}
|
||||||
| stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; }
|
| stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; }
|
||||||
| stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; }
|
| stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; }
|
||||||
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
|
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
|
||||||
|
|
|
@ -17,6 +17,17 @@
|
||||||
* to be notified about gaining or losing the neighbor. Special
|
* to be notified about gaining or losing the neighbor. Special
|
||||||
* routes like black holes or rejects are inserted all the time.
|
* routes like black holes or rejects are inserted all the time.
|
||||||
*
|
*
|
||||||
|
* Multipath routes are tricky. Because these routes depends on
|
||||||
|
* several neighbors we need to integrate that to the neighbor
|
||||||
|
* notification handling, we use dummy static_route nodes, one for
|
||||||
|
* each nexthop. Therefore, a multipath route consists of a master
|
||||||
|
* static_route node (of dest RTD_MULTIPATH), which specifies prefix
|
||||||
|
* and is used in most circumstances, and a list of dummy static_route
|
||||||
|
* nodes (of dest RTD_NONE), which stores info about nexthops and are
|
||||||
|
* connected to neighbor entries and neighbor notifications. Dummy
|
||||||
|
* nodes are chained using mp_next, they aren't in other_routes list,
|
||||||
|
* and abuse some fields (masklen, if_name) for other purposes.
|
||||||
|
*
|
||||||
* The only other thing worth mentioning is that when asked for reconfiguration,
|
* The only other thing worth mentioning is that when asked for reconfiguration,
|
||||||
* Static not only compares the two configurations, but it also calculates
|
* Static not only compares the two configurations, but it also calculates
|
||||||
* difference between the lists of static routes and it just inserts the
|
* difference between the lists of static routes and it just inserts the
|
||||||
|
@ -32,6 +43,7 @@
|
||||||
#include "nest/cli.h"
|
#include "nest/cli.h"
|
||||||
#include "conf/conf.h"
|
#include "conf/conf.h"
|
||||||
#include "lib/string.h"
|
#include "lib/string.h"
|
||||||
|
#include "lib/alloca.h"
|
||||||
|
|
||||||
#include "static.h"
|
#include "static.h"
|
||||||
|
|
||||||
|
@ -54,8 +66,38 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
|
||||||
a.dest = r->dest;
|
a.dest = r->dest;
|
||||||
a.gw = r->via;
|
a.gw = r->via;
|
||||||
a.iface = ifa;
|
a.iface = ifa;
|
||||||
aa = rta_lookup(&a);
|
|
||||||
|
|
||||||
|
if (r->dest == RTD_MULTIPATH)
|
||||||
|
{
|
||||||
|
struct static_route *r2;
|
||||||
|
struct mpnh *nhs = NULL;
|
||||||
|
struct mpnh **nhp = &nhs;
|
||||||
|
|
||||||
|
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
|
||||||
|
if (r2->installed)
|
||||||
|
{
|
||||||
|
struct mpnh *nh = alloca(sizeof(struct mpnh));
|
||||||
|
nh->gw = r2->via;
|
||||||
|
nh->iface = r2->neigh->iface;
|
||||||
|
nh->weight = r2->masklen; /* really */
|
||||||
|
nh->next = NULL;
|
||||||
|
*nhp = nh;
|
||||||
|
nhp = &(nh->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There is at least one nexthop */
|
||||||
|
if (!nhs->next)
|
||||||
|
{
|
||||||
|
/* Fallback to unipath route for exactly one nexthop */
|
||||||
|
a.dest = RTD_ROUTER;
|
||||||
|
a.gw = nhs->gw;
|
||||||
|
a.iface = nhs->iface;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
a.nexthops = nhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
aa = rta_lookup(&a);
|
||||||
n = net_get(p->table, r->net, r->masklen);
|
n = net_get(p->table, r->net, r->masklen);
|
||||||
e = rte_get_temp(aa);
|
e = rte_get_temp(aa);
|
||||||
e->net = n;
|
e->net = n;
|
||||||
|
@ -64,20 +106,6 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
|
||||||
r->installed = 1;
|
r->installed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
static_decide(struct static_config *cf, struct static_route *r)
|
|
||||||
{
|
|
||||||
struct iface *ifa = r->neigh->iface;
|
|
||||||
|
|
||||||
if (!ifa)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (cf->check_link && !(ifa->flags & IF_LINK_UP))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
static_remove(struct proto *p, struct static_route *r)
|
static_remove(struct proto *p, struct static_route *r)
|
||||||
{
|
{
|
||||||
|
@ -93,6 +121,24 @@ static_remove(struct proto *p, struct static_route *r)
|
||||||
r->installed = 0;
|
r->installed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
static_decide(struct static_config *cf, struct static_route *r)
|
||||||
|
{
|
||||||
|
/* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
|
||||||
|
the route also have to be valid (r->neigh != NULL) */
|
||||||
|
|
||||||
|
struct iface *ifa = r->neigh->iface;
|
||||||
|
|
||||||
|
if (!ifa)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (cf->check_link && !(ifa->flags & IF_LINK_UP))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
|
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
|
||||||
{
|
{
|
||||||
|
@ -113,11 +159,46 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
|
||||||
static_remove(p, r);
|
static_remove(p, r);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
|
log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
|
||||||
|
static_remove(p, r);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RTD_DEVICE:
|
case RTD_DEVICE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RTD_MULTIPATH:
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
struct static_route *r2;
|
||||||
|
|
||||||
|
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
|
||||||
|
{
|
||||||
|
struct neighbor *n = neigh_find(p, &r2->via, NEF_STICKY);
|
||||||
|
if (n)
|
||||||
|
{
|
||||||
|
r2->chain = n->data;
|
||||||
|
n->data = r2;
|
||||||
|
r2->neigh = n;
|
||||||
|
r2->installed = static_decide(cf, r2);
|
||||||
|
count += r2->installed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
|
||||||
|
r2->installed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
static_install(p, r, NULL);
|
||||||
|
else
|
||||||
|
static_remove(p, r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
static_install(p, r, NULL);
|
static_install(p, r, NULL);
|
||||||
}
|
}
|
||||||
|
@ -156,12 +237,42 @@ static_neigh_notify(struct neighbor *n)
|
||||||
struct proto *p = n->proto;
|
struct proto *p = n->proto;
|
||||||
struct static_route *r;
|
struct static_route *r;
|
||||||
|
|
||||||
DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
|
log(L_WARN "Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
|
||||||
for(r=n->data; r; r=r->chain)
|
for(r=n->data; r; r=r->chain)
|
||||||
|
switch (r->dest)
|
||||||
|
{
|
||||||
|
case RTD_ROUTER:
|
||||||
if (static_decide((struct static_config *) p->cf, r))
|
if (static_decide((struct static_config *) p->cf, r))
|
||||||
static_install(p, r, n->iface);
|
static_install(p, r, n->iface);
|
||||||
else
|
else
|
||||||
static_remove(p, r);
|
static_remove(p, r);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RTD_NONE: /* a part of multipath route */
|
||||||
|
{
|
||||||
|
int decision = static_decide((struct static_config *) p->cf, r);
|
||||||
|
if (decision == r->installed)
|
||||||
|
break; /* no change */
|
||||||
|
r->installed = decision;
|
||||||
|
|
||||||
|
struct static_route *r1, *r2;
|
||||||
|
int count = 0;
|
||||||
|
r1 = (void *) r->if_name; /* really */
|
||||||
|
for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
|
||||||
|
count += r2->installed;
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
{
|
||||||
|
/* Set of nexthops changed - force reinstall */
|
||||||
|
r1->installed = 0;
|
||||||
|
static_install(p, r1, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
static_remove(p, r1);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -243,9 +354,28 @@ static_same_net(struct static_route *x, struct static_route *y)
|
||||||
static inline int
|
static inline int
|
||||||
static_same_dest(struct static_route *x, struct static_route *y)
|
static_same_dest(struct static_route *x, struct static_route *y)
|
||||||
{
|
{
|
||||||
return (x->dest == y->dest)
|
if (x->dest != y->dest)
|
||||||
&& (x->dest != RTD_ROUTER || ipa_equal(x->via, y->via))
|
return 0;
|
||||||
&& (x->dest != RTD_DEVICE || !strcmp(x->if_name, y->if_name));
|
|
||||||
|
switch (x->dest)
|
||||||
|
{
|
||||||
|
case RTD_ROUTER:
|
||||||
|
return ipa_equal(x->via, y->via);
|
||||||
|
|
||||||
|
case RTD_DEVICE:
|
||||||
|
return !strcmp(x->if_name, y->if_name);
|
||||||
|
|
||||||
|
case RTD_MULTIPATH:
|
||||||
|
for (x = x->mp_next, y = y->mp_next;
|
||||||
|
x && y;
|
||||||
|
x = x->mp_next, y = y->mp_next)
|
||||||
|
if (!ipa_equal(x->via, y->via))
|
||||||
|
return 0;
|
||||||
|
return !x && !y;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -325,9 +455,16 @@ static_show_rt(struct static_route *r)
|
||||||
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
|
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
|
||||||
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
|
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
|
||||||
case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
|
case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
|
||||||
|
case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
|
||||||
default: bsprintf(via, "???");
|
default: bsprintf(via, "???");
|
||||||
}
|
}
|
||||||
cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
|
cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
|
||||||
|
|
||||||
|
struct static_route *r2;
|
||||||
|
if (r->dest == RTD_MULTIPATH)
|
||||||
|
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
|
||||||
|
cli_msg(-1009, "\tvia %I weight %d%s", r2->via, r2->masklen + 1, /* really */
|
||||||
|
r2->installed ? "" : " (dormant)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -28,9 +28,13 @@ struct static_route {
|
||||||
ip_addr via; /* Destination router */
|
ip_addr via; /* Destination router */
|
||||||
struct neighbor *neigh;
|
struct neighbor *neigh;
|
||||||
byte *if_name; /* Name for RTD_DEVICE routes */
|
byte *if_name; /* Name for RTD_DEVICE routes */
|
||||||
|
struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */
|
||||||
int installed; /* Installed in master table */
|
int installed; /* Installed in master table */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Dummy nodes (parts of multipath route) abuses masklen field for weight
|
||||||
|
and if_name field for a ptr to the master (RTD_MULTIPATH) node. */
|
||||||
|
|
||||||
void static_show(struct proto *);
|
void static_show(struct proto *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue