Index | Thread | Search

From:
Claudio Jeker <cjeker@diehard.n-r-g.com>
Subject:
bgpd: limit flowspec size
To:
tech@openbsd.org
Date:
Mon, 18 May 2026 14:47:10 +0200

Download raw body.

Thread
Add a maximum size for a single flowspec rule. Currently 4000 bytes.
Enforce this in both in the parsers but also in the RDE.
In the RDE flowspec_valid() will error out if the lenght is too long
but also pt_get_flow() and pt_add_flow() will error out.
The fixed buffer in pt_get_flow() is now sized appropriately instead
of using a arbitrary size.

A size of 4000 is very luxurious and close to the max for regular BGP
sessions.
-- 
:wq Claudio

Index: bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
diff -u -p -r1.320 bgpctl.c
--- bgpctl/bgpctl.c	4 Feb 2026 11:48:33 -0000	1.320
+++ bgpctl/bgpctl.c	18 May 2026 12:39:00 -0000
@@ -1971,6 +1971,8 @@ res_to_flowspec(struct parse_result *r)
 
 	if (len == 0)
 		errx(1, "no flowspec rule defined");
+	if (len > FLOWSPEC_SIZE_MAX)
+		errx(1, "flowspec rule too long");
 
 	f = malloc(FLOWSPEC_SIZE + len);
 	if (f == NULL)
Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
diff -u -p -r1.542 bgpd.h
--- bgpd/bgpd.h	12 May 2026 09:12:49 -0000	1.542
+++ bgpd/bgpd.h	18 May 2026 12:35:31 -0000
@@ -594,6 +594,7 @@ struct flowspec {
 	uint8_t			data[1];
 };
 #define FLOWSPEC_SIZE	(offsetof(struct flowspec, data))
+#define FLOWSPEC_SIZE_MAX	4000
 
 struct flowspec_config {
 	RB_ENTRY(flowspec_config)	 entry;
Index: bgpd/flowspec.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/flowspec.c,v
diff -u -p -r1.5 flowspec.c
--- bgpd/flowspec.c	23 Oct 2023 13:07:44 -0000	1.5
+++ bgpd/flowspec.c	18 May 2026 12:35:31 -0000
@@ -177,6 +177,10 @@ flowspec_valid(const uint8_t *buf, int l
 	if (len == 0)
 		return -1;
 
+	/* flowspec rule is too large */
+	if (len > FLOWSPEC_SIZE_MAX)
+		return -1;
+
 	while (len > 0) {
 		l = flowspec_next_component(buf, len, is_v6, &type);
 		if (l == -1)
Index: bgpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
diff -u -p -r1.496 parse.y
--- bgpd/parse.y	13 May 2026 09:25:11 -0000	1.496
+++ bgpd/parse.y	18 May 2026 12:37:40 -0000
@@ -1201,7 +1201,6 @@ flowspec	: FLOWSPEC af {
 
 			f = flow_to_flowspec(curflow);
 			if (f == NULL) {
-				yyerror("out of memory");
 				free($5);
 				flow_free(curflow);
 				curflow = NULL;
@@ -5688,6 +5687,7 @@ flow_to_flowspec(struct flowspec_context
 		aid = AID_FLOWSPECv6;
 		break;
 	default:
+		yyerror("unknown AID %d", ctx->aid);
 		return NULL;
 	}
 
@@ -5695,9 +5695,16 @@ flow_to_flowspec(struct flowspec_context
 		if (ctx->components[i] != NULL)
 			len += ctx->complen[i] + 1;
 
+	if (len > FLOWSPEC_SIZE_MAX) {
+		yyerror("flowspec to long %d > %d", len, FLOWSPEC_SIZE_MAX);
+		return NULL;
+	}
+
 	f = flowspec_alloc(aid, len);
-	if (f == NULL)
+	if (f == NULL) {
+		yyerror("out of memory");
 		return NULL;
+	}
 
 	len = 0;
 	for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++)
Index: bgpd/rde_prefix.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_prefix.c,v
diff -u -p -r1.61 rde_prefix.c
--- bgpd/rde_prefix.c	13 May 2026 15:12:14 -0000	1.61
+++ bgpd/rde_prefix.c	18 May 2026 12:35:31 -0000
@@ -405,11 +405,16 @@ pt_get_flow(struct flowspec *f)
 	struct pt_entry *needle;
 	union {
 		struct pt_entry_flow	flow;
-		uint8_t			buf[4096];
+		uint8_t			buf[FLOWSPEC_SIZE_MAX + PT_FLOW_SIZE];
 	} x;
 
 	needle = (struct pt_entry *)&x.flow;
 
+	if (f->len >  FLOWSPEC_SIZE_MAX) {
+		log_warnx("%s: flowspec too long", __func__);
+		return NULL;
+	}
+
 	memset(needle, 0, PT_FLOW_SIZE);
 	needle->aid = f->aid;
 	needle->len = f->len + PT_FLOW_SIZE;
@@ -423,6 +428,9 @@ pt_add_flow(struct flowspec *f)
 {
 	struct pt_entry *p;
 	int len = f->len + PT_FLOW_SIZE;
+
+	if (f->len > FLOWSPEC_SIZE_MAX)
+		fatalx("%s: flowspec too long", __func__);
 
 	p = malloc(len);
 	if (p == NULL)