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 <[email protected]>
> + *
> + * 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 <[email protected]>
> + *
> + * 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);
> +}
> +
>