DORMANTno replies

[PATCH] bridge: tighten VLAN parsing

From: William Gonzalez <hidden>
Date: 2026-08-01 02:40:57
Subsystem: library code, the rest · Maintainers: Andrew Morton, Linus Torvalds

---
 bridge/fdb.c    |  45 +++++++++++--------
 bridge/mdb.c    |  21 ++++++---
 bridge/vlan.c   | 113 ++++++++++++++++++------------------------------
 include/utils.h |   2 +
 lib/utils.c     |  51 ++++++++++++++++++++++
 5 files changed, 136 insertions(+), 96 deletions(-)
diff --git a/bridge/fdb.c b/bridge/fdb.c
index 05f093b5..fadd82ae 100644
--- a/bridge/fdb.c
+++ b/bridge/fdb.c
@@ -364,10 +364,14 @@ static int fdb_show(int argc, char **argv)
 			NEXT_ARG();
 			br = *argv;
 		} else if (strcmp(*argv, "vlan") == 0) {
+			__u16 vid;
+
 			NEXT_ARG();
 			if (filter_vlan)
 				duparg("vlan", *argv);
-			filter_vlan = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
+			filter_vlan = vid;
 		} else if (strcmp(*argv, "state") == 0) {
 			unsigned int state;
 
@@ -474,7 +478,8 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
 	unsigned long src_vni = ~0;
 	unsigned int via = 0;
 	char *endptr;
-	short vid = -1;
+	__u16 vid = 0;
+	bool vid_set = false;
 	__u32 nhid = 0;
 
 	while (argc > 0) {
@@ -537,10 +542,12 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
 			req.ndm.ndm_state |= NUD_REACHABLE;
 			req.ndm.ndm_state &= ~NUD_NOARP;
 		} else if (matches(*argv, "vlan") == 0) {
-			if (vid >= 0)
+			if (vid_set)
 				duparg2("vlan", *argv);
 			NEXT_ARG();
-			vid = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
+			vid_set = true;
 		} else if (matches(*argv, "use") == 0) {
 			req.ndm.ndm_flags |= NTF_USE;
 		} else if (matches(*argv, "extern_learn") == 0) {
@@ -595,7 +602,7 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
 	if (dst_ok)
 		addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
 
-	if (vid >= 0)
+	if (vid_set)
 		addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
 	if (nhid > 0)
 		addattr32(&req.n, sizeof(req), NDA_NH_ID, nhid);
@@ -644,7 +651,8 @@ static int fdb_get(int argc, char **argv)
 	char abuf[ETH_ALEN];
 	int br_ifindex = 0;
 	char *addr = NULL;
-	short vlan = -1;
+	__u16 vlan = 0;
+	bool vlan_set = false;
 	char *endptr;
 	int ret;
 
@@ -669,10 +677,12 @@ static int fdb_get(int argc, char **argv)
 		} else if (matches(*argv, "master") == 0) {
 			req.ndm.ndm_flags |= NTF_MASTER;
 		} else if (matches(*argv, "vlan") == 0) {
-			if (vlan >= 0)
+			if (vlan_set)
 				duparg2("vlan", *argv);
 			NEXT_ARG();
-			vlan = atoi(*argv);
+			if (get_vlan(&vlan, *argv))
+				invarg("invalid vlan", *argv);
+			vlan_set = true;
 		} else if (matches(*argv, "dynamic") == 0) {
 			filter_dynamic = 1;
 		} else {
@@ -702,7 +712,7 @@ static int fdb_get(int argc, char **argv)
 
 	addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
 
-	if (vlan >= 0)
+	if (vlan_set)
 		addattr16(&req.n, sizeof(req), NDA_VLAN, vlan);
 
 	if (vni != ~0)
@@ -758,7 +768,9 @@ static int fdb_flush(int argc, char **argv)
 	};
 	unsigned short ndm_state_mask = 0;
 	unsigned short ndm_flags_mask = 0;
-	short vid = -1, brport_ifidx = -1;
+	short brport_ifidx = -1;
+	__u16 vid = 0;
+	bool vid_set = false;
 	char *d = NULL, *brport = NULL;
 	unsigned short ndm_flags = 0;
 	unsigned short ndm_state = 0;
@@ -832,10 +844,12 @@ static int fdb_flush(int argc, char **argv)
 			NEXT_ARG();
 			brport = *argv;
 		} else if (strcmp(*argv, "vlan") == 0) {
-			if (vid >= 0)
+			if (vid_set)
 				duparg2("vlan", *argv);
 			NEXT_ARG();
-			vid = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
+			vid_set = true;
 		} else if (strcmp(*argv, "src_vni") == 0) {
 			NEXT_ARG();
 			src_vni = strtoul(*argv, &endptr, 0);
@@ -901,11 +915,6 @@ static int fdb_flush(int argc, char **argv)
 		}
 	}
 
-	if (vid >= 4096) {
-		fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
-		return -1;
-	}
-
 	/* if self and master were not specified assume self */
 	if (!(ndm_flags & (NTF_SELF | NTF_MASTER)))
 		ndm_flags |= NTF_SELF;
@@ -914,7 +923,7 @@ static int fdb_flush(int argc, char **argv)
 	req.ndm.ndm_state = ndm_state;
 	if (brport_ifidx > -1)
 		addattr32(&req.n, sizeof(req), NDA_IFINDEX, brport_ifidx);
-	if (vid > -1)
+	if (vid_set)
 		addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
 	if (src_vni != ~0)
 		addattr32(&req.n, sizeof(req), NDA_SRC_VNI, src_vni);
diff --git a/bridge/mdb.c b/bridge/mdb.c
index 112deeb9..b1d6ec28 100644
--- a/bridge/mdb.c
+++ b/bridge/mdb.c
@@ -452,10 +452,14 @@ static int mdb_show(int argc, char **argv)
 				duparg("dev", *argv);
 			filter_dev = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
+			__u16 vid;
+
 			NEXT_ARG();
 			if (filter_vlan)
 				duparg("vid", *argv);
-			filter_vlan = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
+			filter_vlan = vid;
 		}
 		argc--; argv++;
 	}
@@ -706,7 +710,7 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
 	char *src_list = NULL, *proto = NULL, *dst = NULL;
 	struct br_mdb_entry entry = {};
 	bool set_attrs = false;
-	short vid = 0;
+	__u16 vid = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -725,7 +729,8 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
 			;/* nothing */
 		} else if (strcmp(*argv, "vid") == 0) {
 			NEXT_ARG();
-			vid = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
 		} else if (strcmp(*argv, "src") == 0) {
 			NEXT_ARG();
 			src = *argv;
@@ -868,7 +873,7 @@ static int mdb_get(int argc, char **argv)
 	struct br_mdb_entry entry = {};
 	struct nlmsghdr *answer;
 	bool get_attrs = false;
-	short vid = 0;
+	__u16 vid = 0;
 	int ret = 0;
 
 	while (argc > 0) {
@@ -880,7 +885,8 @@ static int mdb_get(int argc, char **argv)
 			grp = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
 			NEXT_ARG();
-			vid = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
 		} else if (strcmp(*argv, "src") == 0) {
 			NEXT_ARG();
 			src = *argv;
@@ -963,7 +969,7 @@ static int mdb_flush(int argc, char **argv)
 	struct br_mdb_entry entry = {};
 	unsigned short state_mask = 0;
 	bool set_attrs = false;
-	short vid = 0;
+	__u16 vid = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -974,7 +980,8 @@ static int mdb_flush(int argc, char **argv)
 			p = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
 			NEXT_ARG();
-			vid = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
 		} else if (strcmp(*argv, "src_vni") == 0) {
 			NEXT_ARG();
 			src_vni = *argv;
diff --git a/bridge/vlan.c b/bridge/vlan.c
index 09c01153..0f0c8333 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -165,8 +165,10 @@ static int vlan_modify(int cmd, int argc, char **argv)
 		.ifm.ifi_family = PF_BRIDGE,
 	};
 	char *d = NULL;
-	short vid = -1;
-	short vid_end = -1;
+	__u16 vid = 0;
+	__u16 vid_end = 0;
+	bool vid_set = false;
+	bool vid_range = false;
 	struct rtattr *afspec;
 	struct bridge_vlan_info vinfo = {};
 	bool tunnel_info_set = false;
@@ -179,19 +181,15 @@ static int vlan_modify(int cmd, int argc, char **argv)
 			NEXT_ARG();
 			d = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
-			char *p;
-
 			NEXT_ARG();
-			p = strchr(*argv, '-');
-			if (p) {
-				*p = '\0';
-				p++;
-				vid = atoi(*argv);
-				vid_end = atoi(p);
+			if (vid_set)
+				duparg2("vid", *argv);
+			if (get_vlan_range(&vid, &vid_end, *argv))
+				invarg("invalid vlan", *argv);
+			vid_set = true;
+			vid_range = vid_end != vid;
+			if (vid_range)
 				vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
-			} else {
-				vid = atoi(*argv);
-			}
 		} else if (strcmp(*argv, "self") == 0) {
 			flags |= BRIDGE_FLAGS_SELF;
 		} else if (strcmp(*argv, "master") == 0) {
@@ -213,7 +211,7 @@ static int vlan_modify(int cmd, int argc, char **argv)
 		argc--; argv++;
 	}
 
