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
|
||||
|
||||
#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_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
|
||||
CF_KEYWORDS(MULTIPATH, WEIGHT)
|
||||
|
||||
|
||||
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_route0 VIA ipa {
|
||||
this_srt->dest = RTD_ROUTER;
|
||||
|
@ -55,6 +76,9 @@ stat_route:
|
|||
rem_node(&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 REJECT { this_srt->dest = RTD_UNREACHABLE; }
|
||||
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
|
||||
|
|
|
@ -17,6 +17,17 @@
|
|||
* to be notified about gaining or losing the neighbor. Special
|
||||
* 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,
|
||||
* Static not only compares the two configurations, but it also calculates
|
||||
* difference between the lists of static routes and it just inserts the
|
||||
|
@ -32,6 +43,7 @@
|
|||
#include "nest/cli.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/alloca.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.gw = r->via;
|
||||
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);
|
||||
e = rte_get_temp(aa);
|
||||
e->net = n;
|
||||
|
@ -64,20 +106,6 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
|
|||
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_remove(struct proto *p, struct static_route *r)
|
||||
{
|
||||
|
@ -93,6 +121,24 @@ static_remove(struct proto *p, struct static_route *r)
|
|||
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_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);
|
||||
}
|
||||
else
|
||||
{
|
||||
log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
|
||||
static_remove(p, r);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RTD_DEVICE:
|
||||
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:
|
||||
static_install(p, r, NULL);
|
||||
}
|
||||
|
@ -156,12 +237,42 @@ static_neigh_notify(struct neighbor *n)
|
|||
struct proto *p = n->proto;
|
||||
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)
|
||||
switch (r->dest)
|
||||
{
|
||||
case RTD_ROUTER:
|
||||
if (static_decide((struct static_config *) p->cf, r))
|
||||
static_install(p, r, n->iface);
|
||||
else
|
||||
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
|
||||
|
@ -243,9 +354,28 @@ static_same_net(struct static_route *x, struct static_route *y)
|
|||
static inline int
|
||||
static_same_dest(struct static_route *x, struct static_route *y)
|
||||
{
|
||||
return (x->dest == y->dest)
|
||||
&& (x->dest != RTD_ROUTER || ipa_equal(x->via, y->via))
|
||||
&& (x->dest != RTD_DEVICE || !strcmp(x->if_name, y->if_name));
|
||||
if (x->dest != y->dest)
|
||||
return 0;
|
||||
|
||||
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
|
||||
|
@ -325,9 +455,16 @@ static_show_rt(struct static_route *r)
|
|||
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
|
||||
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
|
||||
case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
|
||||
case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
|
||||
default: bsprintf(via, "???");
|
||||
}
|
||||
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
|
||||
|
|
|
@ -28,9 +28,13 @@ struct static_route {
|
|||
ip_addr via; /* Destination router */
|
||||
struct neighbor *neigh;
|
||||
byte *if_name; /* Name for RTD_DEVICE routes */
|
||||
struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */
|
||||
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 *);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue