diff mbox series

[iproute2,net-next,2/2] bridge: vlan: support for per vlan tunnel info

Message ID 1509045367-42793-3-git-send-email-roopa@cumulusnetworks.com
State Changes Requested, archived
Delegated to: stephen hemminger
Headers show
Series bridge: support for vlan to tunnel mapping | expand

Commit Message

Roopa Prabhu Oct. 26, 2017, 7:16 p.m. UTC
From: Roopa Prabhu <roopa@cumulusnetworks.com>

This patch uses kernel bridge vlan attribute
IFLA_BRIDGE_VLAN_TUNNEL_INFO to set/delete/show per vlan tunnel info.

$bridge vlan add dev vxlan0 vid 2000 tunnel_info id 2000
$bridge vlan add dev vxlan0 vid 1000-1001 tunnel_info id 2000-2001

$bridge vlan tunnelshow
port    vlan ids        tunnel id
vxlan0   1000-1001       1000-1001
         2000            2000

$bridge  -j vlan tunnelshow
{
    "dummy0": [],
    "dummy1": [],
    "bridge": [],
    "vxlan0": [{
            "vlan": 1000,
            "vlanEnd": 1001,
            "tunid": 1000,
            "tunidEnd": 1001
        },{
            "vlan": 2000,
            "tunid": 2000
        }
    ]
}

This patch also fixes a json termination bug in print_vlan
when filter vlan is provided by the user.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
 bridge/vlan.c     | 308 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 man/man8/bridge.8 |  14 ++-
 2 files changed, 293 insertions(+), 29 deletions(-)
diff mbox series

Patch

diff --git a/bridge/vlan.c b/bridge/vlan.c
index ebcdace..d5b8996 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -16,17 +16,113 @@ 
 
 static unsigned int filter_index, filter_vlan;
 static int last_ifidx = -1;
+static int show_vlan_tunnel_info = 0;
 
 json_writer_t *jw_global = NULL;
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid ] [ untagged ]\n");
+	fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n");
+	fprintf(stderr, "                                                     [ pvid ] [ untagged ]\n");
 	fprintf(stderr, "                                                     [ self ] [ master ]\n");
 	fprintf(stderr, "       bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n");
+	fprintf(stderr, "       bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
 	exit(-1);
 }
 