-	if (d == NULL || vid == -1) {
+	if (d == NULL || !vid_set) {
 		fprintf(stderr, "Device and VLAN ID are required arguments.\n");
 		return -1;
 	}
@@ -224,13 +222,8 @@ static int vlan_modify(int cmd, int argc, char **argv)
 		return -1;
 	}
 
-	if (vid < 1 || vid > 4094) {
-		fprintf(stderr, "Invalid VLAN ID \"%d\"\n", vid);
-		return -1;
-	}
-
-	if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
-		if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) {
+	if (vid_range) {
+		if (vid_end <= vid) {
 			fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
 				vid, vid_end);
 			return -1;
@@ -248,10 +241,12 @@ static int vlan_modify(int cmd, int argc, char **argv)
 		addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
 
 	if (tunnel_info_set)
-		add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end,
+		add_tunnel_info_range(&req.n, sizeof(req), vid,
+				      vid_range ? vid_end : -1,
 				      tun_id_start, tun_id_end);
 	else
-		add_vlan_info_range(&req.n, sizeof(req), vid, vid_end,
+		add_vlan_info_range(&req.n, sizeof(req), vid,
+				    vid_range ? vid_end : -1,
 				    vinfo.flags);
 
 	addattr_nest_end(&req.n, afspec);
@@ -277,7 +272,7 @@ static int vlan_option_set(int argc, char **argv)
 	struct bridge_vlan_info vinfo = {};
 	struct rtattr *afspec;
 	char *d = NULL;
-	short vid = -1;
+	__u16 vid = 0;
 
 	afspec = addattr_nest(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY);
 	afspec->rta_type |= NLA_F_NESTED;
@@ -293,35 +288,19 @@ static int vlan_option_set(int argc, char **argv)
 				return -1;
 			}
 		} else if (strcmp(*argv, "vid") == 0) {
-			short vid_end = -1;
-			char *p;
+			__u16 vid_end = 0;
+			bool vid_range = false;
 
 			NEXT_ARG();
-			p = strchr(*argv, '-');
-			if (p) {
-				*p = '\0';
-				p++;
-				vid = atoi(*argv);
-				vid_end = atoi(p);
-				if (vid >= vid_end || vid_end >= 4096) {
-					fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
-						vid, vid_end);
-					return -1;
-				}
-			} else {
-				vid = atoi(*argv);
-			}
-			if (vid >= 4096) {
-				fprintf(stderr, "Invalid VLAN ID \"%hu\"\n",
-					vid);
-				return -1;
-			}
+			if (get_vlan_range(&vid, &vid_end, *argv))
+				invarg("invalid vlan", *argv);
+			vid_range = vid_end != vid;
 
 			vinfo.flags = BRIDGE_VLAN_INFO_ONLY_OPTS;
 			vinfo.vid = vid;
 			addattr_l(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_INFO,
 				  &vinfo, sizeof(vinfo));
-			if (vid_end != -1)
+			if (vid_range)
 				addattr16(&req.n, sizeof(req),
 					  BRIDGE_VLANDB_ENTRY_RANGE, vid_end);
 		} else if (strcmp(*argv, "state") == 0) {
@@ -400,9 +379,9 @@ static int vlan_global_option_set(int argc, char **argv)
 		.bvm.family = PF_BRIDGE,
 	};
 	struct rtattr *afspec;
-	short vid_end = -1;
+	__u16 vid_end = 0;
 	char *d = NULL;
-	short vid = -1;
+	__u16 vid = 0;
 	__u64 val64;
 	__u32 val32;
 	__u16 val16;
