1*e0c1f479Sclaudio /* $OpenBSD: flowspec.c,v 1.5 2023/10/23 13:07:44 claudio Exp $ */
20f144400Sclaudio
30f144400Sclaudio /*
40f144400Sclaudio * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
50f144400Sclaudio *
60f144400Sclaudio * Permission to use, copy, modify, and distribute this software for any
70f144400Sclaudio * purpose with or without fee is hereby granted, provided that the above
80f144400Sclaudio * copyright notice and this permission notice appear in all copies.
90f144400Sclaudio *
100f144400Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
110f144400Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
120f144400Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
130f144400Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
140f144400Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
150f144400Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
160f144400Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
170f144400Sclaudio */
180f144400Sclaudio
190f144400Sclaudio #include <string.h>
200f144400Sclaudio #include <stdio.h>
210f144400Sclaudio
220f144400Sclaudio #include "bgpd.h"
230f144400Sclaudio #include "rde.h"
240f144400Sclaudio
250f144400Sclaudio /*
260f144400Sclaudio * Extract the next component from a flowspec NLRI buffer.
270f144400Sclaudio * Returns the length of the component including type field or -1 on failure.
280f144400Sclaudio * Also checks that the prefix encoding is valid.
290f144400Sclaudio */
300f144400Sclaudio static int
flowspec_next_component(const uint8_t * buf,int len,int is_v6,int * type)310f144400Sclaudio flowspec_next_component(const uint8_t *buf, int len, int is_v6, int *type)
320f144400Sclaudio {
330f144400Sclaudio int vlen = 0;
340f144400Sclaudio uint8_t plen, off, op;
350f144400Sclaudio
360f144400Sclaudio *type = 0;
370f144400Sclaudio if (len < 1)
380f144400Sclaudio return -1;
390f144400Sclaudio *type = buf[vlen];
400f144400Sclaudio vlen++;
410f144400Sclaudio if (*type < FLOWSPEC_TYPE_MIN || *type >= FLOWSPEC_TYPE_MAX)
420f144400Sclaudio return -1;
430f144400Sclaudio
440f144400Sclaudio switch (*type) {
450f144400Sclaudio case FLOWSPEC_TYPE_DEST:
460f144400Sclaudio case FLOWSPEC_TYPE_SOURCE:
470f144400Sclaudio if (!is_v6) {
480f144400Sclaudio /* regular RFC 4271 encoding of prefixes */
490f144400Sclaudio if (len < vlen + 1)
500f144400Sclaudio return -1;
510f144400Sclaudio plen = buf[vlen];
520f144400Sclaudio vlen += PREFIX_SIZE(plen);
530f144400Sclaudio if (plen > 32 || len < vlen)
540f144400Sclaudio return -1;
550f144400Sclaudio } else {
560f144400Sclaudio /* special RFC 8956 encoding with extra offset */
570f144400Sclaudio if (len < vlen + 2)
580f144400Sclaudio return -1;
590f144400Sclaudio plen = buf[vlen];
600f144400Sclaudio off = buf[vlen + 1];
610f144400Sclaudio if (plen > 128 || off >= plen)
620f144400Sclaudio return -1;
630f144400Sclaudio vlen += PREFIX_SIZE(plen - off) + 1; /* off is extra */
640f144400Sclaudio if (len < vlen)
650f144400Sclaudio return -1;
660f144400Sclaudio }
670f144400Sclaudio break;
680f144400Sclaudio case FLOWSPEC_TYPE_FLOW:
690f144400Sclaudio if (!is_v6)
700f144400Sclaudio return -1;
710f144400Sclaudio /* FALLTHROUGH */
720f144400Sclaudio default:
730f144400Sclaudio do {
740f144400Sclaudio if (len < vlen + 1)
750f144400Sclaudio return -1;
760f144400Sclaudio op = buf[vlen];
770f144400Sclaudio /* first component cannot have and flag set */
780f144400Sclaudio if (vlen == 1 && op & FLOWSPEC_OP_AND)
790f144400Sclaudio return -1;
800f144400Sclaudio vlen += FLOWSPEC_OP_LEN(op) + 1;
810f144400Sclaudio
820f144400Sclaudio if (len < vlen)
830f144400Sclaudio return -1;
840f144400Sclaudio } while ((op & FLOWSPEC_OP_EOL) == 0);
850f144400Sclaudio break;
860f144400Sclaudio }
870f144400Sclaudio return vlen;
880f144400Sclaudio }
890f144400Sclaudio
900f144400Sclaudio #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
910f144400Sclaudio
920f144400Sclaudio /*
930f144400Sclaudio * Compare two IPv4 flowspec prefix components.
9401840ecfSclaudio * Returns -1 if first prefix is preferred, 1 if second, 0 if equal.
950f144400Sclaudio */
960f144400Sclaudio static int
flowspec_cmp_prefix4(const uint8_t * abuf,int ablen,const uint8_t * bbuf,int bblen)970f144400Sclaudio flowspec_cmp_prefix4(const uint8_t *abuf, int ablen, const uint8_t *bbuf,
980f144400Sclaudio int bblen)
990f144400Sclaudio {
1000f144400Sclaudio uint8_t a[4] = { 0 }, b[4] = { 0 };
1010f144400Sclaudio int alen, blen, clen, cmp;
1020f144400Sclaudio
1030f144400Sclaudio alen = abuf[1];
1040f144400Sclaudio blen = bbuf[1];
1050f144400Sclaudio clen = MINIMUM(alen, blen);
1060f144400Sclaudio
1070f144400Sclaudio /* only extract the common prefix */
108e7bb5ee4Sclaudio extract_prefix(abuf + 2, ablen - 2, &a, clen, sizeof(a));
109e7bb5ee4Sclaudio extract_prefix(bbuf + 2, bblen - 2, &b, clen, sizeof(b));
1100f144400Sclaudio
1110f144400Sclaudio /* lowest IP value has precedence */
1120f144400Sclaudio cmp = memcmp(a, b, sizeof(a));
1130f144400Sclaudio if (cmp < 0)
1140f144400Sclaudio return -1;
11501840ecfSclaudio if (cmp > 0)
11601840ecfSclaudio return 1;
1170f144400Sclaudio
1180f144400Sclaudio /* if common prefix, more specific route has precedence */
1190f144400Sclaudio if (alen > blen)
1200f144400Sclaudio return -1;
12101840ecfSclaudio if (alen < blen)
12201840ecfSclaudio return 1;
1230f144400Sclaudio return 0;
1240f144400Sclaudio }
1250f144400Sclaudio
1260f144400Sclaudio /*
1270f144400Sclaudio * Compare two IPv6 flowspec prefix components.
1280f144400Sclaudio * Returns 1 if first prefix is preferred, -1 if second, 0 if equal.
1290f144400Sclaudio * As usual the encoding of IPv6 addresses is extra complex.
1300f144400Sclaudio */
1310f144400Sclaudio static int
flowspec_cmp_prefix6(const uint8_t * abuf,int ablen,const uint8_t * bbuf,int bblen)1320f144400Sclaudio flowspec_cmp_prefix6(const uint8_t *abuf, int ablen, const uint8_t *bbuf,
1330f144400Sclaudio int bblen)
1340f144400Sclaudio {
1350f144400Sclaudio uint8_t a[16] = { 0 }, b[16] = { 0 };
1360f144400Sclaudio int alen, blen, clen, cmp;
1370f144400Sclaudio
1380f144400Sclaudio /* lowest offset has precedence */
1390f144400Sclaudio if (abuf[2] < bbuf[2])
1400f144400Sclaudio return -1;
14101840ecfSclaudio if (abuf[2] > bbuf[2])
14201840ecfSclaudio return 1;
1430f144400Sclaudio
1440f144400Sclaudio /* calculate actual pattern size (len - offset) */
1450f144400Sclaudio alen = abuf[1] - abuf[2];
1460f144400Sclaudio blen = bbuf[1] - bbuf[2];
1470f144400Sclaudio clen = MINIMUM(alen, blen);
1480f144400Sclaudio
1490f144400Sclaudio /* only extract the common prefix */
150e7bb5ee4Sclaudio extract_prefix(abuf + 3, ablen - 3, &a, clen, sizeof(a));
151e7bb5ee4Sclaudio extract_prefix(bbuf + 3, bblen - 3, &b, clen, sizeof(b));
1520f144400Sclaudio
1530f144400Sclaudio /* lowest IP value has precedence */
1540f144400Sclaudio cmp = memcmp(a, b, sizeof(a));
1550f144400Sclaudio if (cmp < 0)
1560f144400Sclaudio return -1;
15701840ecfSclaudio if (cmp > 0)
15801840ecfSclaudio return 1;
1590f144400Sclaudio
1600f144400Sclaudio /* if common prefix, more specific route has precedence */
1610f144400Sclaudio if (alen > blen)
1620f144400Sclaudio return -1;
16301840ecfSclaudio if (alen < blen)
16401840ecfSclaudio return 1;
1650f144400Sclaudio return 0;
1660f144400Sclaudio }
1670f144400Sclaudio
1680f144400Sclaudio /*
1690f144400Sclaudio * Check if the flowspec NLRI is syntactically valid.
1700f144400Sclaudio */
1710f144400Sclaudio int
flowspec_valid(const uint8_t * buf,int len,int is_v6)1720f144400Sclaudio flowspec_valid(const uint8_t *buf, int len, int is_v6)
1730f144400Sclaudio {
1740f144400Sclaudio int l, type, prev = 0;
1750f144400Sclaudio
1760f144400Sclaudio /* empty NLRI is invalid */
1770f144400Sclaudio if (len == 0)
1780f144400Sclaudio return -1;
1790f144400Sclaudio
1800f144400Sclaudio while (len > 0) {
1810f144400Sclaudio l = flowspec_next_component(buf, len, is_v6, &type);
1820f144400Sclaudio if (l == -1)
1830f144400Sclaudio return -1;
1840f144400Sclaudio /* ensure that types are ordered */
1850f144400Sclaudio if (prev >= type)
1860f144400Sclaudio return -1;
1870f144400Sclaudio prev = type;
1880f144400Sclaudio buf += l;
1890f144400Sclaudio len -= l;
1900f144400Sclaudio }
1910f144400Sclaudio if (len < 0)
192e7bb5ee4Sclaudio return -1;
1930f144400Sclaudio return 0;
1940f144400Sclaudio }
1950f144400Sclaudio
1960f144400Sclaudio /*
1970f144400Sclaudio * Compare two valid flowspec NLRI objects according to RFC 8955 & 8956.
19801840ecfSclaudio * Returns -1 if the first object has preference, 1 if not, and 0 if the
1990f144400Sclaudio * two objects are equal.
2000f144400Sclaudio */
2010f144400Sclaudio int
flowspec_cmp(const uint8_t * a,int alen,const uint8_t * b,int blen,int is_v6)2020f144400Sclaudio flowspec_cmp(const uint8_t *a, int alen, const uint8_t *b, int blen, int is_v6)
2030f144400Sclaudio {
2040f144400Sclaudio int atype, btype;
2050f144400Sclaudio int acomplen, bcomplen;
2060f144400Sclaudio int cmp;
2070f144400Sclaudio
2080f144400Sclaudio while (alen > 0 && blen > 0) {
2090f144400Sclaudio acomplen = flowspec_next_component(a, alen, is_v6, &atype);
2100f144400Sclaudio bcomplen = flowspec_next_component(b, blen, is_v6, &btype);
211e7bb5ee4Sclaudio /* should not happen */
2120f144400Sclaudio if (acomplen == -1)
213e7bb5ee4Sclaudio return 1;
214e7bb5ee4Sclaudio if (bcomplen == -1)
215e7bb5ee4Sclaudio return -1;
2160f144400Sclaudio
2170f144400Sclaudio /* If types differ, lowest type wins. */
2180f144400Sclaudio if (atype < btype)
2190f144400Sclaudio return -1;
22001840ecfSclaudio if (atype > btype)
22101840ecfSclaudio return 1;
2220f144400Sclaudio
2230f144400Sclaudio switch (atype) {
2240f144400Sclaudio case FLOWSPEC_TYPE_DEST:
2250f144400Sclaudio case FLOWSPEC_TYPE_SOURCE:
2260f144400Sclaudio if (!is_v6) {
2270f144400Sclaudio cmp = flowspec_cmp_prefix4(a, acomplen,
2280f144400Sclaudio b, bcomplen);
2290f144400Sclaudio } else {
2300f144400Sclaudio cmp = flowspec_cmp_prefix6(a, acomplen,
2310f144400Sclaudio b, bcomplen);
2320f144400Sclaudio }
2330f144400Sclaudio if (cmp != 0)
2340f144400Sclaudio return cmp;
2350f144400Sclaudio break;
2360f144400Sclaudio default:
2370f144400Sclaudio cmp = memcmp(a, b, MINIMUM(acomplen, bcomplen));
2380f144400Sclaudio /*
2390f144400Sclaudio * Lowest common component prefix wins also
2400f144400Sclaudio * if both commponents are same length also lowest
2410f144400Sclaudio * string has precedence.
2420f144400Sclaudio */
2430f144400Sclaudio if (cmp < 0)
2440f144400Sclaudio return -1;
24501840ecfSclaudio if (cmp > 0)
24601840ecfSclaudio return 1;
2470f144400Sclaudio /*
2480f144400Sclaudio * Longest component wins when common prefix is equal.
2490f144400Sclaudio * This is not really possible because EOL encoding will
2500f144400Sclaudio * always tie break on the memcmp but the RFC mandates
2510f144400Sclaudio * it (and it is cheap).
2520f144400Sclaudio */
2530f144400Sclaudio if (acomplen > bcomplen)
2540f144400Sclaudio return -1;
25501840ecfSclaudio if (acomplen < bcomplen)
25601840ecfSclaudio return 1;
2570f144400Sclaudio break;
2580f144400Sclaudio }
2590f144400Sclaudio a += acomplen;
2600f144400Sclaudio alen -= acomplen;
2610f144400Sclaudio b += bcomplen;
2620f144400Sclaudio blen -= bcomplen;
2630f144400Sclaudio
2640f144400Sclaudio /* Rule with more components wins */
2650f144400Sclaudio if (alen > 0 && blen <= 0)
2660f144400Sclaudio return -1;
26701840ecfSclaudio if (alen <= 0 && blen > 0)
26801840ecfSclaudio return 1;
2690f144400Sclaudio }
2700f144400Sclaudio return 0;
2710f144400Sclaudio }
2720f144400Sclaudio
2730f144400Sclaudio static void
shift_right(uint8_t * dst,const uint8_t * src,int off,int len)2740f144400Sclaudio shift_right(uint8_t *dst, const uint8_t *src, int off, int len)
2750f144400Sclaudio {
2760f144400Sclaudio uint8_t carry = 0;
2770f144400Sclaudio int i;
2780f144400Sclaudio
2790f144400Sclaudio dst += off / 8; /* go to inital start point */
2800f144400Sclaudio off %= 8;
2810f144400Sclaudio len = (len + 7) / 8; /* how much to copy in bytes */
2820f144400Sclaudio
2830f144400Sclaudio for (i = 0; i < len; i++) {
2840f144400Sclaudio dst[i] = carry | src[i] >> off;
2850f144400Sclaudio if (off != 0)
2860f144400Sclaudio carry = src[i] << (8 - off);
2870f144400Sclaudio }
2880f144400Sclaudio dst[i] = carry;
2890f144400Sclaudio }
2900f144400Sclaudio
2910f144400Sclaudio /*
2920f144400Sclaudio * Extract a flowspec component and return its buffer and size.
2930f144400Sclaudio * Returns 1 on success, 0 if component is not present and -1 on error.
2940f144400Sclaudio */
2950f144400Sclaudio int
flowspec_get_component(const uint8_t * flow,int flowlen,int type,int is_v6,const uint8_t ** buf,int * len)2960f144400Sclaudio flowspec_get_component(const uint8_t *flow, int flowlen, int type, int is_v6,
2970f144400Sclaudio const uint8_t **buf, int *len)
2980f144400Sclaudio {
2990f144400Sclaudio int complen, t;
3000f144400Sclaudio
3010f144400Sclaudio *buf = NULL;
3020f144400Sclaudio *len = 0;
3030f144400Sclaudio
3040f144400Sclaudio do {
3050f144400Sclaudio complen = flowspec_next_component(flow, flowlen, is_v6, &t);
3060f144400Sclaudio if (complen == -1)
3070f144400Sclaudio return -1;
3080f144400Sclaudio if (type == t)
3090f144400Sclaudio break;
3100f144400Sclaudio if (type < t)
3110f144400Sclaudio return 0;
3120f144400Sclaudio
3130f144400Sclaudio flow += complen;
3140f144400Sclaudio flowlen -= complen;
3150f144400Sclaudio } while (1);
3160f144400Sclaudio
3170f144400Sclaudio *buf = flow + 1;
3180f144400Sclaudio *len = complen - 1;
3190f144400Sclaudio
3200f144400Sclaudio return 1;
3210f144400Sclaudio }
3220f144400Sclaudio
3230f144400Sclaudio /*
3240f144400Sclaudio * Extract source or destination address into provided bgpd_addr.
3250f144400Sclaudio * Returns 1 on success, 0 if no address was present, -1 on error.
3260f144400Sclaudio * Sets plen to the prefix len and olen to the offset for IPv6 case.
3270f144400Sclaudio * If olen is set to NULL when an IPv6 prefix with offset is fetched
3280f144400Sclaudio * the function will return -1.
3290f144400Sclaudio */
3300f144400Sclaudio 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)3310f144400Sclaudio flowspec_get_addr(const uint8_t *flow, int flowlen, int type, int is_v6,
3320f144400Sclaudio struct bgpd_addr *addr, uint8_t *plen, uint8_t *olen)
3330f144400Sclaudio {
3340f144400Sclaudio const uint8_t *comp;
3350f144400Sclaudio int complen, rv;
3360f144400Sclaudio
3370f144400Sclaudio memset(addr, 0, sizeof(*addr));
3380f144400Sclaudio *plen = 0;
3390f144400Sclaudio if (olen != NULL)
3400f144400Sclaudio *olen = 0;
3410f144400Sclaudio
3420f144400Sclaudio rv = flowspec_get_component(flow, flowlen, type, is_v6,
3430f144400Sclaudio &comp, &complen);
3440f144400Sclaudio if (rv != 1)
3450f144400Sclaudio return rv;
3460f144400Sclaudio
3470f144400Sclaudio /* flowspec_get_component only returns valid encodings */
3480f144400Sclaudio if (!is_v6) {
3490f144400Sclaudio addr->aid = AID_INET;
3500f144400Sclaudio if (extract_prefix(comp + 1, complen - 1, &addr->v4, comp[0],
3510f144400Sclaudio sizeof(addr->v4)) == -1)
3520f144400Sclaudio return -1;
3530f144400Sclaudio *plen = comp[0];
3540f144400Sclaudio } else {
3550f144400Sclaudio uint8_t buf[16] = { 0 };
3560f144400Sclaudio int xlen, xoff = 0;
3570f144400Sclaudio
3580f144400Sclaudio addr->aid = AID_INET6;
3590f144400Sclaudio xlen = comp[0];
3600f144400Sclaudio if (comp[1] != 0) {
3610f144400Sclaudio if (olen == NULL)
3620f144400Sclaudio return -1;
3630f144400Sclaudio xoff = comp[1];
3640f144400Sclaudio xlen -= xoff;
3650f144400Sclaudio }
3660f144400Sclaudio if (extract_prefix(comp + 2, complen - 2, buf, xlen,
3670f144400Sclaudio sizeof(buf)) == -1)
3680f144400Sclaudio return -1;
369*e0c1f479Sclaudio shift_right(addr->v6.s6_addr, buf, xoff, xlen);
3700f144400Sclaudio *plen = comp[0];
3710f144400Sclaudio if (olen != NULL)
3720f144400Sclaudio *olen = comp[1];
3730f144400Sclaudio }
3740f144400Sclaudio
3750f144400Sclaudio return 1;
3760f144400Sclaudio }
3770f144400Sclaudio
3780f144400Sclaudio const char *
flowspec_fmt_label(int type)3790f144400Sclaudio flowspec_fmt_label(int type)
3800f144400Sclaudio {
3810f144400Sclaudio switch (type) {
3820f144400Sclaudio case FLOWSPEC_TYPE_DEST:
3830f144400Sclaudio return "to";
3840f144400Sclaudio case FLOWSPEC_TYPE_SOURCE:
3850f144400Sclaudio return "from";
3860f144400Sclaudio case FLOWSPEC_TYPE_PROTO:
3870f144400Sclaudio return "proto";
3880f144400Sclaudio case FLOWSPEC_TYPE_PORT:
3890f144400Sclaudio case FLOWSPEC_TYPE_DST_PORT:
3900f144400Sclaudio case FLOWSPEC_TYPE_SRC_PORT:
3910f144400Sclaudio return "port";
3920f144400Sclaudio case FLOWSPEC_TYPE_ICMP_TYPE:
3930f144400Sclaudio return "icmp-type";
3940f144400Sclaudio case FLOWSPEC_TYPE_ICMP_CODE:
3950f144400Sclaudio return "icmp-code";
3960f144400Sclaudio case FLOWSPEC_TYPE_TCP_FLAGS:
3970f144400Sclaudio return "flags";
3980f144400Sclaudio case FLOWSPEC_TYPE_PKT_LEN:
3990f144400Sclaudio return "length";
4000f144400Sclaudio case FLOWSPEC_TYPE_DSCP:
4010f144400Sclaudio return "tos";
4020f144400Sclaudio case FLOWSPEC_TYPE_FRAG:
4030f144400Sclaudio return "fragment";
4040f144400Sclaudio case FLOWSPEC_TYPE_FLOW:
4050f144400Sclaudio return "flow";
4060f144400Sclaudio default:
4070f144400Sclaudio return "???";
4080f144400Sclaudio }
4090f144400Sclaudio }
4100f144400Sclaudio
4110f144400Sclaudio static uint64_t
extract_val(const uint8_t * comp,int len)4120f144400Sclaudio extract_val(const uint8_t *comp, int len)
4130f144400Sclaudio {
4140f144400Sclaudio uint64_t val = 0;
4150f144400Sclaudio
4160f144400Sclaudio while (len-- > 0) {
4170f144400Sclaudio val <<= 8;
4180f144400Sclaudio val |= *comp++;
4190f144400Sclaudio }
4200f144400Sclaudio return val;
4210f144400Sclaudio }
4220f144400Sclaudio
4230f144400Sclaudio const char *
flowspec_fmt_num_op(const uint8_t * comp,int complen,int * off)4240f144400Sclaudio flowspec_fmt_num_op(const uint8_t *comp, int complen, int *off)
4250f144400Sclaudio {
4260f144400Sclaudio static char buf[32];
4270f144400Sclaudio uint64_t val, val2 = 0;
4280f144400Sclaudio uint8_t op, op2 = 0;
4290f144400Sclaudio int len, len2 = 0;
4300f144400Sclaudio
4310f144400Sclaudio if (*off == -1)
4320f144400Sclaudio return "";
4330f144400Sclaudio if (complen < *off + 1)
4340f144400Sclaudio return "bad encoding";
4350f144400Sclaudio
4360f144400Sclaudio op = comp[*off];
4370f144400Sclaudio len = FLOWSPEC_OP_LEN(op) + 1;
4380f144400Sclaudio if (complen < *off + len)
4390f144400Sclaudio return "bad encoding";
4400f144400Sclaudio val = extract_val(comp + *off + 1, FLOWSPEC_OP_LEN(op));
4410f144400Sclaudio
4420f144400Sclaudio if ((op & FLOWSPEC_OP_EOL) == 0) {
4430f144400Sclaudio if (complen < *off + len + 1)
4440f144400Sclaudio return "bad encoding";
4450f144400Sclaudio op2 = comp[*off + len];
4460f144400Sclaudio /*
4470f144400Sclaudio * Check if this is a range specification else fall back
4480f144400Sclaudio * to basic rules.
4490f144400Sclaudio */
4500f144400Sclaudio if (op2 & FLOWSPEC_OP_AND &&
4510f144400Sclaudio (op & FLOWSPEC_OP_NUM_MASK) == FLOWSPEC_OP_NUM_GE &&
4520f144400Sclaudio (op2 & FLOWSPEC_OP_NUM_MASK) == FLOWSPEC_OP_NUM_LE) {
4530f144400Sclaudio len2 = FLOWSPEC_OP_LEN(op2) + 1;
4540f144400Sclaudio val2 = extract_val(comp + *off + len + 1,
4550f144400Sclaudio FLOWSPEC_OP_LEN(op2));
4560f144400Sclaudio } else
4570f144400Sclaudio op2 = 0;
4580f144400Sclaudio }
4590f144400Sclaudio
4600f144400Sclaudio if (op2 & FLOWSPEC_OP_AND) {
4610f144400Sclaudio /* binary range operation */
4620f144400Sclaudio snprintf(buf, sizeof(buf), "%llu - %llu",
4630f144400Sclaudio (unsigned long long)val, (unsigned long long)val2);
4640f144400Sclaudio } else {
4650f144400Sclaudio /* unary operation */
4660f144400Sclaudio switch (op & FLOWSPEC_OP_NUM_MASK) {
4670f144400Sclaudio case 0:
4680f144400Sclaudio snprintf(buf, sizeof(buf), "%sfalse",
4690f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "");
4700f144400Sclaudio break;
4710f144400Sclaudio case FLOWSPEC_OP_NUM_EQ:
4720f144400Sclaudio snprintf(buf, sizeof(buf), "%s%llu",
4730f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "",
4740f144400Sclaudio (unsigned long long)val);
4750f144400Sclaudio break;
4760f144400Sclaudio case FLOWSPEC_OP_NUM_GT:
4770f144400Sclaudio snprintf(buf, sizeof(buf), "%s> %llu",
4780f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "",
4790f144400Sclaudio (unsigned long long)val);
4800f144400Sclaudio break;
4810f144400Sclaudio case FLOWSPEC_OP_NUM_GE:
4820f144400Sclaudio snprintf(buf, sizeof(buf), "%s>= %llu",
4830f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "",
4840f144400Sclaudio (unsigned long long)val);
4850f144400Sclaudio break;
4860f144400Sclaudio case FLOWSPEC_OP_NUM_LT:
4870f144400Sclaudio snprintf(buf, sizeof(buf), "%s< %llu",
4880f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "",
4890f144400Sclaudio (unsigned long long)val);
4900f144400Sclaudio break;
4910f144400Sclaudio case FLOWSPEC_OP_NUM_LE:
4920f144400Sclaudio snprintf(buf, sizeof(buf), "%s<= %llu",
4930f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "",
4940f144400Sclaudio (unsigned long long)val);
4950f144400Sclaudio break;
4960f144400Sclaudio case FLOWSPEC_OP_NUM_NOT:
4970f144400Sclaudio snprintf(buf, sizeof(buf), "%s!= %llu",
4980f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "",
4990f144400Sclaudio (unsigned long long)val);
5000f144400Sclaudio break;
5010f144400Sclaudio case 0x7:
5020f144400Sclaudio snprintf(buf, sizeof(buf), "%strue",
5030f144400Sclaudio op & FLOWSPEC_OP_AND ? "&& " : "");
5040f144400Sclaudio break;
5050f144400Sclaudio }
5060f144400Sclaudio }
5070f144400Sclaudio
5080f144400Sclaudio if (op2 & FLOWSPEC_OP_EOL || op & FLOWSPEC_OP_EOL)
5090f144400Sclaudio *off = -1;
5100f144400Sclaudio else
5110f144400Sclaudio *off += len + len2;
5120f144400Sclaudio
5130f144400Sclaudio return buf;
5140f144400Sclaudio }
5150f144400Sclaudio
5160f144400Sclaudio static const char *
fmt_flags(uint64_t val,const char * bits,char * buf,size_t blen)5170f144400Sclaudio fmt_flags(uint64_t val, const char *bits, char *buf, size_t blen)
5180f144400Sclaudio {
5190f144400Sclaudio int i, bi;
5200f144400Sclaudio
5210f144400Sclaudio for (i = 0, bi = 0; i < 64 && val != 0; i++) {
5220f144400Sclaudio if (val & 1) {
5230f144400Sclaudio if (bits[i] == '\0' || bits[i] == ' ')
5240f144400Sclaudio goto fail;
5250f144400Sclaudio buf[bi++] = bits[i];
5260f144400Sclaudio }
5270f144400Sclaudio val >>= 1;
5280f144400Sclaudio }
5290f144400Sclaudio buf[bi++] = '\0';
5300f144400Sclaudio return buf;
5310f144400Sclaudio
5320f144400Sclaudio fail:
5330f144400Sclaudio snprintf(buf, blen, "%llx", (unsigned long long)val);
5340f144400Sclaudio return buf;
5350f144400Sclaudio }
5360f144400Sclaudio
5370f144400Sclaudio const char *
flowspec_fmt_bin_op(const uint8_t * comp,int complen,int * off,const char * bits)5380f144400Sclaudio flowspec_fmt_bin_op(const uint8_t *comp, int complen, int *off,
5390f144400Sclaudio const char *bits)
5400f144400Sclaudio {
5410f144400Sclaudio static char buf[36], bit[17], mask[17];
5420f144400Sclaudio uint64_t val, val2 = 0;
5430f144400Sclaudio uint8_t op, op2 = 0;
5440f144400Sclaudio int len, len2 = 0;
5450f144400Sclaudio
5460f144400Sclaudio if (*off == -1)
5470f144400Sclaudio return "";
5480f144400Sclaudio if (complen < *off + 1)
5490f144400Sclaudio return "bad encoding";
5500f144400Sclaudio
5510f144400Sclaudio op = comp[*off];
5520f144400Sclaudio len = FLOWSPEC_OP_LEN(op) + 1;
5530f144400Sclaudio if (complen < *off + len)
5540f144400Sclaudio return "bad encoding";
5550f144400Sclaudio val = extract_val(comp + *off + 1, FLOWSPEC_OP_LEN(op));
5560f144400Sclaudio
5570f144400Sclaudio if ((op & FLOWSPEC_OP_EOL) == 0) {
5580f144400Sclaudio if (complen < *off + len + 1)
5590f144400Sclaudio return "bad encoding";
5600f144400Sclaudio op2 = comp[*off + len];
5610f144400Sclaudio /*
5620f144400Sclaudio * Check if this is a mask specification else fall back
5630f144400Sclaudio * to basic rules.
5640f144400Sclaudio */
5650f144400Sclaudio if (op2 & FLOWSPEC_OP_AND &&
5660f144400Sclaudio (op & FLOWSPEC_OP_BIT_MASK) == FLOWSPEC_OP_BIT_MATCH &&
5670f144400Sclaudio (op2 & FLOWSPEC_OP_BIT_MASK) == FLOWSPEC_OP_BIT_NOT) {
5680f144400Sclaudio len2 = FLOWSPEC_OP_LEN(op2) + 1;
5690f144400Sclaudio val2 = extract_val(comp + *off + len + 1,
5700f144400Sclaudio FLOWSPEC_OP_LEN(op2));
5710f144400Sclaudio } else
5720f144400Sclaudio op2 = 0;
5730f144400Sclaudio }
5740f144400Sclaudio
5750f144400Sclaudio if (op2 & FLOWSPEC_OP_AND) {
5760f144400Sclaudio val2 |= val;
5770f144400Sclaudio snprintf(buf, sizeof(buf), "%s / %s",
5780f144400Sclaudio fmt_flags(val, bits, bit, sizeof(bit)),
5790f144400Sclaudio fmt_flags(val2, bits, mask, sizeof(mask)));
5800f144400Sclaudio } else {
5810f144400Sclaudio switch (op & FLOWSPEC_OP_BIT_MASK) {
5820f144400Sclaudio case 0:
5830f144400Sclaudio snprintf(buf, sizeof(buf), "%s",
5840f144400Sclaudio fmt_flags(val, bits, bit, sizeof(bit)));
5850f144400Sclaudio break;
5860f144400Sclaudio case FLOWSPEC_OP_BIT_NOT:
5870f144400Sclaudio snprintf(buf, sizeof(buf), "/ %s",
5880f144400Sclaudio fmt_flags(val, bits, mask, sizeof(mask)));
5890f144400Sclaudio break;
5900f144400Sclaudio case FLOWSPEC_OP_BIT_MATCH:
5910f144400Sclaudio snprintf(buf, sizeof(buf), "%s / %s",
5920f144400Sclaudio fmt_flags(val, bits, bit, sizeof(bit)),
5930f144400Sclaudio fmt_flags(val, bits, mask, sizeof(mask)));
5940f144400Sclaudio break;
5950f144400Sclaudio case FLOWSPEC_OP_BIT_NOT | FLOWSPEC_OP_BIT_MATCH:
5960f144400Sclaudio snprintf(buf, sizeof(buf), "???");
5970f144400Sclaudio break;
5980f144400Sclaudio }
5990f144400Sclaudio }
6000f144400Sclaudio
6010f144400Sclaudio if (op2 & FLOWSPEC_OP_EOL || op & FLOWSPEC_OP_EOL)
6020f144400Sclaudio *off = -1;
6030f144400Sclaudio else
6040f144400Sclaudio *off += len + len2;
6050f144400Sclaudio
6060f144400Sclaudio return buf;
6070f144400Sclaudio }
608