From: Maria Matejka Date: Wed, 9 Mar 2022 12:13:05 +0000 (+0100) Subject: Merge commit '1b9189d5' into haugesund X-Git-Tag: v3.0-alpha1~171^2~82 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=92b832380d31fc9995d6e45b3db4ce496fcb7837;p=thirdparty%2Fbird.git Merge commit '1b9189d5' into haugesund --- 92b832380d31fc9995d6e45b3db4ce496fcb7837 diff --cc nest/route.h index 595acabd9,4944a854a..c7fb67aa2 --- a/nest/route.h +++ b/nest/route.h @@@ -161,8 -157,8 +161,9 @@@ typedef struct rtable resource r; node n; /* Node in list of all tables */ pool *rp; /* Resource pool to allocate everything from, including itself */ + struct slab *rte_slab; /* Slab to allocate route objects */ struct fib fib; + struct f_trie *trie; /* Trie of prefixes defined in fib */ char *name; /* Name of this table */ list channels; /* List of attached channels (struct channel) */ uint addr_type; /* Type of address data stored in table (NET_*) */ @@@ -309,17 -330,12 +353,13 @@@ static inline void rt_shutdown(rtable * static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); } static inline net *net_find_valid(rtable *tab, const net_addr *addr) - { net *n = net_find(tab, addr); return (n && rte_is_valid(n->routes)) ? n : NULL; } + { net *n = net_find(tab, addr); return (n && n->routes && rte_is_valid(&n->routes->rte)) ? n : NULL; } static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); } -void *net_route(rtable *tab, const net_addr *n); +net *net_get(rtable *tab, const net_addr *addr); +net *net_route(rtable *tab, const net_addr *n); int net_roa_check(rtable *tab, const net_addr *n, u32 asn); - rte *rte_find(net *net, struct rte_src *src); - rte *rte_get_temp(struct rta *, struct rte_src *src); - void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); - /* rte_update() moved to protocol.h to avoid dependency conflicts */ - int rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter); - rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent); + int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter); + rte *rt_export_merged(struct channel *c, net *net, linpool *pool, int silent); void rt_refresh_begin(rtable *t, struct channel *c); void rt_refresh_end(rtable *t, struct channel *c); void rt_modify_stale(rtable *t, struct channel *c); @@@ -336,22 -350,9 +374,21 @@@ void rt_feed_channel_abort(struct chann int rt_reload_channel(struct channel *c); void rt_reload_channel_abort(struct channel *c); void rt_prune_sync(rtable *t, int all); - int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old, rte **old_exported, int refeed); + int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old, struct rte_storage **old_exported, int refeed); struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); +static inline int rt_is_ip(rtable *tab) +{ return (tab->addr_type == NET_IP4) || (tab->addr_type == NET_IP6); } + +static inline int rt_is_vpn(rtable *tab) +{ return (tab->addr_type == NET_VPN4) || (tab->addr_type == NET_VPN6); } + +static inline int rt_is_roa(rtable *tab) +{ return (tab->addr_type == NET_ROA4) || (tab->addr_type == NET_ROA6); } + +static inline int rt_is_flow(rtable *tab) +{ return (tab->addr_type == NET_FLOW4) || (tab->addr_type == NET_FLOW6); } + /* Default limit for ECMP next hops, defined in sysdep code */ extern const int rt_default_ecmp; diff --cc nest/rt-show.c index 198779665,ae5000f51..7b05c64ed --- a/nest/rt-show.c +++ b/nest/rt-show.c @@@ -111,12 -109,13 +110,12 @@@ rt_show_net(struct cli *c, net *n, stru ASSUME(!d->export_mode || ec); int first = 1; + int first_show = 1; int pass = 0; - for (e = n->routes; e; e = e->next) - bsnprintf(ia, sizeof(ia), "%N", n->n.addr); - + for (struct rte_storage *er = n->routes; er; er = er->next) { - if (rte_is_filtered(e) != d->filtered) + if (rte_is_filtered(&er->rte) != d->filtered) continue; d->rt_counter++; @@@ -186,24 -185,12 +185,19 @@@ goto skip; if (d->stats < 2) + { + if (first_show) + net_format(n->n.addr, ia, sizeof(ia)); + else + ia[0] = 0; + - rt_show_rte(c, ia, e, d, (e->net->routes == ee)); + rt_show_rte(c, ia, &e, d, (n->routes == er)); + first_show = 0; + } d->show_counter++; - ia[0] = 0; skip: - if (e != ee) - { - rte_free(e); - e = ee; - } lp_flush(c->show_pool); if (d->primary_only) diff --cc nest/rt-table.c index b3ca3d050,c49400b7c..9a9d57ad3 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@@ -1176,14 -798,10 +1089,14 @@@ rte_validate(rte *e return 0; } - if (net_type_match(n->n.addr, NB_DEST) == !e->attrs->dest) + if (net_type_match(n, NB_DEST) == !e->attrs->dest) { + /* Exception for flowspec that failed validation */ - if (net_is_flow(n->n.addr) && (e->attrs->dest == RTD_UNREACHABLE)) ++ 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, e->attrs->dest, e->sender->proto->name); + n, e->attrs->dest, e->sender->proto->name); return 0; } @@@ -2209,29 -1655,34 +2056,27 @@@ rt_prune_table(rtable *tab again: FIB_ITERATE_START(&tab->fib, fit, net, n) { - rte *e; - rescan: + if (limit <= 0) + { + FIB_ITERATE_PUT(fit); + ev_schedule(tab->rt_event); + return; + } + - for (e=n->routes; e; e=e->next) + for (struct rte_storage *e=n->routes; e; e=e->next) { - if (e->sender->flush_active || (e->flags & REF_DISCARD)) + if (e->rte.sender->flush_active || (e->rte.flags & REF_DISCARD)) { - rte_discard(e); - if (limit <= 0) - { - FIB_ITERATE_PUT(fit); - ev_schedule(tab->rt_event); - return; - } - + rte_discard(n, &e->rte); limit--; goto rescan; } - if (e->flags & REF_MODIFY) + if (e->rte.flags & REF_MODIFY) { - rte_modify(e); - if (limit <= 0) - { - FIB_ITERATE_PUT(fit); - ev_schedule(tab->rt_event); - return; - } - + rte_modify(n, &e->rte); limit--; goto rescan; @@@ -2483,27 -1846,9 +2328,27 @@@ no_nexthop } } +static inline int +rta_next_hop_outdated(rta *a) +{ + struct hostentry *he = a->hostentry; + + if (!he) + return 0; + + if (!he->src) + return a->dest != RTD_UNREACHABLE; + + return (a->dest != he->dest) || (a->igp_metric != he->igp_metric) || + (!he->nexthop_linkable) || !nexthop_same(&(a->nh), &(he->src->nh)); +} + - static inline rte * - rt_next_hop_update_rte(rtable *tab UNUSED, rte *old) + static inline struct rte_storage * + rt_next_hop_update_rte(rtable *tab, net *n, rte *old) { + if (!rta_next_hop_outdated(old->attrs)) + return NULL; + rta *a = alloca(RTA_MAX_SIZE); memcpy(a, old->attrs, rta_size(old->attrs)); @@@ -2513,164 -1858,12 +2358,161 @@@ rta_apply_hostentry(a, old->attrs->hostentry, &mls); a->cached = 0; - rte *e = sl_alloc(rte_slab); - memcpy(e, old, sizeof(rte)); - e->attrs = rta_lookup(a); - rt_lock_source(e->src); + rte e0 = *old; + e0.attrs = a; - return e; + return rte_store(&e0, n, tab); } + +#ifdef CONFIG_BGP + +static inline int +net_flow_has_dst_prefix(const net_addr *n) +{ + ASSUME(net_is_flow(n)); + + if (n->pxlen) + return 1; + + if (n->type == NET_FLOW4) + { + const net_addr_flow4 *n4 = (void *) n; + return (n4->length > sizeof(net_addr_flow4)) && (n4->data[0] == FLOW_TYPE_DST_PREFIX); + } + else + { + const net_addr_flow6 *n6 = (void *) n; + return (n6->length > sizeof(net_addr_flow6)) && (n6->data[0] == FLOW_TYPE_DST_PREFIX); + } +} + +static inline int +rta_as_path_is_empty(rta *a) +{ + eattr *e = ea_find(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); + return !e || (as_path_getlen(e->u.ptr) == 0); +} + +static inline u32 +rta_get_first_asn(rta *a) +{ + eattr *e = ea_find(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); + u32 asn; + + return (e && as_path_get_first_regular(e->u.ptr, &asn)) ? asn : 0; +} + +int +rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior) +{ + ASSERT(rt_is_ip(tab_ip)); + ASSERT(rt_is_flow(tab_flow)); + ASSERT(tab_ip->trie); + + /* RFC 8955 6. a) Flowspec has defined dst prefix */ + if (!net_flow_has_dst_prefix(n)) + return 0; + + /* RFC 9117 4.1. Accept AS_PATH is empty (fr */ + if (interior && rta_as_path_is_empty(a)) + return 1; + + + /* RFC 8955 6. b) Flowspec and its best-match route have the same originator */ + + /* Find flowspec dst prefix */ + net_addr dst; + if (n->type == NET_FLOW4) + net_fill_ip4(&dst, net4_prefix(n), net4_pxlen(n)); + else + net_fill_ip6(&dst, net6_prefix(n), net6_pxlen(n)); + + /* Find best-match BGP unicast route for flowspec dst prefix */ + net *nb = net_route(tab_ip, &dst); - rte *rb = nb ? nb->routes : NULL; ++ const rte *rb = nb ? &nb->routes->rte : NULL; + + /* Register prefix to trie for tracking further changes */ + int max_pxlen = (n->type == NET_FLOW4) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH; + trie_add_prefix(tab_flow->flowspec_trie, &dst, (nb ? nb->n.addr->pxlen : 0), max_pxlen); + + /* No best-match BGP route -> no flowspec */ + if (!rb || (rb->attrs->source != RTS_BGP)) + return 0; + + /* Find ORIGINATOR_ID values */ + u32 orig_a = ea_get_int(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID), 0); + u32 orig_b = ea_get_int(rb->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID), 0); + + /* Originator is either ORIGINATOR_ID (if present), or BGP neighbor address (if not) */ + if ((orig_a != orig_b) || (!orig_a && !orig_b && !ipa_equal(a->from, rb->attrs->from))) + return 0; + + + /* 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; + + /* 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; + + /* RFC 8955 6. c) More-specific routes are from the same AS as the best-match route */ + TRIE_WALK(tab_ip->trie, subnet, &dst) + { + net *nc = net_find_valid(tab_ip, &subnet); + if (!nc) + continue; + - rte *rc = nc->routes; ++ const rte *rc = &nc->routes->rte; + if (rc->attrs->source != RTS_BGP) + return 0; + + if (rta_get_first_asn(rc->attrs) != asn_b) + return 0; + } + TRIE_WALK_END; + + return 1; +} + +#endif /* CONFIG_BGP */ + - static rte * - rt_flowspec_update_rte(rtable *tab, rte *r) ++static struct rte_storage * ++rt_flowspec_update_rte(rtable *tab, net *n, rte *r) +{ +#ifdef CONFIG_BGP + if (r->attrs->source != RTS_BGP) + return NULL; + + struct bgp_channel *bc = (struct bgp_channel *) r->sender; + 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, r->attrs, p->is_interior); ++ int valid = rt_flowspec_check(bc->base_table, tab, n->n.addr, r->attrs, p->is_interior); + int dest = valid ? RTD_NONE : RTD_UNREACHABLE; + + if (dest == r->attrs->dest) + return NULL; + + rta *a = alloca(RTA_MAX_SIZE); + memcpy(a, r->attrs, rta_size(r->attrs)); + a->dest = dest; + a->cached = 0; + - rte *new = sl_alloc(rte_slab); - memcpy(new, r, sizeof(rte)); - new->attrs = rta_lookup(a); ++ rte new; ++ memcpy(&new, r, sizeof(rte)); ++ new.attrs = a; + - return new; ++ return rte_store(&new, n, tab); +#else + return NULL; +#endif +} + + static inline int rt_next_hop_update_net(rtable *tab, net *n) { @@@ -2683,17 -1876,13 +2525,18 @@@ return 0; for (k = &n->routes; e = *k; k = &e->next) - if (rta_next_hop_outdated(e->rte.attrs)) + { + if (!net_is_flow(n->n.addr)) - new = rt_next_hop_update_rte(tab, e); ++ new = rt_next_hop_update_rte(tab, n, &e->rte); + else - new = rt_flowspec_update_rte(tab, e); ++ new = rt_flowspec_update_rte(tab, n, &e->rte); + + if (new) { - new = rt_next_hop_update_rte(tab, n, &e->rte); + new->next = e->next; *k = new; - rte_trace_in(D_ROUTES, new->sender, new, "updated"); + rte_trace_in(D_ROUTES, new->rte.sender, &new->rte, "updated"); rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL); /* Call a pre-comparison hook */ diff --cc proto/bgp/attrs.c index 65e87c968,1d9b36304..1b36368fa --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@@ -1689,10 -1682,6 +1689,10 @@@ bgp_preexport(struct channel *c, rte *e if (src == NULL) return 0; + /* Reject flowspec that failed validation */ - if ((e->attrs->dest == RTD_UNREACHABLE) && net_is_flow(e->net->n.addr)) - return -1; ++ if ((e->attrs->dest == RTD_UNREACHABLE) && net_is_flow(e->net)) ++ return -1; + /* IBGP route reflection, RFC 4456 */ if (p->is_internal && src->is_internal && (p->local_as == src->local_as)) { @@@ -1860,11 -1849,7 +1860,11 @@@ bgp_rt_notify(struct proto *P, struct c if (new) { - struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, bgp_linpool2); + struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, tmp_linpool); + + /* Error during attribute processing */ + if (!attrs) - log(L_ERR "%s: Invalid route %N withdrawn", p->p.name, n->n.addr); ++ log(L_ERR "%s: Invalid route %N withdrawn", p->p.name, n); /* If attributes are invalid, we fail back to withdraw */ buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c); diff --cc proto/bgp/bgp.h index bff49c3a4,c79dd1b24..655b26360 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@@ -518,9 -517,9 +518,9 @@@ struct rte_source *bgp_find_source(stru 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 rt->attrs->dest != RTD_UNREACHABLE; - return a->dest == RTD_UNICAST; ++ return a->dest != RTD_UNREACHABLE; } diff --cc proto/bgp/packets.c index 66f14150b,647551e5f..7be346776 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@@ -1371,12 -1348,8 +1371,12 @@@ bgp_rte_update(struct bgp_parse_state * if (!a0) { + /* Route update was changed to withdraw */ + if (s->err_withdraw && s->reach_nlri_step) + REPORT("Invalid route %N withdrawn", n); + /* Route withdraw */ - rte_update3(&s->channel->c, n, NULL, s->last_src); + rte_update(&s->channel->c, n, NULL, s->last_src); return; } diff --cc sysdep/linux/netlink.c index e103c8ef1,bff2d579f..35ba116ce --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@@ -1778,9 -1681,12 +1777,9 @@@ nl_parse_route(struct nl_parse_state *s if (a[RTA_MULTIPATH]) { - struct nexthop *nh = nl_parse_multipath(s, p, n, a[RTA_MULTIPATH], i->rtm_family, krt_src); - struct nexthop *nh = nl_parse_multipath(s, p, a[RTA_MULTIPATH], i->rtm_family); ++ struct nexthop *nh = nl_parse_multipath(s, p, net, a[RTA_MULTIPATH], i->rtm_family, krt_src); if (!nh) - { - log(L_ERR "KRT: Received strange multipath route %N", net); - return; - } + SKIP("strange RTA_MULTIPATH\n"); nexthop_link(ra, nh); break;