summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
Diffstat (limited to 'proto')
-rw-r--r--proto/babel/babel.c13
-rw-r--r--proto/bgp/attrs.c3
-rw-r--r--proto/bgp/bgp.h2
-rw-r--r--proto/bgp/packets.c20
-rw-r--r--proto/ospf/ospf.c2
-rw-r--r--proto/ospf/rt.c64
-rw-r--r--proto/ospf/rt.h2
-rw-r--r--proto/ospf/topology.c4
-rw-r--r--proto/ospf/topology.h2
-rw-r--r--proto/pipe/pipe.c15
-rw-r--r--proto/rip/rip.c31
-rw-r--r--proto/rpki/rpki.c3
-rw-r--r--proto/static/config.Y110
-rw-r--r--proto/static/static.c858
-rw-r--r--proto/static/static.h46
15 files changed, 556 insertions, 619 deletions
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index 73cb5c3b..1b1d9f62 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -471,21 +471,20 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r)
{
- rta a0 = {
+ rta *ap0 = allocz(RTA_MAX_SIZE);
+ *ap0 = (rta) {
.src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST,
- .dest = r->metric == BABEL_INFINITY ? RTD_UNREACHABLE : RTD_ROUTER,
- .flags = 0,
+ .dest = r->metric == BABEL_INFINITY ? RTD_UNREACHABLE : RTD_UNICAST,
.from = r->neigh->addr,
- .iface = r->neigh->ifa->iface,
+ .nh.iface = r->neigh->ifa->iface,
};
if (r->metric < BABEL_INFINITY)
- a0.gw = r->next_hop;
+ ap0->nh.gw = r->next_hop;
- rta *a = rta_lookup(&a0);
+ rta *a = rta_lookup(ap0);
rte *rte = rte_get_temp(a);
rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id;
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 73318c6a..f2a8e8b5 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1491,8 +1491,7 @@ bgp_get_neighbor(rte *r)
static inline int
rte_resolvable(rte *rt)
{
- int rd = rt->attrs->dest;
- return (rd == RTD_ROUTER) || (rd == RTD_DEVICE) || (rd == RTD_MULTIPATH);
+ return rt->attrs->dest == RTD_UNICAST;
}
int
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 5d2539d5..e7647625 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -337,6 +337,8 @@ struct bgp_parse_state {
u32 mp_reach_af;
u32 mp_unreach_af;
+ mpls_label_stack mls;
+
uint attr_len;
uint ip_reach_len;
uint ip_unreach_len;
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 385d5a36..f7366804 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -743,9 +743,8 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
if (!nbr || (nbr->scope == SCOPE_HOST))
WITHDRAW(BAD_NEXT_HOP);
- a->dest = RTD_ROUTER;
- a->gw = nbr->addr;
- a->iface = nbr->iface;
+ a->dest = RTD_UNICAST;
+ a->nh = (struct nexthop){ .gw = nbr->addr, .iface = nbr->iface };
a->hostentry = NULL;
a->igp_metric = 0;
}
@@ -754,7 +753,7 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
if (ipa_zero(gw))
WITHDRAW(BAD_NEXT_HOP);
- rta_set_recursive_next_hop(c->c.table, a, c->igp_table, gw, ll);
+ rta_set_recursive_next_hop(c->c.table, a, c->igp_table, gw, ll, &(s->mls));
}
}
@@ -792,8 +791,8 @@ bgp_use_gateway(struct bgp_export_state *s)
if (s->channel->cf->next_hop_self)
return 0;
- /* We need valid global gateway */
- if ((ra->dest != RTD_ROUTER) || ipa_zero(ra->gw) || ipa_is_link_local(ra->gw))
+ /* We need one valid global gateway */
+ if ((ra->dest != RTD_UNICAST) || ra->nh.next || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
return 0;
/* Use it when exported to internal peers */
@@ -801,7 +800,7 @@ bgp_use_gateway(struct bgp_export_state *s)
return 1;
/* Use it when forwarded to single-hop BGP peer on on the same iface */
- return p->neigh && (p->neigh->iface == ra->iface);
+ return p->neigh && (p->neigh->iface == ra->nh.iface);
}
static void
@@ -811,7 +810,7 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
{
if (bgp_use_gateway(s))
{
- ip_addr nh[1] = { s->route->attrs->gw };
+ ip_addr nh[1] = { s->route->attrs->nh.gw };
bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, 16);
}
else
@@ -1706,13 +1705,10 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
if (ea)
{
- a = alloca(sizeof(struct rta));
- memset(a, 0, sizeof(struct rta));
+ a = allocz(RTA_MAX_SIZE);
a->source = RTS_BGP;
a->scope = SCOPE_UNIVERSE;
- a->cast = RTC_UNICAST;
- a->dest = RTD_UNREACHABLE;
a->from = s->proto->cf->remote_ip;
a->eattrs = ea;
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index d074600a..daf76ff2 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -235,7 +235,7 @@ ospf_start(struct proto *P)
p->lsab_size = 256;
p->lsab_used = 0;
p->lsab = mb_alloc(P->pool, p->lsab_size);
- p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh));
+ p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
init_list(&(p->iface_list));
init_list(&(p->area_list));
fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 49167ceb..df9eb75b 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -22,7 +22,7 @@ static inline void reset_ri(ort *ort)
}
static inline int
-nh_is_vlink(struct mpnh *nhs)
+nh_is_vlink(struct nexthop *nhs)
{
return !nhs->iface;
}
@@ -33,20 +33,19 @@ unresolved_vlink(ort *ort)
return ort->n.nhs && nh_is_vlink(ort->n.nhs);
}
-static inline struct mpnh *
+static inline struct nexthop *
new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
{
- struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
+ struct nexthop *nh = lp_allocz(p->nhpool, sizeof(struct nexthop));
nh->gw = gw;
nh->iface = iface;
- nh->next = NULL;
nh->weight = weight;
return nh;
}
/* Returns true if there are device nexthops in n */
static inline int
-has_device_nexthops(const struct mpnh *n)
+has_device_nexthops(const struct nexthop *n)
{
for (; n; n = n->next)
if (ipa_zero(n->gw))
@@ -56,13 +55,13 @@ has_device_nexthops(const struct mpnh *n)
}
/* Replace device nexthops with nexthops to gw */
-static struct mpnh *
-fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
+static struct nexthop *
+fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw)
{
- struct mpnh *root1 = NULL;
- struct mpnh *root2 = NULL;
- struct mpnh **nn1 = &root1;
- struct mpnh **nn2 = &root2;
+ struct nexthop *root1 = NULL;
+ struct nexthop *root2 = NULL;
+ struct nexthop **nn1 = &root1;
+ struct nexthop **nn2 = &root2;
if (!p->ecmp)
return new_nexthop(p, gw, n->iface, n->weight);
@@ -73,7 +72,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
for (; n; n = n->next)
{
- struct mpnh *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
+ struct nexthop *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
if (ipa_zero(n->gw))
{
@@ -87,7 +86,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
}
}
- return mpnh_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
+ return nexthop_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
}
@@ -283,7 +282,7 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs)
{
- old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
+ old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool);
old->nhs_reuse = 1;
}
@@ -299,7 +298,7 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs)
{
- old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
+ old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool);
old->nhs_reuse = 1;
}
@@ -1674,18 +1673,18 @@ ospf_rt_spf(struct ospf_proto *p)
static inline int
-inherit_nexthops(struct mpnh *pn)
+inherit_nexthops(struct nexthop *pn)
{
/* Proper nexthops (with defined GW) or dummy vlink nexthops (without iface) */
return pn && (ipa_nonzero(pn->gw) || !pn->iface);
}
-static struct mpnh *
+static struct nexthop *
calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
struct top_hash_entry *par, int pos)
{
struct ospf_proto *p = oa->po;
- struct mpnh *pn = par->nhs;
+ struct nexthop *pn = par->nhs;
struct ospf_iface *ifa;
u32 rid = en->lsa.rt;
@@ -1813,7 +1812,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
if (!link_back(oa, en, par))
return;
- struct mpnh *nhs = calc_next_hop(oa, en, par, pos);
+ struct nexthop *nhs = calc_next_hop(oa, en, par, pos);
if (!nhs)
{
log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -1851,7 +1850,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
/* Merge old and new */
int new_reuse = (par->nhs != nhs);
- en->nhs = mpnh_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
+ en->nhs = nexthop_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
en->nhs_reuse = 1;
return;
}
@@ -1907,8 +1906,7 @@ ort_changed(ort *nf, rta *nr)
(nf->n.metric1 != nf->old_metric1) || (nf->n.metric2 != nf->old_metric2) ||
(nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid) ||
(nr->source != or->source) || (nr->dest != or->dest) ||
- (nr->iface != or->iface) || !ipa_equal(nr->gw, or->gw) ||
- !mpnh_same(nr->nexthops, or->nexthops);
+ !nexthop_same(&(nr->nh), &(or->nh));
}
static void
@@ -1932,7 +1930,7 @@ again1:
/* Sanity check of next-hop addresses, failure should not happen */
if (nf->n.type)
{
- struct mpnh *nh;
+ struct nexthop *nh;
for (nh = nf->n.nhs; nh; nh = nh->next)
if (ipa_nonzero(nh->gw))
{
@@ -1955,26 +1953,10 @@ again1:
.src = p->p.main_source,
.source = nf->n.type,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
+ .dest = RTD_UNICAST,
+ .nh = *(nf->n.nhs),
};
- if (nf->n.nhs->next)
- {
- a0.dest = RTD_MULTIPATH;
- a0.nexthops = nf->n.nhs;
- }
- else if (ipa_nonzero(nf->n.nhs->gw))
- {
- a0.dest = RTD_ROUTER;
- a0.iface = nf->n.nhs->iface;
- a0.gw = nf->n.nhs->gw;
- }
- else
- {
- a0.dest = RTD_DEVICE;
- a0.iface = nf->n.nhs->iface;
- }
-
if (reload || ort_changed(nf, &a0))
{
rta *a = rta_lookup(&a0);
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index 118d09b7..589d2bc5 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -53,7 +53,7 @@ typedef struct orta
struct ospf_area *oa;
struct ospf_area *voa; /* Used when route is replaced in ospf_rt_sum_tr(),
NULL otherwise */
- struct mpnh *nhs; /* Next hops computed during SPF */
+ struct nexthop *nhs; /* Next hops computed during SPF */
struct top_hash_entry *en; /* LSA responsible for this orta */
}
orta;
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index aaaf2e8e..ce77f57a 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -1288,8 +1288,8 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
ip_addr fwd = IPA_NONE;
- if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface))
- fwd = a->gw;
+ if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
+ fwd = a->nh.gw;
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (oa && ipa_zero(fwd))
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index 38447fdf..d1682c54 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -28,7 +28,7 @@ struct top_hash_entry
u16 next_lsa_opts; /* For postponed LSA origination */
bird_clock_t inst_time; /* Time of installation into DB */
struct ort *nf; /* Reference fibnode for sum and ext LSAs, NULL for otherwise */
- struct mpnh *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */
+ struct nexthop *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */
ip_addr lb; /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */
u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */
u32 dist; /* Distance from the root */
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 8924c200..310f3c01 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -51,7 +51,7 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
struct rte_src *src;
rte *e;
- rta a;
+ rta *a;
if (!new && !old)
return;
@@ -65,12 +65,13 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
if (new)
{
- memcpy(&a, new->attrs, sizeof(rta));
+ a = alloca(rta_size(new->attrs));
+ memcpy(a, new->attrs, rta_size(new->attrs));
- a.aflags = 0;
- a.eattrs = attrs;
- a.hostentry = NULL;
- e = rte_get_temp(&a);
+ a->aflags = 0;
+ a->eattrs = attrs;
+ a->hostentry = NULL;
+ e = rte_get_temp(a);
e->pflags = 0;
/* Copy protocol specific embedded attributes. */
@@ -78,7 +79,7 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
e->pref = new->pref;
e->pflags = new->pflags;
- src = a.src;
+ src = a->src;
}
else
{
diff --git a/proto/rip/rip.c b/proto/rip/rip.c
index d87a078c..157093aa 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -147,21 +147,16 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
.src = p->p.main_source,
.source = RTS_RIP,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
+ .dest = RTD_UNICAST,
};
u8 rt_metric = rt->metric;
u16 rt_tag = rt->tag;
- struct rip_rte *rt2 = rt->next;
- /* Find second valid rte */
- while (rt2 && !rip_valid_rte(rt2))
- rt2 = rt2->next;
-
- if (p->ecmp && rt2)
+ if (p->ecmp)
{
/* ECMP route */
- struct mpnh *nhs = NULL;
+ struct nexthop *nhs = NULL;
int num = 0;
for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
@@ -169,33 +164,33 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
if (!rip_valid_rte(rt))
continue;
- struct mpnh *nh = alloca(sizeof(struct mpnh));
+ struct nexthop *nh = allocz(sizeof(struct nexthop));
+
nh->gw = rt->next_hop;
nh->iface = rt->from->nbr->iface;
nh->weight = rt->from->ifa->cf->ecmp_weight;
- mpnh_insert(&nhs, nh);
+
+ nexthop_insert(&nhs, nh);
num++;
if (rt->tag != rt_tag)
rt_tag = 0;
}
- a0.dest = RTD_MULTIPATH;
- a0.nexthops = nhs;
+ a0.nh = *nhs;
}
else
{
/* Unipath route */
- a0.dest = RTD_ROUTER;
- a0.gw = rt->next_hop;
- a0.iface = rt->from->nbr->iface;
a0.from = rt->from->nbr->addr;
+ a0.nh.gw = rt->next_hop;
+ a0.nh.iface = rt->from->nbr->iface;
}
rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a);
- e->u.rip.from = a0.iface;
+ e->u.rip.from = a0.nh.iface;
e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag;
@@ -345,8 +340,8 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
en->metric = rt_metric;
en->tag = rt_tag;
en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
- en->iface = new->attrs->iface;
- en->next_hop = new->attrs->gw;
+ en->iface = new->attrs->nh.iface;
+ en->next_hop = new->attrs->nh.gw;
}
else
{
diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c
index 0d4b1fd3..5459d9c3 100644
--- a/proto/rpki/rpki.c
+++ b/proto/rpki/rpki.c
@@ -124,8 +124,7 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
.src = p->p.main_source,
.source = RTS_RPKI,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST,
- .dest = RTD_BLACKHOLE,
+ .dest = RTD_NONE,
};
rta *a = rta_lookup(&a0);
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 86359f0b..cd8bfcec 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -13,25 +13,35 @@ CF_HDR
CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto)
-static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
+static struct static_route *this_srt, *this_snh;
static struct f_inst **this_srt_last_cmd;
-static void
-static_route_finish(void)
+static struct static_route *
+static_nexthop_new(void)
{
- struct static_route *r;
+ struct static_route *nh = this_srt;
+
+ if (this_snh)
+ {
+ /* Additional next hop */
+ nh = cfg_allocz(sizeof(struct static_route));
+ nh->net = this_srt->net;
+ this_snh->mp_next = nh;
+ }
- /* Update undefined use_bfd entries in multipath nexthops */
- if (this_srt->dest == RTD_MULTIPATH)
- for (r = this_srt->mp_next; r; r = r->mp_next)
- if (r->use_bfd < 0)
- r->use_bfd = this_srt->use_bfd;
-}
+ nh->dest = RTD_UNICAST;
+ nh->mp_head = this_srt;
+ return nh;
+};
+
+static void
+static_route_finish(void)
+{ }
CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
-CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD)
+CF_KEYWORDS(WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
CF_GRAMMAR
@@ -41,7 +51,7 @@ CF_ADDTO(proto, static_proto '}')
static_proto_start: proto_start STATIC
{
this_proto = proto_config_new(&proto_static, $1);
- static_init_config(STATIC_CFG);
+ init_list(&STATIC_CFG->routes);
};
static_proto:
@@ -53,58 +63,55 @@ static_proto:
| static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
;
+stat_nexthop:
+ VIA ipa ipa_scope {
+ this_snh = static_nexthop_new();
+ this_snh->via = $2;
+ this_snh->iface = $3;
+ }
+ | VIA TEXT {
+ this_snh = static_nexthop_new();
+ this_snh->via = IPA_NONE;
+ this_snh->iface = if_get_by_name($2);
+ }
+ | stat_nexthop MPLS label_stack {
+ this_snh->mls = $3;
+ }
+ | stat_nexthop WEIGHT expr {
+ this_snh->weight = $3 - 1;
+ if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
+ }
+ | stat_nexthop BFD bool {
+ this_snh->use_bfd = $3; cf_check_bfd($3);
+ }
+;
+
+stat_nexthops:
+ stat_nexthop
+ | stat_nexthops stat_nexthop
+;
+
stat_route0: ROUTE net_any {
this_srt = cfg_allocz(sizeof(struct static_route));
- add_tail(&STATIC_CFG->other_routes, &this_srt->n);
+ add_tail(&STATIC_CFG->routes, &this_srt->n);
this_srt->net = $2;
this_srt_last_cmd = &(this_srt->cmds);
+ this_srt->mp_next = NULL;
+ this_snh = NULL;
}
;
-stat_multipath1:
- VIA ipa ipa_scope {
- 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->via_if = $3;
- this_srt_nh->if_name = (void *) this_srt; /* really */
- this_srt_nh->use_bfd = -1; /* undefined */
- }
- | stat_multipath1 WEIGHT expr {
- this_srt_nh->weight = $3 - 1;
- if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
- }
- | stat_multipath1 BFD bool {
- this_srt_nh->use_bfd = $3; cf_check_bfd($3);
- }
- ;
-
-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 ipa_scope {
- this_srt->dest = RTD_ROUTER;
+ stat_route0 stat_nexthops
+ | stat_route0 RECURSIVE ipa {
+ this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3;
- this_srt->via_if = $4;
- }
- | stat_route0 VIA TEXT {
- this_srt->dest = RTD_DEVICE;
- this_srt->if_name = $3;
- 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 RECURSIVE ipa {
+ | stat_route0 RECURSIVE ipa MPLS label_stack {
this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3;
+ this_srt->mls = $5;
}
-
| stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; }
| stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; }
| stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; }
@@ -114,7 +121,6 @@ stat_route:
stat_route_item:
cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
- | BFD bool ';' { this_srt->use_bfd = $2; cf_check_bfd($2); }
;
stat_route_opts:
diff --git a/proto/static/static.c b/proto/static/static.c
index fb547537..adefa0b2 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -9,33 +9,32 @@
/**
* DOC: Static
*
- * The Static protocol is implemented in a straightforward way. It keeps
- * two lists of static routes: one containing interface routes and one
- * holding the remaining ones. Interface routes are inserted and removed according
- * to interface events received from the core via the if_notify() hook. Routes
- * pointing to a neighboring router use a sticky node in the neighbor cache
- * to be notified about gaining or losing the neighbor. Special
- * routes like black holes or rejects are inserted all the time.
+ * The Static protocol is implemented in a straightforward way. It keeps a list
+ * of static routes. Routes of dest RTD_UNICAST have associated sticky node in
+ * the neighbor cache to be notified about gaining or losing the neighbor and
+ * about interface-related events (e.g. link down). They may also have a BFD
+ * request if associated with a BFD session. When a route is notified,
+ * static_decide() is used to see whether the route activeness is changed. In
+ * such case, the route is marked as dirty and scheduled to be announced or
+ * withdrawn, which is done asynchronously from event hook. Routes of other
+ * types (e.g. black holes) are announced 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 if_name field for other purposes.
+ * Multipath routes are a bit tricky. To represent additional next hops, dummy
+ * static_route nodes are used, which are chained using @mp_next field and link
+ * to the master node by @mp_head field. Each next hop has a separate neighbor
+ * entry and an activeness state, but the master node is used for most purposes.
+ * Note that most functions DO NOT accept dummy nodes as arguments.
*
* 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
- * newly added routes and removes the obsolete ones.
+ * difference between the lists of static routes and it just inserts the newly
+ * added routes, removes the obsolete ones and reannounces changed ones.
*/
#undef LOCAL_DEBUG
+#include <stdlib.h>
+
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
@@ -51,100 +50,119 @@
static linpool *static_lp;
static inline rtable *
-p_igp_table(struct proto *p)
+p_igp_table(struct static_proto *p)
{
- struct static_config *cf = (void *) p->cf;
- return cf->igp_table ? cf->igp_table->table : p->main_channel->table;
+ struct static_config *cf = (void *) p->p.cf;
+ return cf->igp_table ? cf->igp_table->table : p->p.main_channel->table;
}
static void
-static_install(struct proto *p, struct static_route *r, struct iface *ifa)
+static_announce_rte(struct static_proto *p, struct static_route *r)
{
- rta a;
- rte *e;
+ rta *a = allocz(RTA_MAX_SIZE);
+ a->src = p->p.main_source;
+ a->source = RTS_STATIC;
+ a->scope = SCOPE_UNIVERSE;
+ a->dest = r->dest;
- if (r->installed > 0)
- return;
+ if (r->dest == RTD_UNICAST)
+ {
+ struct static_route *r2;
+ struct nexthop *nhs = NULL;
- DBG("Installing static route %N, rtd=%d\n", r->net, r->dest);
- bzero(&a, sizeof(a));
- a.src = p->main_source;
- a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
- a.scope = SCOPE_UNIVERSE;
- a.cast = RTC_UNICAST;
- a.dest = r->dest;
- a.gw = r->via;
- a.iface = ifa;
-
- if (r->dest == RTD_MULTIPATH)
+ for (r2 = r; r2; r2 = r2->mp_next)
{
- struct static_route *r2;
- struct mpnh *nhs = NULL;
-
- 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->weight;
- mpnh_insert(&nhs, nh);
- }
-
- /* 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;
+ if (!r2->active)
+ continue;
+
+ struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE);
+ nh->gw = r2->via;
+ nh->iface = r2->neigh->iface;
+ nh->weight = r2->weight;
+ if (r2->mls)
+ {
+ nh->labels = r2->mls->len;
+ memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32));
+ }
+
+ nexthop_insert(&nhs, nh);
}
+ if (!nhs)
+ goto withdraw;
+
+ nexthop_link(a, nhs);
+ }
+
if (r->dest == RTDX_RECURSIVE)
- rta_set_recursive_next_hop(p->main_channel->table, &a, p_igp_table(p), r->via, IPA_NONE);
+ rta_set_recursive_next_hop(p->p.main_channel->table, a, p_igp_table(p), r->via, IPA_NONE, r->mls);
- /* We skip rta_lookup() here */
+ /* Already announced */
+ if (r->state == SRS_CLEAN)
+ return;
- e = rte_get_temp(&a);
+ /* We skip rta_lookup() here */
+ rte *e = rte_get_temp(a);
e->pflags = 0;
if (r->cmds)
f_eval_rte(r->cmds, &e, static_lp);
- rte_update(p, r->net, e);
- r->installed = 1;
+ rte_update(&p->p, r->net, e);
+ r->state = SRS_CLEAN;
if (r->cmds)
lp_flush(static_lp);
+
+ return;
+
+withdraw:
+ if (r->state == SRS_DOWN)
+ return;
+
+ rte_update(&p->p, r->net, NULL);
+ r->state = SRS_DOWN;
}
static void
-static_remove(struct proto *p, struct static_route *r)
+static_mark_rte(struct static_proto *p, struct static_route *r)
{
- if (!r->installed)
+ if (r->state == SRS_DIRTY)
return;
- DBG("Removing static route %N via %I\n", r->net, r->via);
- rte_update(p, r->net, NULL);
- r->installed = 0;
+ r->state = SRS_DIRTY;
+ BUFFER_PUSH(p->marked) = r;
+
+ if (!ev_active(p->event))
+ ev_schedule(p->event);
+}
+
+static void
+static_announce_marked(void *P)
+{
+ struct static_proto *p = P;
+
+ BUFFER_WALK(p->marked, r)
+ static_announce_rte(P, r);
+
+ BUFFER_FLUSH(p->marked);
}
static void
static_bfd_notify(struct bfd_request *req);
static void
-static_update_bfd(struct proto *p, struct static_route *r)
+static_update_bfd(struct static_proto *p, struct static_route *r)
{
+ /* The @r is a RTD_UNICAST next hop, may be a dummy node */
+
struct neighbor *nb = r->neigh;
int bfd_up = (nb->scope > 0) && r->use_bfd;
if (bfd_up && !r->bfd_req)
{
// ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
- r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
+ r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, nb->iface,
static_bfd_notify, r);
}
@@ -156,223 +174,171 @@ static_update_bfd(struct proto *p, struct static_route *r)
}
static int
-static_decide(struct static_config *cf, struct static_route *r)
+static_decide(struct static_proto *p, 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) */
+ /* The @r is a RTD_UNICAST next hop, may be a dummy node */
+
+ struct static_config *cf = (void *) p->p.cf;
+ uint old_active = r->active;
if (r->neigh->scope < 0)
- return 0;
+ goto fail;
if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
- return 0;
+ goto fail;
- if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
- return 0;
+ if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP))
+ goto fail;
- return 1;
-}
+ r->active = 1;
+ return !old_active;
+fail:
+ r->active = 0;
+ return old_active;
+}
static void
-static_add(struct proto *p, struct static_config *cf, struct static_route *r)
+static_add_rte(struct static_proto *p, struct static_route *r)
{
- DBG("static_add(%N,%d)\n", r->net, r->dest);
- switch (r->dest)
- {
- case RTD_ROUTER:
- {
- struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
- if (n)
- {
- r->chain = n->data;
- n->data = r;
- r->neigh = n;
-
- static_update_bfd(p, r);
- if (static_decide(cf, r))
- static_install(p, r, n->iface);
- else
- static_remove(p, r);
- }
- else
- {
- log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
- static_remove(p, r);
- }
- break;
- }
+ if (r->dest == RTD_UNICAST)
+ {
+ struct static_route *r2;
+ struct neighbor *n;
- case RTD_DEVICE:
- break;
+ for (r2 = r; r2; r2 = r2->mp_next)
+ {
+ n = ipa_nonzero(r2->via) ?
+ neigh_find2(&p->p, &r2->via, r2->iface, NEF_STICKY) :
+ neigh_find_iface(&p->p, r2->iface);
- case RTD_MULTIPATH:
+ if (!n)
{
- int count = 0;
- struct static_route *r2;
-
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- {
- struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
- if (n)
- {
- r2->chain = n->data;
- n->data = r2;
- r2->neigh = n;
-
- static_update_bfd(p, r2);
- 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;
+ log(L_WARN "Invalid next hop %I of static route %N", r2->via, r2->net);
+ continue;
}
- default:
- static_install(p, r, NULL);
+ r2->neigh = n;
+ r2->chain = n->data;
+ n->data = r2;
+
+ static_update_bfd(p, r2);
+ static_decide(p, r2);
}
+ }
+
+ static_announce_rte(p, r);
}
static void
-static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
+static_reset_rte(struct static_proto *p UNUSED, struct static_route *r)
{
struct static_route *r2;
- if (r->bfd_req)
+ for (r2 = r; r2; r2 = r2->mp_next)
{
- rfree(r->bfd_req);
- r->bfd_req = NULL;
- }
+ r2->neigh = NULL;
+ r2->chain = NULL;
- if (r->dest == RTD_MULTIPATH)
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- if (r2->bfd_req)
- {
- rfree(r2->bfd_req);
- r2->bfd_req = NULL;
- }
+ r2->state = 0;
+ r2->active = 0;
+
+ rfree(r2->bfd_req);
+ r2->bfd_req = NULL;
+ }
}
-static int
-static_start(struct proto *p)
+static void
+static_remove_rte(struct static_proto *p, struct static_route *r)
{
- struct static_config *cf = (void *) p->cf;
- struct static_route *r;
+ if (r->state)
+ rte_update(&p->p, r->net, NULL);
- DBG("Static: take off!\n");
+ static_reset_rte(p, r);
+}
- if (!static_lp)
- static_lp = lp_new(&root_pool, 1008);
- if (cf->igp_table)
- rt_lock_table(cf->igp_table->table);
+static inline int
+static_same_dest(struct static_route *x, struct static_route *y)
+{
+ if (x->dest != y->dest)
+ return 0;
- /* We have to go UP before routes could be installed */
- proto_notify_state(p, PS_UP);
+ switch (x->dest)
+ {
+ case RTD_UNICAST:
+ for (; x && y; x = x->mp_next, y = y->mp_next)
+ {
+ if (!ipa_equal(x->via, y->via) ||
+ (x->iface != y->iface) ||
+ (x->use_bfd != y->use_bfd) ||
+ (x->weight != y->weight) ||
+ (!x->mls != !y->mls) ||
+ ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+ return 0;
+
+ if (!x->mls)
+ continue;
+
+ for (uint i = 0; i < x->mls->len; i++)
+ if (x->mls->stack[i] != y->mls->stack[i])
+ return 0;
+ }
+ return !x && !y;
- WALK_LIST(r, cf->other_routes)
- static_add(p, cf, r);
- return PS_UP;
-}
+ case RTDX_RECURSIVE:
+ if (!ipa_equal(x->via, y->via) ||
+ (!x->mls != !y->mls) ||
+ ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+ return 0;
-static int
-static_shutdown(struct proto *p)
-{
- struct static_config *cf = (void *) p->cf;
- struct static_route *r;
+ if (!x->mls)
+ return 1;
- /* Just reset the flag, the routes will be flushed by the nest */
- WALK_LIST(r, cf->iface_routes)
- r->installed = 0;
- WALK_LIST(r, cf->other_routes)
- {
- static_rte_cleanup(p, r);
- r->installed = 0;
- }
+ for (uint i = 0; i < x->mls->len; i++)
+ if (x->mls->stack[i] != y->mls->stack[i])
+ return 0;
- /* Handle failure during channel reconfigure */
- /* FIXME: This should be handled in a better way */
- cf = (void *) p->cf_new;
- if (cf)
- {
- WALK_LIST(r, cf->iface_routes)
- r->installed = 0;
- WALK_LIST(r, cf->other_routes)
- r->installed = 0;
- }
+ return 1;
- return PS_DOWN;
+ default:
+ return 1;
+ }
}
-static void
-static_cleanup(struct proto *p)
+static inline int
+static_same_rte(struct static_route *or, struct static_route *nr)
{
- struct static_config *cf = (void *) p->cf;
-
- if (cf->igp_table)
- rt_unlock_table(cf->igp_table->table);
+ /* Note that i_same() requires arguments in (new, old) order */
+ return static_same_dest(or, nr) && i_same(nr->cmds, or->cmds);
}
static void
-static_update_rte(struct proto *p, struct static_route *r)
+static_reconfigure_rte(struct static_proto *p, struct static_route *or, struct static_route *nr)
{
- switch (r->dest)
- {
- case RTD_ROUTER:
- if (static_decide((struct static_config *) p->cf, r))
- static_install(p, r, r->neigh->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);
+ if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr))
+ nr->state = SRS_DIRTY;
+ else
+ nr->state = or->state;
- break;
- }
- }
+ static_add_rte(p, nr);
+ static_reset_rte(p, or);
}
+
static void
static_neigh_notify(struct neighbor *n)
{
- struct proto *p = n->proto;
+ struct static_proto *p = (void *) n->proto;
struct static_route *r;
DBG("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)
{
static_update_bfd(p, r);
- static_update_rte(p, r);
+
+ if (static_decide(p, r))
+ static_mark_rte(p, r->mp_head);
}
}
@@ -380,77 +346,20 @@ static void
static_bfd_notify(struct bfd_request *req)
{
struct static_route *r = req->data;
- struct proto *p = r->neigh->proto;
+ struct static_proto *p = (void *) r->neigh->proto;
// if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
- static_update_rte(p, r);
-}
-
-static void
-static_dump_rt(struct static_route *r)
-{
- debug("%-1N: ", r->net);
- switch (r->dest)
- {
- case RTD_ROUTER:
- debug("via %I\n", r->via);
- break;
- case RTD_DEVICE:
- debug("dev %s\n", r->if_name);
- break;
- default:
- debug("rtd %d\n", r->dest);
- break;
- }
-}
-
-static void
-static_dump(struct proto *p)
-{
- struct static_config *c = (void *) p->cf;
- struct static_route *r;
-
- debug("Independent static routes:\n");
- WALK_LIST(r, c->other_routes)
- static_dump_rt(r);
- debug("Device static routes:\n");
- WALK_LIST(r, c->iface_routes)
- static_dump_rt(r);
+ if (static_decide(p, r))
+ static_mark_rte(p, r->mp_head);
}
-static void
-static_if_notify(struct proto *p, unsigned flags, struct iface *i)
-{
- struct static_route *r;
- struct static_config *c = (void *) p->cf;
-
- if (flags & IF_CHANGE_UP)
- {
- WALK_LIST(r, c->iface_routes)
- if (!strcmp(r->if_name, i->name))
- static_install(p, r, i);
- }
- else if (flags & IF_CHANGE_DOWN)
- {
- WALK_LIST(r, c->iface_routes)
- if (!strcmp(r->if_name, i->name))
- static_remove(p, r);
- }
-}
-
-int
+static int
static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
{
return 1;
}
-void
-static_init_config(struct static_config *c)
-{
- init_list(&c->iface_routes);
- init_list(&c->other_routes);
-}
static void
static_postconfig(struct proto_config *CF)
@@ -461,17 +370,11 @@ static_postconfig(struct proto_config *CF)
if (EMPTY_LIST(CF->channels))
cf_error("Channel not specified");
-
- WALK_LIST(r, cf->iface_routes)
- if (r->net->type != CF->net_type)
- cf_error("Route %N incompatible with channel type", r->net);
-
- WALK_LIST(r, cf->other_routes)
- if (r->net->type != CF->net_type)
+ WALK_LIST(r, cf->routes)
+ if (r->net && (r->net->type != CF->net_type))
cf_error("Route %N incompatible with channel type", r->net);
}
-
static struct proto *
static_init(struct proto_config *CF)
{
@@ -482,84 +385,84 @@ static_init(struct proto_config *CF)
P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
P->neigh_notify = static_neigh_notify;
- P->if_notify = static_if_notify;
P->rte_mergable = static_rte_mergable;
return P;
}
-static inline int
-static_same_dest(struct static_route *x, struct static_route *y)
+static int
+static_start(struct proto *P)
{
- if (x->dest != y->dest)
- return 0;
+ struct static_proto *p = (void *) P;
+ struct static_config *cf = (void *) P->cf;
+ struct static_route *r;
- switch (x->dest)
- {
- case RTD_ROUTER:
- return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
-
- 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) ||
- (x->via_if != y->via_if) ||
- (x->use_bfd != y->use_bfd) ||
- (x->weight != y->weight))
- return 0;
- return !x && !y;
+ if (!static_lp)
+ static_lp = lp_new(&root_pool, 1008);
- case RTDX_RECURSIVE:
- return ipa_equal(x->via, y->via);
+ if (cf->igp_table)
+ rt_lock_table(cf->igp_table->table);
- default:
- return 1;
- }
+ p->event = ev_new(p->p.pool);
+ p->event->hook = static_announce_marked;
+ p->event->data = p;
+
+ BUFFER_INIT(p->marked, p->p.pool, 4);
+
+ /* We have to go UP before routes could be installed */
+ proto_notify_state(P, PS_UP);
+
+ WALK_LIST(r, cf->routes)
+ static_add_rte(p, r);
+
+ return PS_UP;
}
-static inline int
-static_same_rte(struct static_route *x, struct static_route *y)
+static int
+static_shutdown(struct proto *P)
{
- return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
-}
+ struct static_proto *p = (void *) P;
+ struct static_config *cf = (void *) P->cf;
+ struct static_route *r;
+
+ /* Just reset the flag, the routes will be flushed by the nest */
+ WALK_LIST(r, cf->routes)
+ static_reset_rte(p, r);
+ return PS_DOWN;
+}
static void
-static_match(struct proto *p, struct static_route *r, struct static_config *n)
+static_cleanup(struct proto *P)
{
- struct static_route *t;
-
- /*
- * For given old route *r we find whether a route to the same
- * network is also in the new route list. In that case, we keep the
- * route and possibly update the route later if destination changed.
- * Otherwise, we remove the route.
- */
-
- if (r->neigh)
- r->neigh->data = NULL;
+ struct static_config *cf = (void *) P->cf;
- WALK_LIST(t, n->iface_routes)
- if (net_equal(r->net, t->net))
- goto found;
+ if (cf->igp_table)
+ rt_unlock_table(cf->igp_table->table);
+}
- WALK_LIST(t, n->other_routes)
- if (net_equal(r->net, t->net))
- goto found;
+static void
+static_dump_rte(struct static_route *r)
+{
+ debug("%-1N: ", r->net);
+ if (r->dest == RTD_UNICAST)
+ if (r->iface && ipa_zero(r->via))
+ debug("dev %s\n", r->iface->name);
+ else
+ debug("via %I%J\n", r->via, r->iface);
+ else
+ debug("rtd %d\n", r->dest);
+}
- static_remove(p, r);
- return;
+static void
+static_dump(struct proto *P)
+{
+ struct static_config *c = (void *) P->cf;
+ struct static_route *r;
- found:
- /* If destination is different, force reinstall */
- if ((r->installed > 0) && !static_same_rte(r, t))
- t->installed = -1;
- else
- t->installed = r->installed;
+ debug("Static routes:\n");
+ WALK_LIST(r, c->routes)
+ static_dump_rte(r);
}
static inline rtable *
@@ -568,76 +471,87 @@ cf_igp_table(struct static_config *cf)
return cf->igp_table ? cf->igp_table->table : NULL;
}
+static inline int
+static_cmp_rte(const void *X, const void *Y)
+{
+ struct static_route *x = *(void **)X, *y = *(void **)Y;
+ return net_compare(x->net, y->net);
+}
+
static int
-static_reconfigure(struct proto *p, struct proto_config *CF)
+static_reconfigure(struct proto *P, struct proto_config *CF)
{
- struct static_config *o = (void *) p->cf;
+ struct static_proto *p = (void *) P;
+ struct static_config *o = (void *) P->cf;
struct static_config *n = (void *) CF;
- struct static_route *r;
+ struct static_route *r, *r2, *or, *nr;
if (cf_igp_table(o) != cf_igp_table(n))
return 0;
- if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
return 0;
- /* Delete all obsolete routes and reset neighbor entries */
- WALK_LIST(r, o->iface_routes)
- static_match(p, r, n);
- WALK_LIST(r, o->other_routes)
- static_match(p, r, n);
+ p->p.cf = CF;
- /* Now add all new routes, those not changed will be ignored by static_install() */
- WALK_LIST(r, n->iface_routes)
- {
- struct iface *ifa;
- if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
- static_install(p, r, ifa);
- }
- WALK_LIST(r, n->other_routes)
- static_add(p, n, r);
+ /* Reset route lists in neighbor entries */
+ WALK_LIST(r, o->routes)
+ for (r2 = r; r2; r2 = r2->mp_next)
+ if (r2->neigh)
+ r2->neigh->data = NULL;
- WALK_LIST(r, o->other_routes)
- static_rte_cleanup(p, r);
+ /* Reconfigure initial matching sequence */
+ for (or = HEAD(o->routes), nr = HEAD(n->routes);
+ NODE_VALID(or) && NODE_VALID(nr) && net_equal(or->net, nr->net);
+ or = NODE_NEXT(or), nr = NODE_NEXT(nr))
+ static_reconfigure_rte(p, or, nr);
- return 1;
-}
+ if (!NODE_VALID(or) && !NODE_VALID(nr))
+ return 1;
-static void
-static_copy_routes(list *dlst, list *slst)
-{
- struct static_route *dr, *sr;
+ /* Reconfigure remaining routes, sort them to find matching pairs */
+ struct static_route *or2, *nr2, **orbuf, **nrbuf;
+ uint ornum = 0, nrnum = 0, orpos = 0, nrpos = 0, i;
- init_list(dlst);
- WALK_LIST(sr, *slst)
- {
- /* copy one route */
- dr = cfg_alloc(sizeof(struct static_route));
- memcpy(dr, sr, sizeof(struct static_route));
-
- /* This fn is supposed to be called on fresh src routes, which have 'live'
- fields (like .chain, .neigh or .installed) zero, so no need to zero them */
-
- /* We need to copy multipath chain, because there are backptrs in 'if_name' */
- if (dr->dest == RTD_MULTIPATH)
- {
- struct static_route *md, *ms, **mp_last;
-
- mp_last = &(dr->mp_next);
- for (ms = sr->mp_next; ms; ms = ms->mp_next)
- {
- md = cfg_alloc(sizeof(struct static_route));
- memcpy(md, ms, sizeof(struct static_route));
- md->if_name = (void *) dr; /* really */
-
- *mp_last = md;
- mp_last = &(md->mp_next);
- }
- *mp_last = NULL;
- }
-
- add_tail(dlst, (node *) dr);
- }
+ for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2))
+ ornum++;
+
+ for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2))
+ nrnum++;
+
+ orbuf = xmalloc(ornum * sizeof(void *));
+ nrbuf = xmalloc(nrnum * sizeof(void *));
+
+ for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2))
+ orbuf[i] = or2;
+
+ for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2))
+ nrbuf[i] = nr2;
+
+ qsort(orbuf, ornum, sizeof(struct static_route *), static_cmp_rte);
+ qsort(nrbuf, nrnum, sizeof(struct static_route *), static_cmp_rte);
+
+ while ((orpos < ornum) && (nrpos < nrnum))
+ {
+ int x = net_compare(orbuf[orpos]->net, nrbuf[nrpos]->net);
+ if (x < 0)
+ static_remove_rte(p, orbuf[orpos++]);
+ else if (x > 0)
+ static_add_rte(p, nrbuf[nrpos++]);
+ else
+ static_reconfigure_rte(p, orbuf[orpos++], nrbuf[nrpos++]);
+ }
+
+ while (orpos < ornum)
+ static_remove_rte(p, orbuf[orpos++]);
+
+ while (nrpos < nrnum)
+ static_add_rte(p, nrbuf[nrpos++]);
+
+ xfree(orbuf);
+ xfree(nrbuf);
+
+ return 1;
}
static void
@@ -646,52 +560,64 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
struct static_config *d = (struct static_config *) dest;
struct static_config *s = (struct static_config *) src;
- /* Copy route lists */
- static_copy_routes(&d->iface_routes, &s->iface_routes);
- static_copy_routes(&d->other_routes, &s->other_routes);
-}
+ struct static_route *srt, *snh;
-struct protocol proto_static = {
- .name = "Static",
- .template = "static%d",
- .preference = DEF_PREF_STATIC,
- .channel_mask = NB_ANY,
- .proto_size = sizeof(struct proto),
- .config_size = sizeof(struct static_config),
- .postconfig = static_postconfig,
- .init = static_init,
- .dump = static_dump,
- .start = static_start,
- .shutdown = static_shutdown,
- .cleanup = static_cleanup,
- .reconfigure = static_reconfigure,
- .copy_config = static_copy_config
-};
+ /* Copy route list */
+ init_list(&d->routes);
+ WALK_LIST(srt, s->routes)
+ {
+ struct static_route *drt = NULL, *dnh = NULL, **dnp = &drt;
+
+ for (snh = srt; snh; snh = snh->mp_next)
+ {
+ dnh = cfg_alloc(sizeof(struct static_route));
+ memcpy(dnh, snh, sizeof(struct static_route));
+
+ if (!drt)
+ add_tail(&d->routes, &(dnh->n));
+
+ *dnp = dnh;
+ dnp = &(dnh->mp_next);
+
+ if (snh->mp_head)
+ dnh->mp_head = drt;
+ }
+ }
+}
static void
static_show_rt(struct static_route *r)
{
- byte via[IPA_MAX_TEXT_LENGTH + 25];
-
switch (r->dest)
+ {
+ case RTD_UNICAST:
+ {
+ struct static_route *r2;
+
+ cli_msg(-1009, "%N", r->net);
+ for (r2 = r; r2; r2 = r2->mp_next)
{
- case RTD_ROUTER: bsprintf(via, "via %I%J", r->via, r->via_if); 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_PROHIBIT: bsprintf(via, "prohibited"); break;
- case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
- case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
- default: bsprintf(via, "???");
+ if (r2->iface && ipa_zero(r2->via))
+ cli_msg(-1009, "\tdev %s%s%s", r2->iface->name,
+ r2->bfd_req ? " (bfd)" : "", r2->active ? "" : " (dormant)");
+ else
+ cli_msg(-1009, "\tvia %I%J%s%s", r2->via, r2->iface,
+ r2->bfd_req ? " (bfd)" : "", r2->active ? "" : " (dormant)");
}
- cli_msg(-1009, "%N %s%s%s", r->net, via,
- r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
+ break;
+ }
- struct static_route *r2;
- if (r->dest == RTD_MULTIPATH)
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->weight + 1,
- r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
+ case RTD_NONE:
+ case RTD_BLACKHOLE:
+ case RTD_UNREACHABLE:
+ case RTD_PROHIBIT:
+ cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]);
+ break;
+
+ case RTDX_RECURSIVE:
+ cli_msg(-1009, "%N\trecursive %I", r->net, r->via);
+ break;
+ }
}
void
@@ -700,9 +626,25 @@ static_show(struct proto *P)
struct static_config *c = (void *) P->cf;
struct static_route *r;
- WALK_LIST(r, c->other_routes)
- static_show_rt(r);
- WALK_LIST(r, c->iface_routes)
+ WALK_LIST(r, c->routes)
static_show_rt(r);
cli_msg(0, "");
}
+
+
+struct protocol proto_static = {
+ .name = "Static",
+ .template = "static%d",
+ .preference = DEF_PREF_STATIC,
+ .channel_mask = NB_ANY,
+ .proto_size = sizeof(struct static_proto),
+ .config_size = sizeof(struct static_config),
+ .postconfig = static_postconfig,
+ .init = static_init,
+ .dump = static_dump,
+ .start = static_start,
+ .shutdown = static_shutdown,
+ .cleanup = static_cleanup,
+ .reconfigure = static_reconfigure,
+ .copy_config = static_copy_config
+};
diff --git a/proto/static/static.h b/proto/static/static.h
index 51486e83..0976a9c9 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -11,41 +11,57 @@
#include "nest/route.h"
#include "nest/bfd.h"
+#include "lib/buffer.h"
struct static_config {
struct proto_config c;
- list iface_routes; /* Routes to search on interface events */
- list other_routes; /* Routes hooked to neighbor cache and reject routes */
+ list routes; /* List of static routes (struct static_route) */
int check_link; /* Whether iface link state is used */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
};
+struct static_proto {
+ struct proto p;
-void static_init_config(struct static_config *);
+ struct event *event; /* Event for announcing updated routes */
+ BUFFER(struct static_route *) marked; /* Routes marked for reannouncement */
+};
struct static_route {
node n;
- struct static_route *chain; /* Next for the same neighbor */
net_addr *net; /* Network we route */
- int dest; /* Destination type (RTD_*) */
ip_addr via; /* Destination router */
- struct iface *via_if; /* Destination iface, for link-local vias */
- struct neighbor *neigh;
- byte *if_name; /* Name for RTD_DEVICE routes */
- struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */
+ struct iface *iface; /* Destination iface, for link-local vias or device routes */
+ struct neighbor *neigh; /* Associated neighbor entry */
+ struct static_route *chain; /* Next for the same neighbor */
+ struct static_route *mp_head; /* First nexthop of this route */
+ struct static_route *mp_next; /* Nexthops for multipath routes */
struct f_inst *cmds; /* List of commands for setting attributes */
- int installed; /* Installed in rt table, -1 for reinstall */
- int use_bfd; /* Configured to use BFD */
- int weight; /* Multipath next hop weight */
+ byte dest; /* Destination type (RTD_*) */
+ byte state; /* State of route announcement (SRS_*) */
+ byte active; /* Next hop is active (nbr/iface/BFD available) */
+ byte weight; /* Multipath next hop weight */
+ byte use_bfd; /* Configured to use BFD */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
+ mpls_label_stack *mls; /* MPLS label stack; may be NULL */
};
-/* Dummy nodes (parts of multipath route) abuses masklen field for weight
- and if_name field for a ptr to the master (RTD_MULTIPATH) node. */
-
+/*
+ * Note that data fields neigh, chain, state, active and bfd_req are runtime
+ * data, not real configuration data. Must be handled carefully.
+ *
+ * Regular (i.e. dest == RTD_UNICAST) routes use static_route structure for
+ * additional next hops (fields mp_head, mp_next). Note that 'state' is for
+ * whole route, while 'active' is for each next hop. Also note that fields
+ * mp_head, mp_next, active are zero for other kinds of routes.
+ */
#define RTDX_RECURSIVE 0x7f /* Phony dest value for recursive routes */
+#define SRS_DOWN 0 /* Route is not announced */
+#define SRS_CLEAN 1 /* Route is active and announced */
+#define SRS_DIRTY 2 /* Route changed since announcement */
+
void static_show(struct proto *);
#endif