*
* Implements draft-ietf-sidrops-aspa-verification-16.
*/
- int aspa_check(rtable *tab, const adata *path)
+ enum aspa_result aspa_check(rtable *tab, const adata *path, bool force_upstream)
{
- struct lp_state lps;
- lp_save(tmp_linpool, &lps);
+ /* Restore tmp linpool state after this check */
+ CLEANUP(lp_saved_cleanup) struct lp_state *_lps = lp_save(tmp_linpool);
/* No support for confed paths */
if (as_path_contains_confed(path))
/* Find the provider blocks for every AS on the path
* and check allowed directions */
- bool *up = alloca(sizeof(bool) * nsz);
- bool *down = alloca(sizeof(bool) * nsz);
- bool unknown_flag = false;
+ uint max_up = 0, min_up = 0, max_down = 0, min_down = 0;
- for (int ap=0; ap<nsz; ap++)
+ RT_READ(tab, tr);
+
+ for (uint ap=0; ap<nsz; ap++)
{
net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
- bool seen = false;
- net *n = net_find(tab, &nau.n);
+
+ /* Find some ASPAs */
+ struct netindex *ni = net_find_index(tr->t->netindex, &nau.n);
+ net *n = ni ? net_find(tr, ni) : NULL;
- if (!n)
- {
- unknown_flag = up[ap] = down[ap] = true;
- continue;
- }
-
- up[ap] = down[ap] = false;
+ bool found = false, down = false, up = false;
- /* Walk the existing records */
- NET_READ_WALK_ROUTES(tr, n, ep, e)
- for (rte *e = (n ? n->routes: NULL); e; e = e->next)
++ if (n) NET_READ_WALK_ROUTES(tr, n, ep, e)
{
- if (!rte_is_valid(e))
+ if (!rte_is_valid(&e->rte))
continue;
- eattr *ea = ea_find(e->attrs->eattrs, EA_ASPA_PROVIDERS);
+ eattr *ea = ea_find(e->rte.attrs, &ea_gen_aspa_providers);
if (!ea)
continue;
for (uint i=0; i * sizeof(u32) < ea->u.ptr->length; i++)
{
if ((ap > 0) && ((u32 *) ea->u.ptr->data)[i] == asns[ap-1])
- down[ap] = true;
+ up = true;
if ((ap + 1 < nsz) && ((u32 *) ea->u.ptr->data)[i] == asns[ap+1])
- up[ap] = true;
+ down = true;
- if (down[ap] && up[ap])
- break;
+ if (down && up)
+ /* Both peers found */
+ goto end_of_aspa;
}
+ }
+ end_of_aspa:;
- if (down[ap] && up[ap])
- break;
+ /* Fast path for the upstream check */
+ if (force_upstream)
+ {
+ if (!found)
+ /* Move min-upstream */
+ min_up = ap;
+ else if (ap && !up)
+ /* Exists but doesn't allow this upstream */
+ return ASPA_INVALID_LEAK;
}
- /* No ASPA for this ASN, therefore UNKNOWN */
- if (!seen)
- unknown_flag = up[ap] = down[ap] = true;
- }
+ /* Fast path for no ASPA here */
+ else if (!found)
+ {
+ /* Extend max-downstream (min-downstream is stopped by unknown) */
+ max_down = ap+1;
- /* Check whether the topology is first ramp up and then ramp down. */
- int up_end = 0;
- while (up_end < nsz && up[up_end])
- up_end++;
+ /* Move min-upstream (can't include unknown) */
+ min_up = ap;
+ }
- int down_end = nsz - 1;
- while (down_end > 0 && down[down_end])
- down_end--;
+ /* ASPA exists and downstream may be extended */
+ else if (down)
+ {
+ /* Extending max-downstream always */
+ max_down = ap+1;
- /* A significant overlap of obvious unknowns or misconfigured ASPAs. */
- if (up_end - down_end >= 2)
- return ASPA_UNKNOWN;
+ /* Extending min-downstream unless unknown seen */
+ if (min_down == ap)
+ min_down = ap+1;
- /* The path has either a single transit provider, or a peering pair on top */
- else if (up_end - down_end >= 0)
- return unknown_flag ? ASPA_UNKNOWN : ASPA_VALID;
+ /* Downstream only */
+ if (!up)
+ min_up = max_up = ap;
+ }
- /* There is a gap between valid ramp up and valid ramp down */
- else
- return ASPA_INVALID;
+ /* No extension for downstream, force upstream only from now */
+ else
+ {
+ force_upstream = 1;
+
+ /* Not even upstream, move the ending here */
+ if (!up)
+ min_up = max_up = ap;
+ }
+ }
+
+ /* Is the path surely valid? */
+ if (min_up <= min_down)
+ return ASPA_VALID;
+
+ /* Is the path maybe valid? */
+ if (max_up <= max_down)
+ return ASPA_UNKNOWN;
+
+ /* Now there is surely a valley there. */
+ return ASPA_INVALID_LEAK;
}
-/**
- * rte_find - find a route
- * @net: network node
- * @src: route source
- *
- * The rte_find() function returns a route for destination @net
- * which is from route source @src.
- */
-rte *
-rte_find(net *net, struct rte_src *src)
+struct rte_storage *
+rte_store(const rte *r, struct netindex *i, struct rtable_private *tab)
{
- rte *e = net->routes;
+ struct rte_storage *s = sl_alloc(tab->rte_slab);
+ struct rte *e = RTES_WRITE(s);
+
+ *e = *r;
+ e->net = i->addr;
+ net_lock_index(tab->netindex, i);
+
+ rt_lock_source(e->src);
+
+ e->attrs = ea_lookup(e->attrs, BIT32_ALL(EALS_PREIMPORT, EALS_FILTERED), EALS_IN_TABLE);
+
+#if 0
+ debug("(store) %N ", i->addr);
+ ea_dump(e->attrs);
+ debug("\n");
+#endif
- while (e && e->src != src)
- e = e->next;
- return e;
+ return s;
}
+static void rte_free_deferred(struct deferred_call *dc);
+
+struct rte_free_deferred_item {
+ struct deferred_call dc;
+ struct rte_storage *e;
+ rtable *tab;
+};
+
/**
- * rte_get_temp - get a temporary &rte
- * @a: attributes to assign to the new route (a &rta; in case it's
- * un-cached, rte_update() will create a cached copy automatically)
- * @src: route source
+ * rte_free_defer - delete a &rte (happens later)
+ * @e: &struct rte_storage to be deleted
+ * @tab: the table which the rte belongs to
*
- * Create a temporary &rte and bind it with the attributes @a.
+ * rte_free() deletes the given &rte from the routing table it's linked to.
*/
-rte *
-rte_get_temp(rta *a, struct rte_src *src)
+
+static void
+rte_free(struct rte_storage *e, struct rtable_private *tab)
{
- rte *e = sl_alloc(rte_slab);
+ struct rte_free_deferred_item rfdi = {
+ .dc.hook = rte_free_deferred,
+ .e = e,
+ .tab = RT_PUB(tab),
+ };
- e->attrs = a;
- e->id = 0;
- e->flags = 0;
- e->pflags = 0;
- rt_lock_source(e->src = src);
- return e;
+ if (!tab->rte_free_deferred++)
+ rt_lock_table(tab);
+
+ rt_rte_trace_in(D_ROUTES, e->rte.sender->req, &e->rte, "freeing");
+ defer_call(&rfdi.dc, sizeof rfdi);
}
-rte *
-rte_do_cow(rte *r)
+static void
+rte_free_deferred(struct deferred_call *dc)
{
- rte *e = sl_alloc(rte_slab);
+ SKIP_BACK_DECLARE(struct rte_free_deferred_item, rfdi, dc, dc);
- memcpy(e, r, sizeof(rte));
+ struct rte_storage *e = rfdi->e;
+ RT_LOCK(rfdi->tab, tab);
- rt_lock_source(e->src);
- e->attrs = rta_clone(r->attrs);
- e->flags = 0;
- return e;
-}
+ /* No need for synchronize_rcu, implied by the deferred_call */
-/**
- * rte_cow_rta - get a private writable copy of &rte with writable &rta
- * @r: a route entry to be copied
- * @lp: a linpool from which to allocate &rta
- *
- * rte_cow_rta() takes a &rte and prepares it and associated &rta for
- * modification. There are three possibilities: First, both &rte and &rta are
- * private copies, in that case they are returned unchanged. Second, &rte is
- * private copy, but &rta is cached, in that case &rta is duplicated using
- * rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case
- * both structures are duplicated by rte_do_cow() and rta_do_cow().
- *
- * Note that in the second case, cached &rta loses one reference, while private
- * copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs,
- * nexthops, ...) with it. To work properly, original shared &rta should have
- * another reference during the life of created private copy.
- *
- * Result: a pointer to the new writable &rte with writable &rta.
- */
-rte *
-rte_cow_rta(rte *r, linpool *lp)
-{
- if (!rta_is_cached(r->attrs))
- return r;
+ struct netindex *i = RTE_GET_NETINDEX(&e->rte);
+ net_unlock_index(tab->netindex, i);
- r = rte_cow(r);
- rta *a = rta_do_cow(r->attrs, lp);
- rta_free(r->attrs);
- r->attrs = a;
- return r;
+ rt_unlock_source(e->rte.src);
+
+ ea_free(e->rte.attrs);
+ sl_free(e);
+
+ if (!--tab->rte_free_deferred)
+ rt_unlock_table(tab);
}
static int /* Actually better or at least as good as */
bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end;
-
-dynamic_attr: BGP_ORIGIN
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); $$.flags = BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_PATH
- { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); $$.flags = BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_NEXT_HOP
- { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); $$.flags = BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_MED
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); $$.flags = BAF_OPTIONAL; } ;
-dynamic_attr: BGP_LOCAL_PREF
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); $$.flags = BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_ATOMIC_AGGR
- { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); $$.flags = BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_AGGREGATOR
- { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_COMMUNITY
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_ORIGINATOR_ID
- { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); $$.flags = BAF_OPTIONAL; } ;
-dynamic_attr: BGP_CLUSTER_LIST
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); $$.flags = BAF_OPTIONAL; } ;
-dynamic_attr: BGP_EXT_COMMUNITY
- { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_AIGP
- { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); $$.flags = BAF_OPTIONAL; } ;
-dynamic_attr: BGP_LARGE_COMMUNITY
- { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
-dynamic_attr: BGP_OTC
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
-
-custom_attr: ATTRIBUTE BGP expr type symbol ';' {
+custom_attr: ATTRIBUTE BGP NUM type symbol ';' {
if ($3 > 255 || $3 < 1)
- cf_error("Invalid attribute number (Given %i, must be 1-255)", $3);
- if ($4 != T_BYTESTRING)
- cf_error("Attribute type must be bytestring, not %s", f_type_name($4));
- if (bgp_attr_name($3))
- cf_error("Attribute BGP.%d already known as %s", $3, bgp_attr_name($3));
-
- struct f_dynamic_attr *a = cfg_alloc(sizeof(struct f_dynamic_attr));
- *a = f_new_dynamic_attr(f_type_attr($4), T_BYTESTRING, EA_CODE(PROTOCOL_BGP, $3));
- a->flags = BAF_TRANSITIVE | BAF_OPTIONAL;
- cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, a);
+ cf_error("Invalid attribute number. (Given %i, must be 1-255.)", $3);
+
+ struct ea_class *ac = bgp_find_ea_class_by_id($3);
+ ASSERT_DIE(ac);
+ if ($4 != ac->type)
+ cf_error("Attribute %d type must be %s, not %s.", $3, f_type_name(ac->type), f_type_name($4));
+
+ ea_ref_class(new_config->pool, ac);
+ cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, ac);
};
-CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
+CF_CLI(RELOAD BGP, proto_patt, [<name>], [[Send and request route refresh to/from neighbor]])
+{
+ proto_apply_cmd($3, bgp_reload_in, 1, 0);
+ proto_apply_cmd($3, bgp_reload_out, 1, 0);
+};
+
+CF_CLI(RELOAD BGP IN, proto_patt, [<name>], [[Request route refresh from neighbor]])
+{
+ proto_apply_cmd($4, bgp_reload_in, 1, 0);
+}
+
+CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
+{
+ proto_apply_cmd($4, bgp_reload_out, 1, 0);
+}
- f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH,
- EA_CODE(PROTOCOL_BGP, BA_AS_PATH))
+ /* ASPA shortcuts */
+ term: ASPA_CHECK '(' rtable ')' { $$ =
+ f_new_inst(FI_ASPA_CHECK_EXPLICIT,
+ f_new_inst(FI_EA_GET,
+ f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
- f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH,
- EA_CODE(PROTOCOL_BGP, BA_AS_PATH))
- ),
++ ea_class_find_by_name("bgp_path")
+ ),
+ f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }),
+ $3
+ );
+ }
+
+ term: ASPA_CHECK_CUSTOMER '(' rtable ')' { $$ =
+ f_new_inst(FI_ASPA_CHECK_EXPLICIT,
+ f_new_inst(FI_EA_GET,
+ f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
++ ea_class_find_by_name("bgp_path")
++ ),
+ f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }),
+ $3
+ );
+ }
+
CF_CODE
CF_END