+static int parse_tunnel_info(int *argcp, char ***argvp, __u32 *tun_id_start,
+			     __u32 *tun_id_end)
+{
+	char **argv = *argvp;
+	int argc = *argcp;
+	char *t;
+
+	NEXT_ARG();
+	if (!matches(*argv, "id")) {
+		NEXT_ARG();
+		t = strchr(*argv, '-');
+		if (t) {
+			*t = '\0';
+			if (get_u32(tun_id_start, *argv, 0) ||
+				    *tun_id_start >= 1u << 24)
+				invarg("invalid tun id", *argv);
+			if (get_u32(tun_id_end, t + 1, 0) ||
+				    *tun_id_end >= 1u << 24)
+				invarg("invalid tun id", *argv);
+
+		} else {
+			if (get_u32(tun_id_start, *argv, 0) ||
+				    *tun_id_start >= 1u << 24)
+				invarg("invalid tun id", *argv);
+		}
+	} else {
+		invarg("tunnel id expected", *argv);
+	}
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int add_tunnel_info(struct nlmsghdr *n, int reqsize,
+			   __u16 vid, __u32 tun_id, __u16 flags)
+{
+	struct rtattr *tinfo;
+
+	tinfo = addattr_nest(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
+	addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_ID, tun_id);
+	addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid);
+	addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags);
+
+	addattr_nest_end(n, tinfo);
+
+	return 0;
+}
+
+static int add_tunnel_info_range(struct nlmsghdr *n, int reqsize,
+				 __u16 vid_start, int16_t vid_end,
+				 __u32 tun_id_start, __u32 tun_id_end)
+{
+	if (vid_end != -1 && (vid_end - vid_start) > 0) {
+		add_tunnel_info(n, reqsize, vid_start, tun_id_start,
+				BRIDGE_VLAN_INFO_RANGE_BEGIN);
+
+		add_tunnel_info(n, reqsize, vid_end, tun_id_end,
+				BRIDGE_VLAN_INFO_RANGE_END);
+	} else {
+		add_tunnel_info(n, reqsize, vid_start, tun_id_start, 0);
+	}
+
+	return 0;
+}
+
+static int add_vlan_info_range(struct nlmsghdr *n, int reqsize, __u16 vid_start,
+			       int16_t vid_end, __u16 flags)
+{
+	struct bridge_vlan_info vinfo = {};
+
+	vinfo.flags = flags;
+	vinfo.vid = vid_start;
+	if (vid_end != -1) {
+		/* send vlan range start */
+		addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
+			  sizeof(vinfo));
+		vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
+
+		/* Now send the vlan range end */
+		vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
+		vinfo.vid = vid_end;
+		addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
+			  sizeof(vinfo));
+	} else {
+		addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
+			  sizeof(vinfo));
+	}
+
+	return 0;
+}
+
 static int vlan_modify(int cmd, int argc, char **argv)
 {
 	struct {
@@ -44,7 +140,10 @@  static int vlan_modify(int cmd, int argc, char **argv)
 	short vid_end = -1;
 	struct rtattr *afspec;
 	struct bridge_vlan_info vinfo = {};
+	bool tunnel_info_set = false;
 	unsigned short flags = 0;
+	__u32 tun_id_start = 0;
+	__u32 tun_id_end = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -72,6 +171,12 @@  static int vlan_modify(int cmd, int argc, char **argv)
 			vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
 		} else if (strcmp(*argv, "untagged") == 0) {
 			vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+		} else if (strcmp(*argv, "tunnel_info") == 0) {
+				if (parse_tunnel_info(&argc, &argv,
+						      &tun_id_start,
+						      &tun_id_end))
+					return -1;
+				tunnel_info_set = true;
 		} else {
 			if (matches(*argv, "help") == 0) {
 				NEXT_ARG();
@@ -114,22 +219,12 @@  static int vlan_modify(int cmd, int argc, char **argv)
 	if (flags)
 		addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
 
-	vinfo.vid = vid;
-	if (vid_end != -1) {
-		/* send vlan range start */
-		addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
-			  sizeof(vinfo));
-		vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
-
-		/* Now send the vlan range end */
-		vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
-		vinfo.vid = vid_end;
-		addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
-			  sizeof(vinfo));
-	} else {
-		addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
-			  sizeof(vinfo));
-	}
+	if (tunnel_info_set)
+		add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end,
+				      tun_id_start, tun_id_end);
+	else
+		add_vlan_info_range(&req.n, sizeof(req), vid, vid_end,
+				    vinfo.flags);
 
 	addattr_nest_end(&req.n, afspec);
 
@@ -146,14 +241,14 @@  static int vlan_modify(int cmd, int argc, char **argv)
  *             which are less than filter_vlan)
  * return  1 - print the entry and continue
  */