@@ -422,31 +401,15 @@ static int vlan_global_option_set(int argc, char **argv)
 				return -1;
 			}
 		} else if (strcmp(*argv, "vid") == 0) {
-			char *p;
+			bool vid_range = false;
 
 			NEXT_ARG();
-			p = strchr(*argv, '-');
-			if (p) {
-				*p = '\0';
-				p++;
-				vid = atoi(*argv);
-				vid_end = atoi(p);
-				if (vid >= vid_end || vid_end >= 4096) {
-					fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
-						vid, vid_end);
-					return -1;
-				}
-			} else {
-				vid = atoi(*argv);
-			}
-			if (vid >= 4096) {
-				fprintf(stderr, "Invalid VLAN ID \"%hu\"\n",
-					vid);
-				return -1;
-			}
+			if (get_vlan_range(&vid, &vid_end, *argv))
+				invarg("invalid vlan", *argv);
+			vid_range = vid_end != vid;
 			addattr16(&req.n, sizeof(req), BRIDGE_VLANDB_GOPTS_ID,
 				  vid);
-			if (vid_end != -1)
+			if (vid_range)
 				addattr16(&req.n, sizeof(req),
 					  BRIDGE_VLANDB_GOPTS_RANGE, vid_end);
 		} else if (strcmp(*argv, "mcast_snooping") == 0) {
@@ -1102,10 +1065,14 @@ static int vlan_show(int argc, char **argv, int subject)
 				duparg("dev", *argv);
 			filter_dev = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
+			__u16 vid;
+
 			NEXT_ARG();
 			if (filter_vlan)
 				duparg("vid", *argv);
-			filter_vlan = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
+			filter_vlan = vid;
 		}
 		argc--; argv++;
 	}
@@ -1219,10 +1186,14 @@ static int vlan_global_show(int argc, char **argv)
 				duparg("dev", *argv);
 			filter_dev = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
+			__u16 vid;
+
 			NEXT_ARG();
 			if (filter_vlan)
 				duparg("vid", *argv);
-			filter_vlan = atoi(*argv);
+			if (get_vlan(&vid, *argv))
+				invarg("invalid vlan", *argv);
+			filter_vlan = vid;
 		}
 		argc--; argv++;
 	}
diff --git a/include/utils.h b/include/utils.h
index e4e318e2..06d5242f 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -151,6 +151,8 @@ int get_u32(__u32 *val, const char *arg, int base);
 int get_s32(__s32 *val, const char *arg, int base);
 int get_u16(__u16 *val, const char *arg, int base);
 int get_u8(__u8 *val, const char *arg, int base);
+int get_vlan(__u16 *val, const char *arg);
+int get_vlan_range(__u16 *start, __u16 *end, const char *arg);
 int get_be64(__be64 *val, const char *arg, int base);
 int get_be32(__be32 *val, const char *arg, int base);
 int get_be16(__be16 *val, const char *arg, int base);
diff --git a/lib/utils.c b/lib/utils.c
index 1215fe31..debba751 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -387,6 +387,57 @@ int get_u16(__u16 *val, const char *arg, int base)
 	return 0;
 }
 
+int get_vlan(__u16 *val, const char *arg)
+{
+	if (get_u16(val, arg, 10))
+		return -1;
+
+	if (*val < 1 || *val > 4094)
+		return -1;
+
+	return 0;
+}
+
+int get_vlan_range(__u16 *start, __u16 *end, const char *arg)
+{
+	char *start_arg;
+	const char *sep;
+	size_t len;
+	int ret;
+
+	if (!arg)
+		return -1;
+
+	sep = strchr(arg, '-');
+	if (!sep) {
+		ret = get_vlan(start, arg);
+		if (!ret)
+			*end = *start;
+		return ret;
+	}
+
+	len = sep - arg;
+	if (!len || !sep[1])
+		return -1;
+
+	start_arg = malloc(len + 1);
+	if (!start_arg)
+		return -1;
+
+	memcpy(start_arg, arg, len);
+	start_arg[len] = '\0';
+
+	ret = get_vlan(start, start_arg) || get_vlan(end, sep + 1);
+	free(start_arg);
+	if (ret)
+		return -1;
+
+	if (*start >= *end)
+		return -1;
+
+	return 0;
+}
+
 int get_u8(__u8 *val, const char *arg, int base)
 {
 	unsigned long res;
-- 
2.39.3 (Apple Git-145)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help