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).

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.

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.
-- 
: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)
+#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.
+ * 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) {
+                       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;
+               }
+               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));
+       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));
+       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
+                        * 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;
+
+       while (len-- > 0) {
+               val <<= 8;
+               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",
+                           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);
+}
+

Reply via email to