From: Maria Matejka Date: Fri, 29 Nov 2024 09:49:53 +0000 (+0100) Subject: Merge commit '38195ac6' into thread-merge-2.16 X-Git-Tag: v3.0.0~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1ad3e58c0bc3cabacaeef5ee17f887036430ec9;p=thirdparty%2Fbird.git Merge commit '38195ac6' into thread-merge-2.16 --- c1ad3e58c0bc3cabacaeef5ee17f887036430ec9 diff --cc filter/config.Y index 6bc421740,72996850d..144bbdef6 --- a/filter/config.Y +++ b/filter/config.Y @@@ -963,9 -949,9 +963,9 @@@ term | DELETE '(' term ',' term ')' { $$ = f_dispatch_method_x("delete", $3->type, $3, $5); } | FILTER '(' term ',' term ')' { $$ = f_dispatch_method_x("filter", $3->type, $3, $5); } - | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); } - | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); } + | ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); } + | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); } - | ASPA_CHECK '(' rtable ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $3); } + | ASPA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $3); } | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); } diff --cc filter/f-inst.c index 5bf96a87b,8529ad621..b1b0c3168 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@@ -1522,11 -1606,12 +1522,12 @@@ } - INST(FI_ASPA_CHECK_EXPLICIT, 1, 1) { /* ASPA Check */ + INST(FI_ASPA_CHECK_EXPLICIT, 2, 1) { /* ASPA Check */ NEVER_CONSTANT; ARG(1, T_PATH); - RTC(2); + ARG(2, T_BOOL); + RTC(3); - struct rtable *table = rtc->table; + rtable *table = rtc->table; if (!table) runtime("Missing ASPA table"); diff --cc nest/route.h index 648e46b46,c74c410f1..c64464461 --- a/nest/route.h +++ b/nest/route.h @@@ -929,12 -783,12 +929,15 @@@ void ea_show_nexthop_list(struct cli *c #define ROA_VALID 1 #define ROA_INVALID 2 - #define ASPA_UNKNOWN 0 - #define ASPA_VALID 1 - #define ASPA_INVALID 2 - #define ASPA_CONTAINS_CONFED 3 + enum aspa_result { + ASPA_UNKNOWN = 0, + ASPA_VALID, + ASPA_INVALID_EMPTY, + ASPA_INVALID_CONFED, + ASPA_INVALID_LEAK, + }; +int net_roa_check(rtable *tab, const net_addr *n, u32 asn); - int aspa_check(rtable *tab, const struct adata *path); ++enum aspa_result aspa_check(rtable *tab, const struct adata *path, bool force_upstream); + #endif diff --cc nest/rt-table.c index ac5e364ed,8cee48d69..d74dfad91 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@@ -724,10 -353,10 +724,10 @@@ net_roa_check(rtable *tp, const net_add * * 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)) @@@ -744,36 -377,21 +748,26 @@@ /* 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; + RT_READ(tab, tr); + - for (int ap=0; apt->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; @@@ -782,120 -401,158 +777,151 @@@ 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 */ diff --cc proto/bgp/config.Y index 85cbd3848,7ae6cde97..ab6b899c6 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@@ -354,35 -347,77 +354,58 @@@ bgp_channel_end 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, [], [[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, [], [[Request route refresh from neighbor]]) +{ + proto_apply_cmd($4, bgp_reload_in, 1, 0); +} + +CF_CLI(RELOAD BGP OUT, proto_patt, [], [[Refresh routes to neighbor]]) +{ + proto_apply_cmd($4, bgp_reload_out, 1, 0); +} + /* 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, }), - 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 = 1, }), + $3 + ); + } + CF_CODE CF_END