diff options
-rw-r--r-- | filter/f-inst.c | 79 | ||||
-rw-r--r-- | lib/route.h | 67 | ||||
-rw-r--r-- | lib/type.h | 1 | ||||
-rw-r--r-- | nest/config.Y | 2 | ||||
-rw-r--r-- | nest/rt-attr.c | 30 | ||||
-rw-r--r-- | nest/rt-dev.c | 5 | ||||
-rw-r--r-- | nest/rt-show.c | 12 | ||||
-rw-r--r-- | nest/rt-table.c | 159 | ||||
-rw-r--r-- | nest/rt.h | 4 | ||||
-rw-r--r-- | proto/babel/babel.c | 18 | ||||
-rw-r--r-- | proto/bgp/attrs.c | 16 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 4 | ||||
-rw-r--r-- | proto/bgp/packets.c | 11 | ||||
-rw-r--r-- | proto/ospf/rt.c | 4 | ||||
-rw-r--r-- | proto/ospf/topology.c | 15 | ||||
-rw-r--r-- | proto/perf/perf.c | 4 | ||||
-rw-r--r-- | proto/rip/rip.c | 4 | ||||
-rw-r--r-- | proto/rpki/rpki.c | 4 | ||||
-rw-r--r-- | proto/static/static.c | 6 | ||||
-rw-r--r-- | sysdep/linux/netlink.c | 78 | ||||
-rw-r--r-- | sysdep/unix/krt.c | 9 |
21 files changed, 297 insertions, 235 deletions
diff --git a/filter/f-inst.c b/filter/f-inst.c index b276387f..1690c9f6 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -530,20 +530,24 @@ STATIC_ATTR; ACCESS_RTE; ACCESS_EATTRS; - struct rta *rta = fs->rte->attrs; switch (sa.sa_code) { 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) { + case SA_DEST: + RESULT(sa.type, i, nhad ? + (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) + : RTD_NONE); + break; case SA_GW: RESULT(sa.type, ip, nh ? nh->gw : IPA_NONE); break; @@ -576,36 +580,33 @@ f_rta_cow(fs); { - struct rta *rta = fs->rte->attrs; + union { + struct nexthop_adata nha; + struct { + struct adata ad; + struct nexthop nh; + u32 label; + }; + } nha; + + nha.ad = (struct adata) { + .length = sizeof (struct nexthop_adata) - sizeof (struct adata), + }; - if (sa.sa_code == SA_DEST) + eattr *a = NULL; + + switch (sa.sa_code) + { + case SA_DEST: { int i = v1.val.i; if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); - rta->dest = i; - ea_unset_attr(fs->eattrs, 1, &ea_gen_nexthop); + nha.nha.dest = i; + nha.ad.length = NEXTHOP_DEST_SIZE; + break; } - else - { - union { - struct nexthop_adata nha; - struct { - struct adata ad; - struct nexthop nh; - u32 label; - }; - } nha; - - nha.ad = (struct adata) { - .length = sizeof (struct nexthop_adata) - sizeof (struct adata), - }; - - eattr *a = NULL; - - switch (sa.sa_code) - { case SA_GW: { struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop); @@ -618,7 +619,6 @@ if (!n || (n->scope == SCOPE_HOST)) runtime( "Invalid gw address" ); - rta->dest = RTD_UNICAST; nha.nh = (struct nexthop) { .gw = ip, .iface = n->iface, @@ -632,7 +632,6 @@ if (!ifa) runtime( "Invalid iface name" ); - rta->dest = RTD_UNICAST; nha.nh = (struct nexthop) { .iface = ifa, }; @@ -666,15 +665,16 @@ int i = v1.val.i; if (i < 1 || i > 256) runtime( "Setting weight value out of bounds" ); - if (rta->dest != RTD_UNICAST) - runtime( "Setting weight needs regular nexthop " ); struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop); if (!nh_ea) runtime( "No nexthop to set weight on" ); - struct nexthop_adata *nhax = (struct nexthop_adata *) - tmp_copy_adata(&((struct nexthop_adata *) nh_ea->u.ptr)->ad); + struct nexthop_adata *nhad = (struct nexthop_adata *) nh_ea->u.ptr; + if (!NEXTHOP_IS_REACHABLE(nhad)) + runtime( "Setting weight needs regular nexthop " ); + + struct nexthop_adata *nhax = (struct nexthop_adata *) tmp_copy_adata(&nhad->ad); /* Set weight on all next hops */ NEXTHOP_WALK(nh, nhax) @@ -687,15 +687,14 @@ default: bug("Invalid static attribute access (%u/%u)", sa.type, sa.sa_code); - } + } - if (!a) - a = ea_set_attr(fs->eattrs, - EA_LITERAL_DIRECT_ADATA(&ea_gen_nexthop, 0, tmp_copy_adata(&nha.ad))); + if (!a) + a = ea_set_attr(fs->eattrs, + EA_LITERAL_DIRECT_ADATA(&ea_gen_nexthop, 0, tmp_copy_adata(&nha.ad))); - a->originated = 1; - a->fresh = 1; - } + a->originated = 1; + a->fresh = 1; } } diff --git a/lib/route.h b/lib/route.h index 613df0c3..283a3760 100644 --- a/lib/route.h +++ b/lib/route.h @@ -78,9 +78,17 @@ struct nexthop { /* For packing one into eattrs */ struct nexthop_adata { struct adata ad; - struct nexthop nh; + /* There is either a set of nexthops or a special destination (RTD_*) */ + union { + struct nexthop nh; + uint dest; + }; }; +#define NEXTHOP_DEST_SIZE (OFFSETOF(struct nexthop_adata, dest) + sizeof(uint) - OFFSETOF(struct adata, data)) +#define NEXTHOP_DEST_LITERAL(x) ((struct nexthop_adata) { \ + .ad.length = NEXTHOP_DEST_SIZE, .dest = (x), }) + #define RNF_ONLINK 0x1 /* Gateway is onlink regardless of IP ranges */ @@ -90,7 +98,6 @@ typedef struct rta { u32 hash_key; /* Hash over important fields */ struct ea_list *eattrs; /* Extended Attribute chain */ u16 cached:1; /* Are attributes cached? */ - u16 dest:4; /* Route destination type (RTD_...) */ } rta; #define RTS_STATIC 1 /* Normal static route */ @@ -111,7 +118,7 @@ typedef struct rta { #define RTS_MAX 16 #define RTD_NONE 0 /* Undefined next hop */ -#define RTD_UNICAST 1 /* Next hop is neighbor router */ +#define RTD_UNICAST 1 /* A standard next hop */ #define RTD_BLACKHOLE 2 /* Silently drop packets */ #define RTD_UNREACHABLE 3 /* Reject as unreachable */ #define RTD_PROHIBIT 4 /* Administratively prohibited */ @@ -122,10 +129,6 @@ extern const char * rta_dest_names[RTD_MAX]; static inline const char *rta_dest_name(uint n) { return (n < RTD_MAX) ? rta_dest_names[n] : "???"; } -/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */ -static inline int rte_is_reachable(rte *r) -{ return r->attrs->dest == RTD_UNICAST; } - /* * Extended Route Attributes @@ -333,9 +336,31 @@ extern struct ea_class ea_gen_source; static inline u32 rt_get_source_attr(const rte *rt) { return ea_get_int(rt->attrs->eattrs, &ea_gen_source, 0); } +/* Flowspec validation result */ +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; +static inline void ea_set_dest(struct ea_list **to, uint flags, uint dest) +{ + struct nexthop_adata nhad = NEXTHOP_DEST_LITERAL(dest); + ea_set_attr_data(to, &ea_gen_nexthop, flags, &nhad.ad.data, nhad.ad.length); +} + /* Next hop structures */ #define NEXTHOP_ALIGNMENT (_Alignof(struct nexthop)) @@ -361,7 +386,35 @@ struct nexthop_adata *nexthop_merge(struct nexthop_adata *x, struct nexthop_adat 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) +/* 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)) @@ -66,6 +66,7 @@ enum btype { T_ENUM_BGP_ORIGIN = 0x11, /* BGP Origin enum */ T_ENUM_RA_PREFERENCE = 0x13, /* RA Preference enum */ + T_ENUM_FLOWSPEC_VALID = 0x15, /* Flowspec validation result */ #define EAF_TYPE__MAX 0x1f #define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ diff --git a/nest/config.Y b/nest/config.Y index 068a1d30..7c68a09a 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -133,7 +133,7 @@ CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) -CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT) +CF_ENUM(T_ENUM_RTD, RTD_, BLACKHOLE, UNREACHABLE, PROHIBIT) CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) diff --git a/nest/rt-attr.c b/nest/rt-attr.c index cf3ab659..f548a575 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -166,6 +166,18 @@ const char * rta_dest_names[RTD_MAX] = { [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; @@ -1246,20 +1258,13 @@ rta_alloc_hash(void) static inline uint rta_hash(rta *a) { - u64 h; - mem_hash_init(&h); -#define BMIX(f) mem_hash_mix_num(&h, a->f); - BMIX(dest); -#undef MIX - - return mem_hash_value(&h) ^ ea_hash(a->eattrs); + return ea_hash(a->eattrs); } static inline int rta_same(rta *x, rta *y) { - return (x->dest == y->dest && - ea_same(x->eattrs, y->eattrs)); + return ea_same(x->eattrs, y->eattrs); } static rta * @@ -1382,10 +1387,8 @@ rta_do_cow(rta *o, linpool *lp) void rta_dump(rta *a) { - static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" }; - - debug("uc=%d %s h=%04x", - a->uc, rtd[a->dest], a->hash_key); + debug("uc=%d h=%04x", + a->uc, a->hash_key); if (!a->cached) debug(" !CACHED"); if (a->eattrs) @@ -1449,6 +1452,7 @@ rta_init(void) ea_register_init(&ea_gen_source); ea_register_init(&ea_gen_nexthop); ea_register_init(&ea_gen_hostentry); + ea_register_init(&ea_gen_flowspec_valid); } /* diff --git a/nest/rt-dev.c b/nest/rt-dev.c index 107f67a7..fa224f9a 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -79,10 +79,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) /* Use iface ID as local source ID */ struct rte_src *src = rt_get_source(P, ad->iface->index); - rta a0 = { - .dest = RTD_UNICAST, - }; - + rta a0 = {}; struct nexthop_adata nhad = { .nh = { .iface = ad->iface, }, .ad = { .length = (void *) NEXTHOP_NEXT(&nhad.nh) - (void *) nhad.ad.data, }, diff --git a/nest/rt-show.c b/nest/rt-show.c index 6cfd99fc..cc5a9a10 100644 --- a/nest/rt-show.c +++ b/nest/rt-show.c @@ -45,8 +45,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary 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 = 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); @@ -68,10 +71,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary 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), - 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; diff --git a/nest/rt-table.c b/nest/rt-table.c index 491ae1d9..946f4021 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -683,7 +683,7 @@ rte_trace(const char *name, const rte *e, int dir, const char *msg) { 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 @@ -1226,29 +1226,29 @@ rte_validate(struct channel *ch, rte *e) return 0; } - if (net_type_match(n, NB_DEST) == !e->attrs->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; + 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)) + { + 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", + log(L_WARN "Ignoring route %N having a nexthop attribute received via %s", n, ch->proto->name); return 0; } @@ -2547,26 +2547,27 @@ rta_apply_hostentry(rta *a, struct hostentry_adata *head) u32 *labels = head->labels; u32 lnum = (u32 *) (head->ad.data + head->ad.length) - labels; - a->dest = he->dest; - ea_set_attr_u32(&a->eattrs, &ea_gen_igp_metric, 0, he->igp_metric); - if (a->dest != RTD_UNICAST) + if (!he->src) { - /* No nexthop */ - ea_unset_attr(&a->eattrs, 0, &ea_gen_nexthop); + ea_set_dest(&a->eattrs, 0, RTD_UNREACHABLE); return; } - if (!lnum && he->nexthop_linkable) + eattr *he_nh_ea = ea_find(he->src->eattrs, &ea_gen_nexthop); + ASSERT_DIE(he_nh_ea); + + struct nexthop_adata *nhad = (struct nexthop_adata *) he_nh_ea->u.ptr; + int idest = nhea_dest(he_nh_ea); + + if ((idest != RTD_UNICAST) || + !lnum && he->nexthop_linkable) { /* Just link the nexthop chain, no label append happens. */ ea_copy_attr(&a->eattrs, he->src->eattrs, &ea_gen_nexthop); return; } - eattr *he_nh_ea = ea_find(he->src->eattrs, &ea_gen_nexthop); - struct nexthop_adata *nhad = (struct nexthop_adata *) he_nh_ea->u.ptr; - uint total_size = OFFSETOF(struct nexthop_adata, nh); NEXTHOP_WALK(nh, nhad) @@ -2583,10 +2584,14 @@ rta_apply_hostentry(rta *a, struct hostentry_adata *head) if (total_size == OFFSETOF(struct nexthop_adata, nh)) { - a->dest = RTD_UNREACHABLE; log(L_WARN "No valid nexthop remaining, setting route unreachable"); - ea_unset_attr(&a->eattrs, 0, &ea_gen_nexthop); + struct nexthop_adata nha = { + .ad.length = NEXTHOP_DEST_SIZE, + .dest = RTD_UNREACHABLE, + }; + + ea_set_attr_data(&a->eattrs, &ea_gen_nexthop, 0, &nha.ad.data, nha.ad.length); return; } @@ -2627,19 +2632,28 @@ rta_apply_hostentry(rta *a, struct hostentry_adata *head) static inline struct hostentry_adata * rta_next_hop_outdated(rta *a) { + /* First retrieve the hostentry */ eattr *heea = ea_find(a->eattrs, &ea_gen_hostentry); if (!heea) return NULL; struct hostentry_adata *head = (struct hostentry_adata *) heea->u.ptr; + /* If no nexthop is present, we have to create one */ + eattr *a_nh_ea = ea_find(a->eattrs, &ea_gen_nexthop); + if (!a_nh_ea) + return head; + + struct nexthop_adata *nhad = (struct nexthop_adata *) a_nh_ea->u.ptr; + + /* Shortcut for unresolvable hostentry */ if (!head->he->src) - return (a->dest != RTD_UNREACHABLE) ? head : NULL; + return NEXTHOP_IS_REACHABLE(nhad) ? head : NULL; + /* Comparing our nexthop with the hostentry nexthop */ eattr *he_nh_ea = ea_find(head->he->src->eattrs, &ea_gen_nexthop); - eattr *a_nh_ea = ea_find(a->eattrs, &ea_gen_nexthop); - return ((a->dest != head->he->dest) || + return ( (ea_get_int(a->eattrs, &ea_gen_igp_metric, IGP_METRIC_UNKNOWN) != head->he->igp_metric) || (!head->he->nexthop_linkable) || (!he_nh_ea != !a_nh_ea) || @@ -2722,7 +2736,7 @@ rta_get_first_asn(rta *a) 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)); @@ -2731,11 +2745,11 @@ rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, i /* 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 */ @@ -2757,7 +2771,7 @@ rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, i /* 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); @@ -2768,17 +2782,17 @@ rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, i 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) @@ -2789,14 +2803,14 @@ rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, i 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 */ @@ -2812,18 +2826,20 @@ rt_flowspec_update_rte(rtable *tab, net *n, rte *r) if (!bc->base_table) return NULL; - 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; + 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); *a = *r->attrs; - a->dest = dest; a->cached = 0; + ea_set_attr_u32(&a->eattrs, &ea_gen_flowspec_valid, 0, valid); + rte new; memcpy(&new, r, sizeof(rte)); new.attrs = a; @@ -2838,18 +2854,23 @@ static inline void rt_flowspec_resolve_rte(rte *r, struct channel *c) { #ifdef CONFIG_BGP - if (rt_get_source_attr(r) != RTS_BGP) - return; - + enum flowspec_valid valid, old = rt_get_flowspec_valid(r); struct bgp_channel *bc = (struct bgp_channel *) c; - if (!bc->base_table) - return; - 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 ( (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; - if (dest == r->attrs->dest) + if (valid == old) return; if (r->attrs->cached) @@ -2860,7 +2881,10 @@ rt_flowspec_resolve_rte(rte *r, struct channel *c) r->attrs = a; } - r->attrs->dest = dest; + 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 } @@ -3651,7 +3675,6 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) /* Reset the hostentry */ he->src = NULL; - he->dest = RTD_UNREACHABLE; he->nexthop_linkable = 0; he->igp_metric = 0; @@ -3672,16 +3695,12 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) goto done; } - if (a->dest == RTD_UNICAST) - { - eattr *ea = ea_find(a->eattrs, &ea_gen_nexthop); - if (!ea) - { - log(L_WARN "No nexthop in unicast route"); - goto done; - } - - NEXTHOP_WALK(nh, (struct nexthop_adata *) ea->u.ptr) + eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop); + ASSERT_DIE(nhea); + struct nexthop_adata *nhad = (void *) nhea->u.ptr; + + if (NEXTHOP_IS_REACHABLE(nhad)) + NEXTHOP_WALK(nh, nhad) if (ipa_zero(nh->gw)) { if (if_local_addr(he->addr, nh->iface)) @@ -3694,10 +3713,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) direct++; } - } he->src = rta_clone(a); - he->dest = a->dest; he->nexthop_linkable = !direct; he->igp_metric = rt_get_igp_metric(&e->rte); } @@ -148,7 +148,6 @@ struct hostentry { unsigned hash_key; /* Hash key */ unsigned uc; /* Use count */ struct rta *src; /* Source rta entry */ - byte dest; /* Chosen route destination type (RTD_...) */ byte nexthop_linkable; /* Nexthop list is completely non-device */ u32 igp_metric; /* Chosen route IGP metric */ }; @@ -460,9 +459,6 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr } */ -int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior); - - /* * Default protocol preferences */ diff --git a/proto/babel/babel.c b/proto/babel/babel.c index f3456369..65d2567e 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -677,10 +677,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e) } }; - rta a0 = { - .dest = RTD_UNICAST, - .eattrs = &eattrs.l, - }; + rta a0 = { .eattrs = &eattrs.l, }; rte e0 = { .attrs = &a0, @@ -693,12 +690,11 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e) else if (e->valid && (e->router_id != p->router_id)) { /* Unreachable */ - rta a0 = { - .dest = RTD_UNREACHABLE, - }; + rta a0 = {}; 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); rte e0 = { .attrs = &a0, @@ -2266,9 +2262,13 @@ babel_kick_timer(struct babel_proto *p) static int babel_preexport(struct channel *c, struct rte *new) { - struct rta *a = new->attrs; + 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; diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index ccfda4df..0b715eaa 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1715,8 +1715,20 @@ bgp_preexport(struct channel *c, rte *e) return 0; /* 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: + 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)) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 8e3ed70e..662d9d48 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -519,7 +519,9 @@ struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id); static inline int rta_resolvable(rta *a) { - return a->dest != RTD_UNREACHABLE; + 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); } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 06b563b0..9911738d 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -968,7 +968,6 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll) ea_set_attr_u32(&a->eattrs, &ea_gen_igp_metric, 0, c->cf->cost); - a->dest = RTD_UNICAST; struct nexthop_adata nhad = { .nh = { .gw = nbr->addr, @@ -1003,8 +1002,7 @@ bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 lnum, u32 labels[ln { REPORT("Too many MPLS labels ($u)", lnum); - a->dest = RTD_UNREACHABLE; - ea_unset_attr(&a->eattrs, 0, &ea_gen_nexthop); + ea_set_dest(&a->eattrs, 0, RTD_UNREACHABLE); return; } @@ -1092,17 +1090,14 @@ bgp_use_gateway(struct bgp_export_state *s) if (c->cf->next_hop_self && bgp_match_src(s, c->cf->next_hop_self)) return NULL; - /* Unreachable */ - if (ra->dest != RTD_UNICAST) - return NULL; - eattr *nhea = ea_find(ra->eattrs, &ea_gen_nexthop); if (!nhea) return NULL; /* We need one valid global gateway */ struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr; - if (!NEXTHOP_ONE(nhad) || ipa_zero(nhad->nh.gw) || + if (!NEXTHOP_IS_REACHABLE(nhad) || + !NEXTHOP_ONE(nhad) || ipa_zero(nhad->nh.gw) || ipa_is_link_local(nhad->nh.gw)) return NULL; diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 0498a3c2..1c76aee7 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1983,8 +1983,7 @@ ort_changed(ort *nf, rta *nr) if (!or || (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->dest != or->dest)) + (nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid)) return 1; eattr *nhea_n = ea_find(nr->eattrs, &ea_gen_nexthop); @@ -2049,7 +2048,6 @@ again1: if (nf->n.type) /* Add the route */ { rta a0 = { - .dest = RTD_UNICAST, }; struct { diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index c2b12cfc..6ff6a745 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1366,20 +1366,11 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *n, rt 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); + 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)) diff --git a/proto/perf/perf.c b/proto/perf/perf.c index 71925bf1..8a883dd7 100644 --- a/proto/perf/perf.c +++ b/proto/perf/perf.c @@ -142,9 +142,7 @@ perf_loop(void *data) *((net_addr_ip4 *) &(p->data[i].net)) = random_net_ip4(); if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) { - struct rta a0 = { - .dest = RTD_UNICAST, - }; + struct rta a0 = {}; ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, p->p.main_channel->preference); ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_PERF); diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 5a66a1be..cc8b57eb 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -151,9 +151,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en) if (rt) { /* Update */ - rta a0 = { - .dest = RTD_UNICAST, - }; + rta a0 = {}; struct { ea_list l; diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c index 96a794fa..c8b0ff67 100644 --- a/proto/rpki/rpki.c +++ b/proto/rpki/rpki.c @@ -120,9 +120,7 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_ { struct rpki_proto *p = cache->p; - rta a0 = { - .dest = RTD_NONE, - }; + rta a0 = {}; ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, channel->preference); ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_RPKI); diff --git a/proto/static/static.c b/proto/static/static.c index 038ee018..6369fea5 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -55,7 +55,6 @@ static_announce_rte(struct static_proto *p, struct static_route *r) { rta *a = allocz(RTA_MAX_SIZE); struct rte_src *src = static_get_source(p, r->index); - a->dest = r->dest; ea_set_attr_u32(&a->eattrs, &ea_gen_preference, 0, p->p.main_channel->preference); ea_set_attr_u32(&a->eattrs, &ea_gen_source, 0, RTS_STATIC); @@ -97,7 +96,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r) nhad->ad.data, (void *) nh - (void *) nhad->ad.data); } - if (r->dest == RTDX_RECURSIVE) + else if (r->dest == RTDX_RECURSIVE) { rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6; u32 *labels = r->mls ? (void *) r->mls->data : NULL; @@ -107,6 +106,9 @@ static_announce_rte(struct static_proto *p, struct static_route *r) r->via, IPA_NONE, lnum, labels); } + else if (r->dest) + ea_set_dest(&a->eattrs, 0, r->dest); + /* Already announced */ if (r->state == SRS_CLEAN) return; diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index e63fe938..a4172a6d 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1407,11 +1407,16 @@ HASH_DEFINE_REHASH_FN(RTH, struct krt_proto) int krt_capable(rte *e) { - rta *a = e->attrs; + eattr *ea = ea_find(e->attrs->eattrs, &ea_gen_nexthop); + if (!ea) + return 0; + + struct nexthop_adata *nhad = (void *) ea->u.ptr; + if (NEXTHOP_IS_REACHABLE(nhad)) + return 1; - switch (a->dest) + switch (nhad->dest) { - case RTD_UNICAST: case RTD_BLACKHOLE: case RTD_UNREACHABLE: case RTD_PROHIBIT: @@ -1590,7 +1595,7 @@ nl_add_rte(struct krt_proto *p, rte *e) eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop); struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL; - if (krt_ecmp6(p) && nhad && !NEXTHOP_ONE(nhad)) + if (krt_ecmp6(p) && nhad && NEXTHOP_IS_REACHABLE(nhad) && !NEXTHOP_ONE(nhad)) { uint cnt = 0; NEXTHOP_WALK(nh, nhad) @@ -1615,7 +1620,8 @@ nl_add_rte(struct krt_proto *p, rte *e) return err; } - return nl_send_route(p, e, NL_OP_ADD, a->dest, nhad); + return nl_send_route(p, e, NL_OP_ADD, + NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest, nhad); } static inline int @@ -1637,7 +1643,8 @@ nl_replace_rte(struct krt_proto *p, rte *e) rta *a = e->attrs; eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop); struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL; - return nl_send_route(p, e, NL_OP_REPLACE, a->dest, nhad); + return nl_send_route(p, e, NL_OP_REPLACE, + NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest, nhad); } @@ -1900,8 +1907,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) switch (i->rtm_type) { case RTN_UNICAST: - ra->dest = RTD_UNICAST; - if (a[RTA_MULTIPATH]) { struct nexthop_adata *nh = nl_parse_multipath(s, p, net, a[RTA_MULTIPATH], i->rtm_family, krt_src); @@ -1952,15 +1957,40 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) } } +#ifdef HAVE_MPLS_KERNEL + if ((i->rtm_family == AF_MPLS) && a[RTA_NEWDST] && !a[RTA_MULTIPATH]) + nhad.nh.labels = rta_get_mpls(a[RTA_NEWDST], nhad.nh.label); + + if (a[RTA_ENCAP] && a[RTA_ENCAP_TYPE] && !a[RTA_MULTIPATH]) + { + switch (rta_get_u16(a[RTA_ENCAP_TYPE])) + { + case LWTUNNEL_ENCAP_MPLS: + { + struct rtattr *enca[BIRD_RTA_MAX]; + nl_attr_len = RTA_PAYLOAD(a[RTA_ENCAP]); + nl_parse_attrs(RTA_DATA(a[RTA_ENCAP]), encap_mpls_want, enca, sizeof(enca)); + nhad.nh.labels = rta_get_mpls(enca[RTA_DST], nhad.nh.label); + break; + } + default: + SKIP("unknown encapsulation method %d\n", rta_get_u16(a[RTA_ENCAP_TYPE])); + break; + } + } +#endif + + /* Finalize the nexthop */ + nhad.ad.length = (void *) NEXTHOP_NEXT(&nhad.nh) - (void *) nhad.ad.data; break; case RTN_BLACKHOLE: - ra->dest = RTD_BLACKHOLE; + nhad.nhad = NEXTHOP_DEST_LITERAL(RTD_BLACKHOLE); break; case RTN_UNREACHABLE: - ra->dest = RTD_UNREACHABLE; + nhad.nhad = NEXTHOP_DEST_LITERAL(RTD_UNREACHABLE); break; case RTN_PROHIBIT: - ra->dest = RTD_PROHIBIT; + nhad.nhad = NEXTHOP_DEST_LITERAL(RTD_PROHIBIT); break; /* FIXME: What about RTN_THROW? */ default: @@ -1968,32 +1998,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) return; } -#ifdef HAVE_MPLS_KERNEL - if ((i->rtm_family == AF_MPLS) && a[RTA_NEWDST] && !a[RTA_MULTIPATH]) - nhad.nh.labels = rta_get_mpls(a[RTA_NEWDST], nhad.nh.label); - - if (a[RTA_ENCAP] && a[RTA_ENCAP_TYPE] && !a[RTA_MULTIPATH]) - { - switch (rta_get_u16(a[RTA_ENCAP_TYPE])) - { - case LWTUNNEL_ENCAP_MPLS: - { - struct rtattr *enca[BIRD_RTA_MAX]; - nl_attr_len = RTA_PAYLOAD(a[RTA_ENCAP]); - nl_parse_attrs(RTA_DATA(a[RTA_ENCAP]), encap_mpls_want, enca, sizeof(enca)); - nhad.nh.labels = rta_get_mpls(enca[RTA_DST], nhad.nh.label); - break; - } - default: - SKIP("unknown encapsulation method %d\n", rta_get_u16(a[RTA_ENCAP_TYPE])); - break; - } - } -#endif - - /* Finalize the nexthop */ - nhad.ad.length = (void *) NEXTHOP_NEXT(&nhad.nh) - (void *) nhad.ad.data; - if (i->rtm_scope != def_scope) ea_set_attr(&ra->eattrs, EA_LITERAL_EMBEDDED(&ea_krt_scope, 0, i->rtm_scope)); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 5bcaa99b..6e55d8f5 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -618,17 +618,10 @@ krt_same_dest(rte *k, rte *e) { rta *ka = k->attrs, *ea = e->attrs; - if (ka->dest != ea->dest) - return 0; - - if (ka->dest != RTD_UNICAST) - return 1; - eattr *nhea_k = ea_find(ka->eattrs, &ea_gen_nexthop); eattr *nhea_e = ea_find(ea->eattrs, &ea_gen_nexthop); - ASSUME(nhea_k && nhea_e); - return adata_same(nhea_k->u.ptr, nhea_e->u.ptr); + return (!nhea_k == !nhea_e) && adata_same(nhea_k->u.ptr, nhea_e->u.ptr); } /* |