On Wed, Apr 12, 2023 at 05:33:10PM +0200, Claudio Jeker wrote: > This is the first big amount of flowspec specific code. > It adds a new file (flowspec.c) which exposes basic API functions to work > with flowspec. Right now apart from the regress test nothing uses these > functions (but don't worry I have other diffs which make use of them). > > Flowspec encoding is super annoying. It is extremly flexible, the length > of individual components is encoded into the component, there are different > encodings for IPv4 and IPv6, some encodings make absolutly no sense, and the > total length can be up to 4095 bytes (yikes).
This is complete madness. > Because of this the idea is to keep flowspec as NLRI blob, use this API to > validate the blob and provide a RFC compliant compare function. > Additionally provide functions to extract components and helpers to print > components. Yes, that makes sense. > I know some not so important functions are still missing but this is a > decent start. Especially formating a flowspec into a string is not fully > there yet. Here's a first round of feedback. I need to ponder this more since it involves a lot of scary pointer manipulations. I don't think my head will cooperate without doing something else for a while. Unless it blocks you from further progress, I would prefer to wait with landing this for a day or two more. > -- > :wq Claudio > > Index: usr.sbin/bgpd/Makefile > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v > retrieving revision 1.38 > diff -u -p -r1.38 Makefile > --- usr.sbin/bgpd/Makefile 11 Jan 2023 13:53:17 -0000 1.38 > +++ usr.sbin/bgpd/Makefile 12 Apr 2023 15:14:49 -0000 > @@ -5,7 +5,7 @@ SRCS= bgpd.c session.c log.c logmsg.c pa > rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c control.c \ > pfkey.c rde_update.c rde_attr.c rde_community.c printconf.c \ > rde_filter.c rde_sets.c rde_aspa.c rde_trie.c pftable.c name2id.c \ > - util.c carp.c timer.c rde_peer.c rtr.c rtr_proto.c > + util.c carp.c timer.c rde_peer.c rtr.c rtr_proto.c flowspec.c > CFLAGS+= -Wall -I${.CURDIR} > CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes > CFLAGS+= -Wmissing-declarations > Index: usr.sbin/bgpd/bgpd.h > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v > retrieving revision 1.470 > diff -u -p -r1.470 bgpd.h > --- usr.sbin/bgpd/bgpd.h 3 Apr 2023 10:48:00 -0000 1.470 > +++ usr.sbin/bgpd/bgpd.h 12 Apr 2023 15:14:49 -0000 > @@ -1087,18 +1087,22 @@ struct ext_comm_pairs { > extern const struct ext_comm_pairs iana_ext_comms[]; > > /* BGP flowspec defines RFC 8955 and 8956 */ > -#define FLOWSPEC_LEN_LIMIT 0xf0 > -#define FLOWSPEC_OP_EOL 0x80 > -#define FLOWSPEC_OP_AND 0x40 > -#define FLOWSPEC_OP_LEN_MASK 0x30 > -#define FLOWSPEC_OP_LEN_SHIFT 4 > +#define FLOWSPEC_LEN_LIMIT 0xf0 > +#define FLOWSPEC_OP_EOL 0x80 > +#define FLOWSPEC_OP_AND 0x40 > +#define FLOWSPEC_OP_LEN_MASK 0x30 > +#define FLOWSPEC_OP_LEN_SHIFT 4 > #define FLOWSPEC_OP_LEN(op) \ > (1 << (((op) & FLOWSPEC_OP_LEN_MASK) >> FLOWSPEC_OP_LEN_SHIFT)) > -#define FLOWSPEC_OP_NUM_LT 0x04 > -#define FLOWSPEC_OP_NUM_GT 0x02 > -#define FLOWSPEC_OP_NUM_EQ 0x01 > -#define FLOWSPEC_OP_BIT_NOT 0x02 > -#define FLOWSPEC_OP_BIT_MATCH 0x01 > +#define FLOWSPEC_OP_NUM_LT 0x04 > +#define FLOWSPEC_OP_NUM_GT 0x02 > +#define FLOWSPEC_OP_NUM_EQ 0x01 > +#define FLOWSPEC_OP_NUM_LE (FLOWSPEC_OP_NUM_LT | FLOWSPEC_OP_NUM_EQ) > +#define FLOWSPEC_OP_NUM_GE (FLOWSPEC_OP_NUM_GT | FLOWSPEC_OP_NUM_EQ) > +#define FLOWSPEC_OP_NUM_NOT (FLOWSPEC_OP_NUM_GT | FLOWSPEC_OP_NUM_LT) Maybe add #define FLOWSPEC_OP_NUM_FALSE 0x00 #define FLOWSPEC_OP_NUM_TRUE 0x07 and use them in the switch in flowspec_fmt_num_op(). > +#define FLOWSPEC_OP_NUM_MASK 0x07 > +#define FLOWSPEC_OP_BIT_NOT 0x02 > +#define FLOWSPEC_OP_BIT_MATCH 0x01 > > #define FLOWSPEC_TYPE_MIN 1 > #define FLOWSPEC_TYPE_DEST 1 > @@ -1510,6 +1514,7 @@ int aspath_verify(void *, uint16_t, in > #define AS_ERR_BAD -3 > #define AS_ERR_SOFT -4 > u_char *aspath_inflate(void *, uint16_t, uint16_t *); > +int extract_prefix(const u_char *, int, void *, uint8_t, uint8_t); > int nlri_get_prefix(u_char *, uint16_t, struct bgpd_addr *, > uint8_t *); > int nlri_get_prefix6(u_char *, uint16_t, struct bgpd_addr *, > @@ -1532,6 +1537,15 @@ int af2aid(sa_family_t, uint8_t, uint8 > struct sockaddr *addr2sa(const struct bgpd_addr *, uint16_t, socklen_t > *); > void sa2addr(struct sockaddr *, struct bgpd_addr *, uint16_t *); > const char * get_baudrate(unsigned long long, char *); > + > +/* flowspec.c */ > +int flowspec_valid(const uint8_t *, int, int); > +int flowspec_cmp(const uint8_t *, int, const uint8_t *, int, int); > +int flowspec_get_component(const uint8_t *, int, int, int, > + const uint8_t **, int *); > +int flowspec_get_addr(const uint8_t *, int, int, int, struct bgpd_addr *, > + uint8_t *, uint8_t *); > +const char *flowspec_fmt_num_op(const uint8_t *, int, int *); > > static const char * const log_procnames[] = { > "parent", > Index: usr.sbin/bgpd/flowspec.c > =================================================================== > RCS file: usr.sbin/bgpd/flowspec.c > diff -N usr.sbin/bgpd/flowspec.c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ usr.sbin/bgpd/flowspec.c 12 Apr 2023 15:14:49 -0000 > @@ -0,0 +1,475 @@ > +/* $OpenBSD$ */ > + > +/* > + * Copyright (c) 2023 Claudio Jeker <clau...@openbsd.org> > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <string.h> > +#include <stdio.h> > + > +#include "bgpd.h" > +#include "rde.h" > + > +/* > + * Extract the next component from a flowspec NLRI buffer. > + * Returns the length of the component inclusive type field or -1 on failure. I'd do s/inclusive/including > + * Also checks that the prefix encoding is valid. > + */ > +static int > +flowspec_next_component(const uint8_t *buf, int len, int is_v6, int *type) > +{ > + int vlen = 0; > + uint8_t plen, off, op; > + > + *type = 0; > + if (len < 1) > + return -1; > + *type = buf[vlen]; > + vlen++; > + if (*type < FLOWSPEC_TYPE_MIN || *type > FLOWSPEC_TYPE_MAX) > + return -1; > + > + switch (*type) { > + case FLOWSPEC_TYPE_DEST: > + case FLOWSPEC_TYPE_SOURCE: > + if (!is_v6) { Maybe add a reference to RFC 4271, section 4.3 here. The generic references in RFC 8955 aren't helpful. > + if (len < vlen + 1) > + return -1; > + plen = buf[vlen]; > + vlen += PREFIX_SIZE(plen); > + if (plen > 32 || len < vlen) > + return -1; > + } else { > + if (len < vlen + 2) > + return -1; > + plen = buf[vlen]; > + off = buf[vlen + 1]; > + if (plen > 128 || off >= plen) > + return -1; > + vlen += PREFIX_SIZE(plen - off) + 1; /* off is extra */ > + if (len < vlen) > + return -1; It took me way too long to figure out I have to go look at RFC 8956, section 3.1. Starting from RFC 8955 I tried to find something about this mysterious off in RFC 4271 and was utterly confused for the better part of an hour... In short: please add a comment for the stupid. Future me will be grateful. > + } > + break; > + case FLOWSPEC_TYPE_FLOW: > + if (!is_v6) > + return -1; > + /* FALLTHROUGH */ > + default: > + do { > + if (len < vlen + 1) > + return -1; > + op = buf[vlen]; > + /* first component cannot have and flag set */ > + if (vlen == 1 && op & FLOWSPEC_OP_AND) > + return -1; > + vlen += FLOWSPEC_OP_LEN(op) + 1; > + > + if (len < vlen) > + return -1; > + } while ((op & FLOWSPEC_OP_EOL) == 0); > + break; > + } > + return vlen; > +} > + > +#define MINIMUM(a, b) ((a) < (b) ? (a) : (b)) > + > +/* > + * Compare two IPv4 flowspec prefix components. > + * Returns 1 if first prefix is preferred, -1 if second, 0 if equal. > + */ > +static int > +flowspec_cmp_prefix4(const uint8_t *abuf, int ablen, const uint8_t *bbuf, > + int bblen) > +{ > + uint8_t a[4] = { 0 }, b[4] = { 0 }; > + int alen, blen, clen, cmp; > + > + alen = abuf[1]; > + blen = bbuf[1]; > + clen = MINIMUM(alen, blen); > + > + /* only extract the common prefix */ > + if (extract_prefix(abuf + 2, ablen - 2, &a, clen, sizeof(a)) == -1) > + fatalx("bad flowspec prefix encoding"); > + if (extract_prefix(bbuf + 2, bblen - 2, &b, clen, sizeof(b)) == -1) > + fatalx("bad flowspec prefix encoding"); > + > + /* lowest IP value has precedence */ > + cmp = memcmp(&a, &b, sizeof(a)); This is fine as it is, but I'd prefer to omit the &. This always confuses me. > + if (cmp < 0) > + return 1; > + if (cmp > 0) > + return -1; > + > + /* if common prefix, more specific route has precedence */ > + if (alen > blen) > + return 1; > + if (alen < blen) > + return -1; > + return 0; > +} > + > +/* > + * Compare two IPv6 flowspec prefix components. > + * Returns 1 if first prefix is preferred, -1 if second, 0 if equal. > + * As usual the encoding of IPv6 addresses is extra complex. > + */ > +static int > +flowspec_cmp_prefix6(const uint8_t *abuf, int ablen, const uint8_t *bbuf, > + int bblen) > +{ > + uint8_t a[16] = { 0 }, b[16] = { 0 }; > + int alen, blen, clen, cmp; > + > + /* lowest offset has precedence */ > + if (abuf[2] < bbuf[2]) > + return 1; > + if (abuf[2] > bbuf[2]) > + return -1; > + > + /* calculate actual pattern size (len - offset) */ > + alen = abuf[1] - abuf[2]; > + blen = bbuf[1] - bbuf[2]; > + clen = MINIMUM(alen, blen); > + > + /* only extract the common prefix */ > + if (extract_prefix(abuf + 3, ablen - 3, &a, clen, sizeof(a)) == -1) > + fatalx("bad flowspec prefix encoding"); > + if (extract_prefix(bbuf + 3, bblen - 3, &b, clen, sizeof(b)) == -1) > + fatalx("bad flowspec prefix encoding"); > + > + /* lowest IP value has precedence */ > + cmp = memcmp(&a, &b, sizeof(a)); ditto > + if (cmp < 0) > + return 1; > + if (cmp > 0) > + return -1; > + > + /* if common prefix, more specific route has precedence */ > + if (alen > blen) > + return 1; > + if (alen < blen) > + return -1; > + return 0; > +} > + > +/* > + * Check if the flowspec NLRI is syntactically valid. > + */ > +int > +flowspec_valid(const uint8_t *buf, int len, int is_v6) > +{ > + int l, type, prev = 0; > + > + /* empty NLRI is invalid */ > + if (len == 0) > + return -1; > + > + while (len > 0) { > + l = flowspec_next_component(buf, len, is_v6, &type); > + if (l == -1) > + return -1; > + /* ensure that types are ordered */ > + if (prev >= type) > + return -1; > + prev = type; > + buf += l; > + len -= l; > + } > + if (len < 0) > + fatalx("flowspec overflow"); > + return 0; > +} > + > +/* > + * Compare two valid flowspec NLRI objects according to RFC 8955 & 8956. > + * Returns 1 if the first object has preference, -1 if not, and 0 if the > + * two objects are equal. > + */ > +int > +flowspec_cmp(const uint8_t *a, int alen, const uint8_t *b, int blen, int > is_v6) > +{ > + int atype, btype; > + int acomplen, bcomplen; > + int cmp; > + > + while (alen > 0 && blen > 0) { > + acomplen = flowspec_next_component(a, alen, is_v6, &atype); > + if (acomplen == -1) > + fatalx("bad flowspec component"); > + bcomplen = flowspec_next_component(b, blen, is_v6, &btype); > + if (acomplen == -1) > + fatalx("bad flowspec component"); > + > + /* If types differ, lowest type wins. */ > + if (atype < btype) > + return 1; > + if (atype > btype) > + return -1; > + > + switch (atype) { > + case FLOWSPEC_TYPE_DEST: > + case FLOWSPEC_TYPE_SOURCE: > + if (!is_v6) { > + cmp = flowspec_cmp_prefix4(a, acomplen, > + b, bcomplen); > + } else { > + cmp = flowspec_cmp_prefix6(a, acomplen, > + b, bcomplen); > + } > + if (cmp != 0) > + return cmp; > + break; > + default: > + cmp = memcmp(a, b, MINIMUM(acomplen, bcomplen)); > + /* > + * Lowest common component prefix wins also > + * if both commponents are same lenght also lowest s/lenght/length > + * string has precedence. > + */ > + if (cmp < 0) > + return 1; > + if (cmp > 0) > + return -1; > + /* > + * Longest component wins when common prefix is equal. > + * This is not really possible because EOL encoding will > + * always tie break on the memcmp but the RFC mandates > + * it (and it is cheap). > + */ > + if (acomplen > bcomplen) > + return 1; > + if (acomplen < bcomplen) > + return -1; > + break; > + } > + a += acomplen; > + alen -= acomplen; > + b += bcomplen; > + blen -= bcomplen; > + > + /* Rule with more components wins */ > + if (alen > 0 && blen <= 0) > + return 1; > + if (alen <= 0 && blen > 0) > + return -1; > + } > + return 0; > +} > + > +static void > +shift_right(uint8_t *dst, const uint8_t *src, int off, int len) > +{ > + uint8_t carry = 0; > + int i; > + > + dst += off / 8; /* go to inital start point */ > + off %= 8; > + len = (len + 7) / 8; /* how much to copy in bytes */ > + > + for (i = 0; i < len; i++) { > + dst[i] = carry | src[i] >> off; > + if (off != 0) > + carry = src[i] << (8 - off); > + } > + dst[i] = carry; > +} > + > +/* > + * Extract a flowspec component and return its buffer and size. > + * Returns 1 on success, 0 if component is not present and -1 on error. > + */ > +int > +flowspec_get_component(const uint8_t *flow, int flowlen, int type, int is_v6, > + const uint8_t **buf, int *len) > +{ > + int complen, t; > + > + *buf = NULL; > + *len = 0; > + > + do { > + complen = flowspec_next_component(flow, flowlen, is_v6, &t); > + if (complen == -1) > + return -1; > + if (type == t) > + break; > + if (type < t) > + return 0; > + > + flow += complen; > + flowlen -= complen; > + } while (1); > + > + *buf = flow + 1; > + *len = complen - 1; > + > + return 1; > +} > + > +/* > + * Extract source or destination address into provided bgpd_addr. > + * Returns 1 on success, 0 if no address was present, -1 on error. > + * Sets plen to the prefix len and olen to the offset for IPv6 case. > + * If olen is set to NULL when an IPv6 prefix with offset is fetched > + * the function will return -1. > + */ > +int > +flowspec_get_addr(const uint8_t *flow, int flowlen, int type, int is_v6, > + struct bgpd_addr *addr, uint8_t *plen, uint8_t *olen) > +{ > + const uint8_t *comp; > + int complen, rv; > + > + memset(addr, 0, sizeof(*addr)); > + *plen = 0; > + if (olen != NULL) > + *olen = 0; > + > + rv = flowspec_get_component(flow, flowlen, type, is_v6, > + &comp, &complen); > + if (rv != 1) > + return rv; > + > + /* flowspec_get_component only returns valid encodings */ > + if (!is_v6) { > + addr->aid = AID_INET; > + if (extract_prefix(comp + 1, complen - 1, &addr->v4, comp[0], > + sizeof(addr->v4)) == -1) > + return -1; > + *plen = comp[0]; > + } else { > + uint8_t buf[16] = { 0 }; > + int xlen, xoff = 0; > + > + addr->aid = AID_INET6; > + xlen = comp[0]; > + if (comp[1] != 0) { > + if (olen == NULL) > + return -1; > + xoff = comp[1]; > + xlen -= xoff; > + } > + if (extract_prefix(comp + 2, complen - 2, buf, xlen, > + sizeof(buf)) == -1) > + return -1; > + shift_right(addr->v6.s6_addr, buf, *olen, xlen); > + *plen = comp[0]; > + if (olen != NULL) > + *olen = comp[1]; > + } > + > + return 1; > +} > + > +static long long > +extract_val(const uint8_t *comp, int len) > +{ > + long long val = 0; I'm surprised val isn't a uint64_t. > + > + while (len-- > 0) { > + val <<= 8; If the function is called with a len of 8 and the top bit of comp[0] is set, this will "shift into the sign bit" of val, which is undefined behavior. > + val |= *comp++; > + } > + return val; > +} > + > +const char * > +flowspec_fmt_num_op(const uint8_t *comp, int complen, int *off) > +{ > + static char buf[32]; > + long long val, val2 = 0; > + uint8_t op, op2 = 0; > + int len, len2 = 0; > + > + if (*off == -1) > + return ""; > + if (complen < *off + 1) > + return "bad encoding"; > + > + op = comp[*off]; > + len = FLOWSPEC_OP_LEN(op) + 1; > + if (complen < *off + len) > + return "bad encoding"; > + val = extract_val(comp + *off + 1, FLOWSPEC_OP_LEN(op)); > + > + if ((op & FLOWSPEC_OP_EOL) == 0) { > + if (complen < *off + len + 1) > + return "bad encoding"; > + op2 = comp[*off + len]; > + /* > + * Check if this is a range specification else fall back > + * to basic rules. > + */ > + if (op2 & FLOWSPEC_OP_AND && > + (op & FLOWSPEC_OP_NUM_MASK) == FLOWSPEC_OP_NUM_GE && > + (op2 & FLOWSPEC_OP_NUM_MASK) == FLOWSPEC_OP_NUM_LE) { > + len2 = FLOWSPEC_OP_LEN(op2) + 1; > + val2 = extract_val(comp + *off + len + 1, > + FLOWSPEC_OP_LEN(op2)); > + } else > + op2 = 0; > + } > + > + if (op2 & FLOWSPEC_OP_AND) { > + /* binary range operation */ > + snprintf(buf, sizeof(buf), "%lld - %lld", val, val2); > + } else { > + /* unary operation */ > + switch (op & FLOWSPEC_OP_NUM_MASK) { > + case 0: > + snprintf(buf, sizeof(buf), "%sfalse", > + op & FLOWSPEC_OP_AND ? "&& " : ""); > + break; > + case FLOWSPEC_OP_NUM_EQ: > + snprintf(buf, sizeof(buf), "%s%lld", > + op & FLOWSPEC_OP_AND ? "&& " : "", val); > + break; > + case FLOWSPEC_OP_NUM_GT: > + snprintf(buf, sizeof(buf), "%s> %lld", > + op & FLOWSPEC_OP_AND ? "&& " : "", val); > + break; > + case FLOWSPEC_OP_NUM_GE: > + snprintf(buf, sizeof(buf), "%s>= %lld", > + op & FLOWSPEC_OP_AND ? "&& " : "", val); > + break; > + case FLOWSPEC_OP_NUM_LT: > + snprintf(buf, sizeof(buf), "%s< %lld", > + op & FLOWSPEC_OP_AND ? "&& " : "", val); > + break; > + case FLOWSPEC_OP_NUM_LE: > + snprintf(buf, sizeof(buf), "%s<= %lld", > + op & FLOWSPEC_OP_AND ? "&& " : "", val); > + break; > + case FLOWSPEC_OP_NUM_NOT: > + snprintf(buf, sizeof(buf), "%s!= %lld", > + op & FLOWSPEC_OP_AND ? "&& " : "", val); > + break; > + case 0x7: > + snprintf(buf, sizeof(buf), "%sfalse", That should be true, not false > + op & FLOWSPEC_OP_AND ? "&& " : ""); > + break; > + } > + } > + > + if (op2 & FLOWSPEC_OP_EOL || op & FLOWSPEC_OP_EOL) > + *off = -1; > + else > + *off += len + len2; > + > + return buf; > +} > Index: usr.sbin/bgpd/util.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/util.c,v > retrieving revision 1.76 > diff -u -p -r1.76 util.c > --- usr.sbin/bgpd/util.c 3 Apr 2023 10:48:00 -0000 1.76 > +++ usr.sbin/bgpd/util.c 12 Apr 2023 15:14:49 -0000 > @@ -494,8 +494,8 @@ aspath_inflate(void *data, uint16_t len, > } > > /* NLRI functions to extract prefixes from the NLRI blobs */ > -static int > -extract_prefix(u_char *p, uint16_t len, void *va, uint8_t pfxlen, uint8_t > max) > +int > +extract_prefix(const u_char *p, int len, void *va, uint8_t pfxlen, uint8_t > max) > { > static u_char addrmask[] = { > 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; > Index: regress/usr.sbin/bgpd/unittests/Makefile > =================================================================== > RCS file: /cvs/src/regress/usr.sbin/bgpd/unittests/Makefile,v > retrieving revision 1.10 > diff -u -p -r1.10 Makefile > --- regress/usr.sbin/bgpd/unittests/Makefile 11 Jan 2023 13:55:08 -0000 > 1.10 > +++ regress/usr.sbin/bgpd/unittests/Makefile 12 Apr 2023 15:15:50 -0000 > @@ -7,6 +7,7 @@ PROGS += rde_trie_test > PROGS += rde_community_test > PROGS += rde_decide_test > PROGS += rde_aspa_test > +PROGS += rde_flowspec_test > > .for p in ${PROGS} > REGRESS_TARGETS += run-regress-$p > @@ -38,5 +39,7 @@ run-regress-rde_trie_test-${n}: rde_trie > SRCS_rde_community_test= rde_community_test.c rde_community.c > > SRCS_rde_decide_test= rde_decide_test.c rde_decide.c rde_attr.c util.c > + > +SRCS_rde_flowspec_test= rde_flowspec_test.c flowspec.c util.c > > .include <bsd.regress.mk> > Index: regress/usr.sbin/bgpd/unittests/rde_flowspec_test.c > =================================================================== > RCS file: regress/usr.sbin/bgpd/unittests/rde_flowspec_test.c > diff -N regress/usr.sbin/bgpd/unittests/rde_flowspec_test.c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ regress/usr.sbin/bgpd/unittests/rde_flowspec_test.c 12 Apr 2023 > 15:15:50 -0000 > @@ -0,0 +1,198 @@ > +/* $OpenBSD$ */ > + > +/* > + * Copyright (c) 2023 Claudio Jeker <clau...@openbsd.org> > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <err.h> > +#include <stdio.h> > + > +#include "bgpd.h" > +#include "rde.h" > + > +const uint8_t ordered0[] = { 0x01, 0x00, 0x02, 0x00, 0x03, 0x80, 0x00 }; > +const uint8_t ordered1[] = { 0x02, 0x00, 0x01, 0x00, 0x03, 0x80, 0x00 }; > +const uint8_t ordered2[] = { 0x02, 0x00, 0x03, 0x80, 0x00, 0x01, 0x00 }; > +const uint8_t ordered3[] = { 0x01, 0x00, 0x01, 0x00, 0x03, 0x80, 0x00 }; > + > +const uint8_t minmax0[] = { 0x00, 0x00 }; > +const uint8_t minmax1[] = { 0x0e, 0x00 }; > +const uint8_t minmax2[] = { 0xfe, 0x00 }; > + > +const uint8_t flow[] = { 0x0d, 0x80, 0x00 }; > + > +const uint8_t badand[] = { 0x04, 0xc0, 0x00 }; > +const uint8_t goodand[] = { 0x04, 0x00, 0x00, 0xc0, 0x00 }; > + > +const uint8_t overflow0[] = { 0x04 }; > +const uint8_t overflow1[] = { 0x04, 0x80 }; > +const uint8_t overflow2[] = { 0x04, 0x90, 0x00 }; > +const uint8_t overflow3[] = { 0x04, 0xc0, 0x00, 0x00, 0x00 }; > +const uint8_t overflow4[] = { 0x04, 0x00, 0x00 }; > +const uint8_t overflow5[] = { 0x04, 0x00, 0x00, 0x80 }; > +const uint8_t overflow6[] = { 0x04, 0x10, 0x00, 0x80 }; > +const uint8_t prefix0[] = { 0x01 }; > +const uint8_t prefix1[] = { 0x01, 0x07 }; > +const uint8_t prefix2[] = { 0x01, 0x0a, 0xef }; > +const uint8_t prefix3[] = { 0x01, 0x11, 0xef, 0x00 }; > +const uint8_t prefix4[] = { 0x01, 0x21, 0xef, 0x00, 0x00, 0x01, 0x00 }; > +const uint8_t prefix60[] = { 0x01 }; > +const uint8_t prefix61[] = { 0x01, 0x07 }; > +const uint8_t prefix62[] = { 0x01, 0x10, 0x1 }; > +const uint8_t prefix63[] = { 0x01, 0x10, 0x01, 0x20 }; > +const uint8_t prefix64[] = { 0x01, 0x81, 0x73, 0x20, 0x01 }; > +const uint8_t prefix65[] = { 0x01, 0x80, 0x83, 0x20, 0x01 }; > +const uint8_t prefix66[] = { 0x01, 0x40, 0x40, 0x20, 0x01 }; > + > +static void > +test_flowspec_valid(void) > +{ > + /* empty NLRI is invalid */ > + if (flowspec_valid(NULL, 0, 0) != -1) > + errx(1, "empty NLRI is not invalid"); > + > + /* ensure that type range is checked */ > + if (flowspec_valid(minmax0, sizeof(minmax0), 0) != -1 || > + flowspec_valid(minmax1, sizeof(minmax1), 0) != -1 || > + flowspec_valid(minmax2, sizeof(minmax2), 0) != -1) > + errx(1, "out of range type is not invalid"); > + > + /* ensure that types are ordered */ > + if (flowspec_valid(ordered0, sizeof(ordered0), 0) != 0) > + errx(1, "in order NLRI is not valid"); > + if (flowspec_valid(ordered1, sizeof(ordered1), 0) != -1 || > + flowspec_valid(ordered2, sizeof(ordered2), 0) != -1 || > + flowspec_valid(ordered3, sizeof(ordered3), 0) != -1) > + errx(1, "out of order types are not invalid"); > + > + /* flow is only valid in the IPv6 case */ > + if (flowspec_valid(flow, sizeof(flow), 0) != -1) > + errx(1, "FLOW type for IPv4 is not invalid"); > + if (flowspec_valid(flow, sizeof(flow), 1) != 0) > + errx(1, "FLOW type for IPv4 is not valid"); > + > + /* first component cannot have and flag set */ > + if (flowspec_valid(badand, sizeof(badand), 0) != -1) > + errx(1, "AND in first element is not invalid"); > + if (flowspec_valid(goodand, sizeof(goodand), 0) != 0) > + errx(1, "AND in other element is not valid"); > + > + /* various overflows */ > + if (flowspec_valid(overflow0, sizeof(overflow0), 0) != -1 || > + flowspec_valid(overflow1, sizeof(overflow1), 0) != -1 || > + flowspec_valid(overflow2, sizeof(overflow2), 0) != -1 || > + flowspec_valid(overflow3, sizeof(overflow3), 0) != -1 || > + flowspec_valid(overflow4, sizeof(overflow4), 0) != -1 || > + flowspec_valid(overflow5, sizeof(overflow5), 0) != -1 || > + flowspec_valid(overflow6, sizeof(overflow6), 0) != -1) > + errx(1, "overflow not detected"); > + > + if (flowspec_valid(prefix0, sizeof(prefix0), 0) != -1 || > + flowspec_valid(prefix1, sizeof(prefix1), 0) != -1 || > + flowspec_valid(prefix2, sizeof(prefix2), 0) != -1 || > + flowspec_valid(prefix3, sizeof(prefix3), 0) != -1 || > + flowspec_valid(prefix4, sizeof(prefix4), 0) != -1) > + errx(1, "bad prefix encoding is not invalid"); > + > + if (flowspec_valid(prefix60, sizeof(prefix60), 1) != -1 || > + flowspec_valid(prefix61, sizeof(prefix61), 1) != -1 || > + flowspec_valid(prefix62, sizeof(prefix62), 1) != -1 || > + flowspec_valid(prefix63, sizeof(prefix63), 1) != -1 || > + flowspec_valid(prefix64, sizeof(prefix64), 1) != -1 || > + flowspec_valid(prefix65, sizeof(prefix65), 1) != -1 || > + flowspec_valid(prefix66, sizeof(prefix66), 1) != -1) > + errx(1, "bad IPv6 prefix encoding is not invalid"); > +} > + > +static int > +do_cmp(const uint8_t *a, int alen, const uint8_t *b, int blen, int is_v6) > +{ > + if (flowspec_cmp(a, alen, b, blen, is_v6) != 1 || > + flowspec_cmp(b, blen, a, alen, is_v6) != -1) > + return -1; > + return 0; > +} > + > +const uint8_t cmp1[] = { 0x01, 0x00 }; > +const uint8_t cmp2[] = { 0x02, 0x00 }; > +const uint8_t cmp3[] = { 0x01, 0x00, 0x2, 0x00 }; > +const uint8_t cmp4[] = { 0x04, 0x80, 0x2 }; > +const uint8_t cmp5[] = { 0x04, 0x80, 0x3 }; > +const uint8_t cmp6[] = { 0x04, 0x00, 0x3, 0x80, 0x02 }; > + > +const uint8_t cmp41[] = { 0x01, 24, 192, 168, 16 }; > +const uint8_t cmp42[] = { 0x01, 24, 192, 168, 32 }; > +const uint8_t cmp43[] = { 0x01, 24, 192, 168, 42 }; > +const uint8_t cmp44[] = { 0x01, 20, 192, 168, 32 }; > + > +const uint8_t cmp61[] = { 0x01, 48, 0, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0xfe }; > +const uint8_t cmp62[] = { 0x01, 48, 8, 0x01, 0x0d, 0xb8, 0xc0, 0xfe }; > +const uint8_t cmp63[] = { 0x01, 40, 0, 0x20, 0x01, 0x0d, 0xb8, 0xc0 }; > +const uint8_t cmp64[] = { 0x01, 40, 0, 0x20, 0x01, 0x0d, 0xb8, 0xd0 }; > + > +static void > +test_flowspec_cmp(void) > +{ > + if (do_cmp(cmp1, sizeof(cmp1), cmp2, sizeof(cmp2), 0) != 0) > + errx(1, "cmp on type failed"); > + if (do_cmp(cmp3, sizeof(cmp3), cmp1, sizeof(cmp1), 0) != 0) > + errx(1, "cmp on more components failed"); > + if (do_cmp(cmp4, sizeof(cmp4), cmp5, sizeof(cmp5), 0) != 0) > + errx(1, "cmp on lowest common component failed"); > + if (do_cmp(cmp6, sizeof(cmp6), cmp5, sizeof(cmp5), 0) != 0) > + errx(1, "cmp on lowest common component failed"); > + if (do_cmp(cmp6, sizeof(cmp6), cmp4, sizeof(cmp4), 0) != 0) > + errx(1, "cmp on lowest common component failed"); > + > + if (do_cmp(cmp41, sizeof(cmp41), cmp42, sizeof(cmp42), 0) != 0) > + errx(1, "cmp 1 on prefix component failed"); > + if (do_cmp(cmp41, sizeof(cmp41), cmp43, sizeof(cmp43), 0) != 0) > + errx(1, "cmp 2 on prefix component failed"); > + if (do_cmp(cmp41, sizeof(cmp41), cmp44, sizeof(cmp44), 0) != 0) > + errx(1, "cmp 3 on prefix component failed"); > + if (do_cmp(cmp42, sizeof(cmp42), cmp43, sizeof(cmp43), 0) != 0) > + errx(1, "cmp 4 on prefix component failed"); > + if (do_cmp(cmp42, sizeof(cmp42), cmp44, sizeof(cmp44), 0) != 0) > + errx(1, "cmp 5 on prefix component failed"); > + if (do_cmp(cmp43, sizeof(cmp43), cmp44, sizeof(cmp44), 0) != 0) > + errx(1, "cmp 6 on prefix component failed"); > + > + if (do_cmp(cmp61, sizeof(cmp61), cmp62, sizeof(cmp62), 1) != 0) > + errx(1, "cmp 1 on inet6 prefix component failed"); > + if (do_cmp(cmp61, sizeof(cmp61), cmp63, sizeof(cmp63), 1) != 0) > + errx(1, "cmp 1 on inet6 prefix component failed"); > + if (do_cmp(cmp61, sizeof(cmp61), cmp64, sizeof(cmp64), 1) != 0) > + errx(1, "cmp 1 on inet6 prefix component failed"); > + if (do_cmp(cmp63, sizeof(cmp63), cmp64, sizeof(cmp64), 1) != 0) > + errx(1, "cmp 1 on inet6 prefix component failed"); > +} > + > +int > +main(int argc, char **argv) > +{ > + test_flowspec_valid(); > + test_flowspec_cmp(); > + printf("OK\n"); > + return 0; > +} > + > +__dead void > +fatalx(const char *emsg, ...) > +{ > + va_list ap; > + va_start(ap, emsg); > + verrx(2, emsg, ap); > +} > + >