summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
Diffstat (limited to 'proto')
-rw-r--r--proto/static/config.Y26
-rw-r--r--proto/static/static.c187
-rw-r--r--proto/static/static.h4
3 files changed, 191 insertions, 26 deletions
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 2849015b..46debbc3 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -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; }
diff --git a/proto/static/static.c b/proto/static/static.c
index 4ee2cbdb..448a560a 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -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);
+ {
+ 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)
- if (static_decide((struct static_config *) p->cf, r))
- static_install(p, r, n->iface);
- else
- static_remove(p, r);
+ 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
@@ -323,11 +453,18 @@ static_show_rt(struct static_route *r)
case RTD_ROUTER: bsprintf(via, "via %I", r->via); break;
case RTD_DEVICE: bsprintf(via, "dev %s", r->if_name); 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_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
diff --git a/proto/static/static.h b/proto/static/static.h
index 5c31e009..c91b9cef 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -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