[PATCH RFC]: add VLAN support to iplink

Patrick McHardy kaber at trash.net
Wed Jul 10 11:22:14 UTC 2013


The following patch adds support for specifying device specific parameters
to "ip link add" and support for VLAN configuration. Modern features such
as GVRP, MVRP, 802.1ad and some minor features are not supported through
vconfig and won't be in the future, so this is needed to make use of these
features.

Open questions:

- Is displaying VLAN specific parameters in "ip link list" considered
  important? I didn't add this so far, but can so if it is.

- Any coding style issues or other changes required for inclusion? I'm not
  familiar with busybox coding style requirements, but tried to follow the
  existing style.

Please CC me on replies.
-------------- next part --------------
commit 473224594d8f45fe3261ec0bf9d4a96f61c847b3
Author: Patrick McHardy <kaber at trash.net>
Date:   Wed Jul 10 12:41:28 2013 +0200

    iplink: add VLAN support
    
    Add support for configuring VLAN devices to "ip link". Modern VLAN features
    such as GVRP, MVRP, 802.1ad etc. are not supported through the vconfig command.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
index bad2017..b15cdae 100644
--- a/networking/libiproute/iplink.c
+++ b/networking/libiproute/iplink.c
@@ -1,6 +1,7 @@
 /* vi: set sw=4 ts=4: */
 /*
  * Authors: Alexey Kuznetsov, <kuznet at ms2.inr.ac.ru>
+ * 			Patrick McHardy <kaber at trash.net>
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
@@ -8,6 +9,7 @@
 #include <net/if_packet.h>
 #include <netpacket/packet.h>
 #include <netinet/if_ether.h>
+#include <linux/if_vlan.h>
 
 #include "ip_common.h"  /* #include "libbb.h" is inside */
 #include "rt_names.h"
@@ -277,6 +279,111 @@ static int ipaddr_list_link(char **argv)
 	return ipaddr_list_or_flush(argv, 0);
 }
 
+static int vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
+{
+	static const char keywords[] ALIGN1 =
+		"id\0"
+		"reorder_hdr\0"
+		"gvrp\0"
+		"mvrp\0"
+		"loose_binding\0"
+		"protocol\0";
+	static const char protocols[] ALIGN1 =
+		"802.1q\0"
+		"802.1ad\0";
+	static const char str_on_off[] ALIGN1 =
+		"on\0"
+		"off\0";
+	enum {
+		ARG_id = 0,
+		ARG_reorder_hdr,
+		ARG_gvrp,
+		ARG_mvrp,
+		ARG_loose_binding,
+		ARG_protocol,
+	};
+	enum {
+		PROTO_8021Q = 0,
+		PROTO_8021AD,
+	};
+	enum {
+		PARM_on = 0,
+		PARM_off
+	};
+	smalluint arg;
+	uint16_t id, proto;
+	struct ifla_vlan_flags flags = {};
+
+	while (*argv) {
+		arg = index_in_substrings(keywords, *argv);
+		if (arg == ARG_id) {
+			NEXT_ARG();
+			id = get_u16(*argv, "id");
+			addattr_l(n, size, IFLA_VLAN_ID, &id, sizeof(id));
+		} else if (arg == ARG_protocol) {
+			NEXT_ARG();
+			arg = index_in_substrings(protocols, *argv);
+			if (arg == PROTO_8021Q)
+				proto = ETH_P_8021Q;
+			else if (arg == PROTO_8021AD)
+				proto = ETH_P_8021AD;
+			else
+				bb_error_msg_and_die("unknown VLAN encapsulation protocol %s",
+								     *argv);
+
+			addattr_l(n, size, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto));
+		} else {
+			int param;
+			NEXT_ARG();
+			param = index_in_strings(str_on_off, *argv);
+
+			if (arg == ARG_reorder_hdr) {
+				if (param < 0)
+					die_must_be_on_off("reorder_hdr");
+
+				flags.mask |= VLAN_FLAG_REORDER_HDR;
+				if (param == PARM_on)
+					flags.flags |= VLAN_FLAG_REORDER_HDR;
+				else
+					flags.flags &= ~VLAN_FLAG_REORDER_HDR;
+			} else if (arg == ARG_gvrp) {
+				if (param < 0)
+					die_must_be_on_off("gvrp");
+
+				flags.mask |= VLAN_FLAG_GVRP;
+				if (param == PARM_on)
+					flags.flags |= VLAN_FLAG_GVRP;
+				else
+					flags.flags &= ~VLAN_FLAG_GVRP;
+			} else if (arg == ARG_mvrp) {
+				if (param < 0)
+					die_must_be_on_off("mvrp");
+
+				flags.mask |= VLAN_FLAG_MVRP;
+				if (param == PARM_on)
+					flags.flags |= VLAN_FLAG_MVRP;
+				else
+					flags.flags &= ~VLAN_FLAG_MVRP;
+			} else if (arg == ARG_loose_binding) {
+				if (param < 0)
+					die_must_be_on_off("loose_binding");
+
+				flags.mask |= VLAN_FLAG_LOOSE_BINDING;
+				if (param == PARM_on)
+					flags.flags |= VLAN_FLAG_LOOSE_BINDING;
+				else
+					flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
+			}
+		}
+		argv++;
+	}
+
+	if (flags.mask)
+		addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
+
+	return 0;
+}
+
 #ifndef NLMSG_TAIL
 #define NLMSG_TAIL(nmsg) \
 	((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
@@ -286,12 +393,17 @@ static int do_change(char **argv, const unsigned rtm)
 {
 	static const char keywords[] ALIGN1 =
 		"link\0""name\0""type\0""dev\0";
+	static const char types[] ALIGN1 =
+		"vlan\0";
 	enum {
 		ARG_link,
 		ARG_name,
 		ARG_type,
 		ARG_dev,
 	};
+	enum {
+		TYPE_vlan,
+	};
 	struct rtnl_handle rth;
 	struct {
 		struct nlmsghdr  n;
@@ -321,6 +433,8 @@ static int do_change(char **argv, const unsigned rtm)
 		} else if (arg == ARG_type) {
 			NEXT_ARG();
 			type_str = *argv;
+			argv++;
+			break;
 		} else {
 			if (arg == ARG_dev) {
 				if (dev_str)
@@ -339,6 +453,18 @@ static int do_change(char **argv, const unsigned rtm)
 		addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
 		addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
 				strlen(type_str));
+
+		arg = index_in_substrings(types, type_str);
+		if (*argv) {
+			struct rtattr *data = NLMSG_TAIL(&req.n);
+			addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
+
+			if (arg == TYPE_vlan)
+				vlan_parse_opt(argv, &req.n, sizeof(req));
+
+			data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
+		}
+
 		linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
 	}
 	if (rtm != RTM_NEWLINK) {


More information about the busybox mailing list