switch (sa.sa_code)
{
- case SA_NET: RESULT(sa.type, net, (*fs->rte)->net->n.addr); break;
- case SA_PROTO: RESULT(sa.type, s, (*fs->rte)->src->proto->name); break;
+ case SA_NET: RESULT(sa.type, net, fs->rte->net); break;
+ case SA_PROTO: RESULT(sa.type, s, fs->rte->src->proto->name); break;
- case SA_DEST: RESULT(sa.type, i, rta->dest); break;
default:
{
- struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
- struct nexthop *nh = nh_ea ? &((struct nexthop_adata *) nh_ea->u.ptr)->nh : NULL;
+ struct eattr *nhea = ea_find(*fs->eattrs, &ea_gen_nexthop);
+ struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
+ struct nexthop *nh = nhad ? &nhad->nh : NULL;
switch (sa.sa_code)
{
/* Source: An old method to devise the route source protocol and kind.
* To be superseded in a near future by something more informative. */
extern struct ea_class ea_gen_source;
-static inline u32 rt_get_source_attr(rte *rt)
+static inline u32 rt_get_source_attr(const rte *rt)
{ return ea_get_int(rt->attrs->eattrs, &ea_gen_source, 0); }
-#define FLOWSPEC_UNKNOWN 0
-#define FLOWSPEC_VALID 1
-#define FLOWSPEC_INVALID 2
+ /* Flowspec validation result */
-static inline u32 rt_get_flowspec_valid(rte *rt)
++enum flowspec_valid {
++ FLOWSPEC_UNKNOWN = 0,
++ FLOWSPEC_VALID = 1,
++ FLOWSPEC_INVALID = 2,
++ FLOWSPEC__MAX,
++};
++
++extern const char * flowspec_valid_names[FLOWSPEC__MAX];
++static inline const char *flowspec_valid_name(enum flowspec_valid v)
++{ return (v < FLOWSPEC__MAX) ? flowspec_valid_names[v] : "???"; }
+
+ extern struct ea_class ea_gen_flowspec_valid;
++static inline enum flowspec_valid rt_get_flowspec_valid(rte *rt)
+ { return ea_get_int(rt->attrs->eattrs, &ea_gen_flowspec_valid, FLOWSPEC_UNKNOWN); }
+
/* Next hop: For now, stored as adata */
extern struct ea_class ea_gen_nexthop;
struct nexthop_adata *nexthop_sort(struct nexthop_adata *x, linpool *lp);
int nexthop_is_sorted(struct nexthop_adata *x);
+ #define NEXTHOP_IS_REACHABLE(nhad) ((nhad)->ad.length > NEXTHOP_DEST_SIZE)
-static inline int rte_dest(rte *r)
+ /* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
+ static inline int rte_is_reachable(rte *r)
+ {
+ eattr *nhea = ea_find(r->attrs->eattrs, &ea_gen_nexthop);
+ if (!nhea)
+ return 0;
+
+ struct nexthop_adata *nhad = (void *) nhea->u.ptr;
+ return NEXTHOP_IS_REACHABLE(nhad);
+ }
+
+ static inline int nhea_dest(eattr *nhea)
+ {
+ if (!nhea)
+ return RTD_NONE;
+
+ struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
+ if (NEXTHOP_IS_REACHABLE(nhad))
+ return RTD_UNICAST;
+ else
+ return nhad->dest;
+ }
+
++static inline int rte_dest(const rte *r)
+ {
+ return nhea_dest(ea_find(r->attrs->eattrs, &ea_gen_nexthop));
+ }
void rta_init(void);
#define rta_size(...) (sizeof(rta))
[RTD_PROHIBIT] = "prohibited",
};
+ struct ea_class ea_gen_flowspec_valid = {
+ .name = "flowspec_valid",
+ .type = T_ENUM_FLOWSPEC_VALID,
+ .readonly = 1,
+ };
+
++const char * flowspec_valid_names[FLOWSPEC__MAX] = {
++ [FLOWSPEC_UNKNOWN] = "unknown",
++ [FLOWSPEC_VALID] = "",
++ [FLOWSPEC_INVALID] = "invalid",
++};
++
pool *rta_pool;
static slab *rta_slab;
rta *a = e->attrs;
int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
void (*get_route_info)(struct rte *, byte *buf);
-- eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
++ eattr *nhea = net_type_match(e->net, NB_DEST) ?
++ ea_find(a->eattrs, &ea_gen_nexthop) : NULL;
struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
- int dest = NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest;
++ int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
++ int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN;
tm_format_time(tm, &config->tf_route, e->lastmod);
ip_addr a_from = ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE);
if (d->last_table != d->tab)
rt_show_table(c, d);
- cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
- cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(dest),
-- e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
++ cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia,
++ net_is_flow(e->net) ? flowspec_valid_name(flowspec_valid) : rta_dest_name(dest),
++ e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
- if (a->dest == RTD_UNICAST)
+ if (dest == RTD_UNICAST)
NEXTHOP_WALK(nh, nhad)
{
char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
}
static void
-rte_trace(struct channel *c, rte *e, int dir, char *msg)
+rte_trace(const char *name, const rte *e, int dir, const char *msg)
{
- log(L_TRACE "%s.%s %c %s %N %uL %uG %s",
- c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr, e->src->private_id, e->src->global_id,
+ log(L_TRACE "%s %c %s %N %uL %uG %s",
+ name, dir, msg, e->net, e->src->private_id, e->src->global_id,
- rta_dest_name(e->attrs->dest));
+ rta_dest_name(rte_dest(e)));
}
static inline void
return 0;
}
- if (net_type_match(n, NB_DEST) == !e->attrs->dest)
- eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
- int dest = nhea_dest(nhea);
-
- if (net_type_match(n->n.addr, NB_DEST) == !dest)
++ if (net_type_match(n, NB_DEST))
{
- /* Exception for flowspec that failed validation */
- if (net_is_flow(n) && (e->attrs->dest == RTD_UNREACHABLE))
- return 1;
- log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
- n->n.addr, dest, e->sender->proto->name);
- return 0;
- }
++ eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
++ int dest = nhea_dest(nhea);
+
- log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
- n, e->attrs->dest, ch->proto->name);
- return 0;
- }
++ if (dest == RTD_NONE)
++ {
++ log(L_WARN "Ignoring route %N with no destination received via %s",
++ n, ch->proto->name);
++ return 0;
++ }
- eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
- if ((!nhea) != (e->attrs->dest != RTD_UNICAST))
- {
- log(L_WARN "Ignoring route %N with destination %d and %snexthop received via %s",
- n, e->attrs->dest, (nhea ? "" : "no "), ch->proto->name);
- return 0;
- if ((dest == RTD_UNICAST) &&
- !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
++ if ((dest == RTD_UNICAST) &&
++ !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
++ {
++ log(L_WARN "Ignoring unsorted multipath route %N received via %s",
++ n, ch->proto->name);
++ return 0;
++ }
+ }
-
- if ((e->attrs->dest == RTD_UNICAST) &&
- !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
++ else if (ea_find(e->attrs->eattrs, &ea_gen_nexthop))
{
-- log(L_WARN "Ignoring unsorted multipath route %N received via %s",
- n->n.addr, e->sender->proto->name);
++ log(L_WARN "Ignoring route %N having a nexthop attribute received via %s",
+ n, ch->proto->name);
return 0;
}
return (e && as_path_get_first_regular(e->u.ptr, &asn)) ? asn : 0;
}
--int
++static inline enum flowspec_valid
rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior)
{
ASSERT(rt_is_ip(tab_ip));
/* RFC 8955 6. a) Flowspec has defined dst prefix */
if (!net_flow_has_dst_prefix(n))
-- return 0;
++ return FLOWSPEC_INVALID;
/* RFC 9117 4.1. Accept AS_PATH is empty (fr */
if (interior && rta_as_path_is_empty(a))
-- return 1;
++ return FLOWSPEC_VALID;
/* RFC 8955 6. b) Flowspec and its best-match route have the same originator */
/* No best-match BGP route -> no flowspec */
if (!rb || (rt_get_source_attr(rb) != RTS_BGP))
-- return 0;
++ return FLOWSPEC_INVALID;
/* Find ORIGINATOR_ID values */
u32 orig_a = ea_get_int(a->eattrs, "bgp_originator_id", 0);
ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE),
ea_get_ip(rb->attrs->eattrs, &ea_gen_from, IPA_NONE)
)))
-- return 0;
++ return FLOWSPEC_INVALID;
/* Find ASN of the best-match route, for use in next checks */
u32 asn_b = rta_get_first_asn(rb->attrs);
if (!asn_b)
-- return 0;
++ return FLOWSPEC_INVALID;
/* RFC 9117 4.2. For EBGP, flowspec and its best-match route are from the same AS */
if (!interior && (rta_get_first_asn(a) != asn_b))
-- return 0;
++ return FLOWSPEC_INVALID;
/* RFC 8955 6. c) More-specific routes are from the same AS as the best-match route */
TRIE_WALK(tab_ip->trie, subnet, &dst)
if (!nc)
continue;
- rte *rc = nc->routes;
+ const rte *rc = &nc->routes->rte;
if (rt_get_source_attr(rc) != RTS_BGP)
-- return 0;
++ return FLOWSPEC_INVALID;
if (rta_get_first_asn(rc->attrs) != asn_b)
-- return 0;
++ return FLOWSPEC_INVALID;
}
TRIE_WALK_END;
-- return 1;
++ return FLOWSPEC_VALID;
}
#endif /* CONFIG_BGP */
if (!bc->base_table)
return NULL;
- const net_addr *n = r->net->n.addr;
-- struct bgp_proto *p = (void *) r->src->proto;
- int valid = rt_flowspec_check(bc->base_table, tab, n->n.addr, r->attrs, p->is_interior);
- int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
- int valid = rt_flowspec_check(bc->base_table, tab, n, r->attrs, p->is_interior);
- int old = rt_get_flowspec_valid(r);
++ struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, bc->c.proto);
++
++ enum flowspec_valid old = rt_get_flowspec_valid(r),
++ valid = rt_flowspec_check(bc->base_table, tab, n->n.addr, r->attrs, p->is_interior);
+
- if (dest == r->attrs->dest)
+ if (old == valid)
return NULL;
rta *a = alloca(RTA_MAX_SIZE);
- memcpy(a, r->attrs, rta_size(r->attrs));
+ *a = *r->attrs;
- a->dest = dest;
a->cached = 0;
- rte *new = sl_alloc(rte_slab);
- memcpy(new, r, sizeof(rte));
- new->attrs = rta_lookup(a);
+ ea_set_attr_u32(&a->eattrs, &ea_gen_flowspec_valid, 0, valid);
+
+ rte new;
+ memcpy(&new, r, sizeof(rte));
+ new.attrs = a;
- return new;
+ return rte_store(&new, n, tab);
#else
return NULL;
#endif
}
- if (rt_get_source_attr(r) != RTS_BGP)
- return;
-
+static inline void
+rt_flowspec_resolve_rte(rte *r, struct channel *c)
+{
+#ifdef CONFIG_BGP
- if (!bc->base_table)
- return;
++ enum flowspec_valid valid, old = rt_get_flowspec_valid(r);
+ struct bgp_channel *bc = (struct bgp_channel *) c;
- struct bgp_proto *p = (void *) r->src->proto;
- int valid = rt_flowspec_check(bc->base_table, c->in_req.hook->table, r->net, r->attrs, p->is_interior);
- int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
+
- if (dest == r->attrs->dest)
++ if ( (rt_get_source_attr(r) == RTS_BGP)
++ && (c->channel == &channel_bgp)
++ && (bc->base_table))
++ {
++ struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, bc->c.proto);
++ valid = rt_flowspec_check(
++ bc->base_table,
++ c->in_req.hook->table,
++ r->net, r->attrs, p->is_interior);
++ }
++ else
++ valid = FLOWSPEC_UNKNOWN;
+
- r->attrs->dest = dest;
++ if (valid == old)
+ return;
+
+ if (r->attrs->cached)
+ {
+ rta *a = tmp_alloc(RTA_MAX_SIZE);
+ *a = *r->attrs;
+ a->cached = 0;
+ r->attrs = a;
+ }
+
++ if (valid == FLOWSPEC_UNKNOWN)
++ ea_unset_attr(&r->attrs->eattrs, 0, &ea_gen_flowspec_valid);
++ else
++ ea_set_attr_u32(&r->attrs->eattrs, &ea_gen_flowspec_valid, 0, valid);
+#endif
+}
static inline int
rt_next_hop_update_net(rtable *tab, net *n)
direct++;
}
- }
he->src = rta_clone(a);
- he->dest = a->dest;
he->nexthop_linkable = !direct;
- he->igp_metric = rt_get_igp_metric(e);
+ he->igp_metric = rt_get_igp_metric(&e->rte);
}
done:
}
*/
--int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior);
--
--
/*
* Default protocol preferences
*/
}
};
- rta a0 = {
- .dest = RTD_UNICAST,
- .eattrs = &eattrs.l,
- };
+ rta a0 = { .eattrs = &eattrs.l, };
- rta *a = rta_lookup(&a0);
- rte *rte = rte_get_temp(a, p->p.main_source);
+ rte e0 = {
+ .attrs = &a0,
+ .src = p->p.main_source,
+ };
e->unreachable = 0;
- rte_update2(c, e->n.addr, rte, p->p.main_source);
+ rte_update(c, e->n.addr, &e0, p->p.main_source);
}
else if (e->valid && (e->router_id != p->router_id))
{
ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, 1);
ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_BABEL);
+ ea_set_dest(&a0.eattrs, 0, RTD_UNREACHABLE);
- rta *a = rta_lookup(&a0);
- rte *rte = rte_get_temp(a, p->p.main_source);
- rte->pflags = 0;
+ rte e0 = {
+ .attrs = &a0,
+ .src = p->p.main_source,
+ };
e->unreachable = 1;
- rte_update2(c, e->n.addr, rte, p->p.main_source);
+ rte_update(c, e->n.addr, &e0, p->p.main_source);
}
else
{
static int
-babel_preexport(struct proto *P, struct rte *new)
+babel_preexport(struct channel *c, struct rte *new)
{
- struct rta *a = new->attrs;
- if (new->src->proto != P)
++ if (new->src->proto != c->proto)
+ return 0;
+
/* Reject our own unreachable routes */
- if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == c->proto))
+ eattr *ea = ea_find(new->attrs->eattrs, &ea_gen_nexthop);
+ struct nexthop_adata *nhad = (void *) ea->u.ptr;
+ if (!NEXTHOP_IS_REACHABLE(nhad))
return -1;
return 0;
if (src == NULL)
return 0;
- /* Reject flowspec that failed or are pending validation */
- if (net_is_flow(e->net->n.addr))
+ /* Reject flowspec that failed validation */
- if ((e->attrs->dest == RTD_UNREACHABLE) && net_is_flow(e->net))
- return -1;
++ if (net_is_flow(e->net))
+ switch (rt_get_flowspec_valid(e))
+ {
+ case FLOWSPEC_VALID:
+ break;
+ case FLOWSPEC_INVALID:
+ return -1;
+ case FLOWSPEC_UNKNOWN:
- if ((rt_get_source_attr(e) == RTS_BGP) &&
- ((struct bgp_channel *) e->sender)->base_table)
- return -1;
++ ASSUME((rt_get_source_attr(e) != RTS_BGP) ||
++ !((struct bgp_channel *) SKIP_BACK(struct channel, in_req, e->sender->req))->base_table);
+ break;
++ case FLOWSPEC__MAX:
++ bug("This never happens.");
+ }
/* IBGP route reflection, RFC 4456 */
if (p->is_internal && src->is_internal && (p->local_as == src->local_as))
struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
static inline int
-rte_resolvable(rte *rt)
+rta_resolvable(rta *a)
{
- return a->dest != RTD_UNREACHABLE;
- eattr *nhea = ea_find(rt->attrs->eattrs, &ea_gen_nexthop);
++ eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
+ struct nexthop_adata *nhad = (void *) nhea->u.ptr;
+ return NEXTHOP_IS_REACHABLE(nhad) || (nhad->dest != RTD_UNREACHABLE);
}
uint tag = ea_get_int(a->eattrs, &ea_ospf_tag, 0);
ip_addr fwd = IPA_NONE;
- if (a->dest == RTD_UNICAST)
- {
- eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
- if (!nhea)
- {
- log(L_ERR "%s: Unicast route without nexthop for %N",
- p->p.name, n);
- return;
- }
-
- struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
+ eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
- if (nhea)
- {
- struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
++ struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
++ if (NEXTHOP_IS_REACHABLE(nhad))
if (use_gw_for_fwaddr(p, nhad->nh.gw, nhad->nh.iface))
fwd = nhad->nh.gw;
-- }
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (oa && ipa_zero(fwd))
switch (i->rtm_type)
{
case RTN_UNICAST:
- ra->dest = RTD_UNICAST;
-
if (a[RTA_MULTIPATH])
{
- struct nexthop_adata *nh = nl_parse_multipath(s, p, n, a[RTA_MULTIPATH], i->rtm_family, krt_src);
+ struct nexthop_adata *nh = nl_parse_multipath(s, p, net, a[RTA_MULTIPATH], i->rtm_family, krt_src);
if (!nh)
SKIP("strange RTA_MULTIPATH\n");