Multipath support for static protocol.

This commit is contained in:
Ondrej Zajicek 2010-12-07 23:34:36 +01:00
parent 7e95c05d88
commit 9852f81064
3 changed files with 191 additions and 26 deletions

View file

@ -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; }

View file

@ -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

View file

@ -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