-static int filter_vlan_check(struct bridge_vlan_info *vinfo)
+static int filter_vlan_check(__u16 vid, __u16 flags)
 {
 	/* if we're filtering we should stop on the first greater entry */
-	if (filter_vlan && vinfo->vid > filter_vlan &&
-	    !(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
+	if (filter_vlan && vid > filter_vlan &&
+	    !(flags & BRIDGE_VLAN_INFO_RANGE_END))
 		return -1;
-	if ((vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
-	    vinfo->vid < filter_vlan)
+	if ((flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
+	    vid < filter_vlan)
 		return 0;
 
 	return 1;
@@ -181,6 +276,144 @@  static void start_json_vlan_flags_array(bool *vlan_flags)
 	*vlan_flags = true;
 }
 
+static int print_vlan_tunnel_info(const struct sockaddr_nl *who,
+				  struct nlmsghdr *n,
+				  void *arg)
+{
+	struct ifinfomsg *ifm = NLMSG_DATA(n);
+	struct rtattr *tb[IFLA_MAX+1];
+	int len = n->nlmsg_len;
+	FILE *fp = arg;
+	int jsonw_end_parray = 0;
+
+	if (n->nlmsg_type != RTM_NEWLINK) {
+		fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*ifm));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (ifm->ifi_family != AF_BRIDGE)
+		return 0;
+
+	if (filter_index && filter_index != ifm->ifi_index)
+		return 0;
+
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
+
+	/* if AF_SPEC isn't there, vlan table is not preset for this port */
+	if (!tb[IFLA_AF_SPEC]) {
+		if (!filter_vlan && !jw_global)
+			fprintf(fp, "%s\tNone\n",
+				ll_index_to_name(ifm->ifi_index));
+		return 0;
+	} else {
+		struct rtattr *i, *list = tb[IFLA_AF_SPEC];
+		int rem = RTA_PAYLOAD(list);
+		__u16 last_vid_start = 0;
+		__u32 last_tunid_start = 0;
+
+		if (!filter_vlan) {
+			print_vlan_port(fp, ifm->ifi_index);
+			jsonw_end_parray = 1;
+		}
+
+		for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+			__u32 tunnel_id = 0;
+			__u16 tunnel_vid = 0;
+			__u16 tunnel_flags = 0;
+			int vcheck_ret;
+
+			if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO)
+				continue;
+
+			parse_rtattr(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
+				     RTA_DATA(i), RTA_PAYLOAD(i));
+
+			if (tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
+				tunnel_vid =
+					rta_getattr_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
+			else
+				continue;
+
+			if (tb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
+				tunnel_id =
+					rta_getattr_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
+
+			if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
+				tunnel_flags =
+					rta_getattr_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
+
+			if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) {
+				last_vid_start = tunnel_vid;
+				last_tunid_start = tunnel_id;
+			}
+			vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags);
+			if (vcheck_ret == -1)
+				break;
+			else if (vcheck_ret == 0)
+				continue;
+
+			if (tunnel_flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
+				continue;
+
+			if (filter_vlan) {
+				print_vlan_port(fp, ifm->ifi_index);
+				jsonw_end_parray = 1;
+			}
+
+			if (jw_global) {
+				jsonw_start_object(jw_global);
+				jsonw_uint_field(jw_global, "vlan",
+						 last_vid_start);
+			} else {
+				fprintf(fp, "\t %hu", last_vid_start);
+			}
+			if (last_vid_start != tunnel_vid) {
+				if (jw_global)
+					jsonw_uint_field(jw_global, "vlanEnd",
+							 tunnel_vid);
+				else
+					fprintf(fp, "-%hu", tunnel_vid);
+			}
+
+			if (jw_global) {
+				jsonw_uint_field(jw_global, "tunid",
+						 last_tunid_start);
+			} else {
+				fprintf(fp, "\t %hu", last_tunid_start);
+			}
+			if (last_vid_start != tunnel_vid) {
+				if (jw_global)
+					jsonw_uint_field(jw_global, "tunidEnd",
+							 tunnel_id);
+				else
+					fprintf(fp, "-%hu", tunnel_id);
+			}
+
+			if (jw_global)
+				jsonw_end_object(jw_global);
+			else
+				fprintf(fp, "\n");
+		}
+	}
+
+	if (jsonw_end_parray) {
+		if (jw_global)
+			jsonw_end_array(jw_global);
+		else
+			fprintf(fp, "\n");
+	}
+
+	fflush(fp);
+	return 0;
+}
+
 static int print_vlan(const struct sockaddr_nl *who,
 		      struct nlmsghdr *n,
 		      void *arg)
@@ -190,6 +423,7 @@  static int print_vlan(const struct sockaddr_nl *who,
 	int len = n->nlmsg_len;
 	struct rtattr *tb[IFLA_MAX+1];
 	bool vlan_flags = false;
+	int jsonw_end_parray = 0;
 
 	if (n->nlmsg_type != RTM_NEWLINK) {
 		fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
@@ -222,8 +456,10 @@  static int print_vlan(const struct sockaddr_nl *who,
 		int rem = RTA_PAYLOAD(list);
 		__u16 last_vid_start = 0;
 
-		if (!filter_vlan)
+		if (!filter_vlan) {
 			print_vlan_port(fp, ifm->ifi_index);
+			jsonw_end_parray = 1;
+		}
 
 		for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 			struct bridge_vlan_info *vinfo;
@@ -236,14 +472,16 @@  static int print_vlan(const struct sockaddr_nl *who,
 
 			if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
 				last_vid_start = vinfo->vid;
-			vcheck_ret = filter_vlan_check(vinfo);
+			vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags);
 			if (vcheck_ret == -1)
 				break;
 			else if (vcheck_ret == 0)
 				continue;
 
-			if (filter_vlan)
+			if (filter_vlan) {
 				print_vlan_port(fp, ifm->ifi_index);
+				jsonw_end_parray = 1;
+			}
 			if (jw_global) {
 				jsonw_start_object(jw_global);
 				jsonw_uint_field(jw_global, "vlan",
@@ -288,7 +526,7 @@  static int print_vlan(const struct sockaddr_nl *who,
 				fprintf(fp, "\n");
 		}
 	}
-	if (!filter_vlan) {
+	if (jsonw_end_parray) {
 		if (jw_global)
 			jsonw_end_array(jw_global);
 		else
@@ -384,6 +622,7 @@  static int print_vlan_stats(const struct sockaddr_nl *who,
 static int vlan_show(int argc, char **argv)
 {
 	char *filter_dev = NULL;
+	int ret = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -424,10 +663,19 @@  static int vlan_show(int argc, char **argv)
 			}
 			jsonw_start_object(jw_global);
 		} else {
-			printf("port\tvlan ids\n");
+			if (show_vlan_tunnel_info)
+				printf("port\tvlan ids\ttunnel id\n");
+			else
+				printf("port\tvlan ids\n");
 		}
 
-		if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
+		if (show_vlan_tunnel_info)
+			ret = rtnl_dump_filter(&rth, print_vlan_tunnel_info,
+					       stdout);
+		else
+			ret = rtnl_dump_filter(&rth, print_vlan, stdout);
+
+		if (ret < 0) {
 			fprintf(stderr, "Dump ternminated\n");
 			exit(1);
 		}
@@ -483,6 +731,10 @@  int do_vlan(int argc, char **argv)
 		    matches(*argv, "lst") == 0 ||
 		    matches(*argv, "list") == 0)
 			return vlan_show(argc-1, argv+1);
+		if (matches(*argv, "tunnelshow") == 0) {
+			show_vlan_tunnel_info = 1;
+			return vlan_show(argc-1, argv+1);
+		}
 		if (matches(*argv, "help") == 0)
 			usage();
 	} else {
diff --git a/man/man8/bridge.8 b/man/man8/bridge.8
index d3c5b1e..d6baa81 100644
--- a/man/man8/bridge.8
+++ b/man/man8/bridge.8
@@ -105,11 +105,13 @@  bridge \- show / manipulate bridge addresses and devices
 .IR DEV
 .B vid
 .IR VID " [ "
+.BR tunnel_info
+.IR TUNNEL_ID " ] [ "
 .BR pvid " ] [ " untagged " ] [ "
 .BR self " ] [ " master " ] "
 
 .ti -8
-.BR "bridge vlan" " [ " show " ] [ "
+.BR "bridge vlan" " [ " show " | " tunnelshow " ] [ "
 .B dev
 .IR DEV " ]"
 
@@ -562,6 +564,12 @@  the interface with which this vlan is associated.
 the VLAN ID that identifies the vlan.
 
 .TP
+.BI tunnel_info " TUNNEL_ID"
+the TUNNEL ID that maps to this vlan. The tunnel id is set in dst_metadata for
+every packet that belongs to this vlan (applicable to bridge ports with vlan_tunnel
+flag set).
+
+.TP
 .BI pvid
 the vlan specified is to be considered a PVID at ingress.
 Any untagged frames will be assigned to this VLAN.
@@ -598,6 +606,10 @@  With the
 .B -statistics
 option, the command displays per-vlan traffic statistics.
 
+.SS bridge vlan tunnelshow - list vlan tunnel mapping.
+
+This command displays the current vlan tunnel info mapping.
+
 .SH bridge monitor - state monitoring
 
 The