xref: /openbsd-src/usr.sbin/bgpd/flowspec.c (revision e0c1f4798a44fdb15cd95a1654e82d7569139c58)
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