diff options
Diffstat (limited to 'proto')
-rw-r--r-- | proto/babel/babel.c | 13 | ||||
-rw-r--r-- | proto/bgp/attrs.c | 3 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 2 | ||||
-rw-r--r-- | proto/bgp/packets.c | 20 | ||||
-rw-r--r-- | proto/ospf/ospf.c | 2 | ||||
-rw-r--r-- | proto/ospf/rt.c | 64 | ||||
-rw-r--r-- | proto/ospf/rt.h | 2 | ||||
-rw-r--r-- | proto/ospf/topology.c | 4 | ||||
-rw-r--r-- | proto/ospf/topology.h | 2 | ||||
-rw-r--r-- | proto/pipe/pipe.c | 15 | ||||
-rw-r--r-- | proto/rip/rip.c | 31 | ||||
-rw-r--r-- | proto/rpki/rpki.c | 3 | ||||
-rw-r--r-- | proto/static/config.Y | 110 | ||||
-rw-r--r-- | proto/static/static.c | 858 | ||||
-rw-r--r-- | proto/static/static.h | 46 |
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 |