Blob Blame History Raw
From 7bfe1d25875b2fef1cfd679fdd8c4a999928bb80 Mon Sep 17 00:00:00 2001
From: Jakub Sitnicki <jkbs@redhat.com>
Date: Wed, 27 Jul 2016 15:56:19 +0200
Subject: [PATCH] iplink: Check address length via netlink

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1253767
          https://bugzilla.redhat.com/show_bug.cgi?id=1271580
Upstream Status: iproute2.git commit 8fe58d5
Conflicts:
* context in iplink_parse_vf() because we don't have
  6c5ffb9a2c3d ("iplink: cleanup whitespace and checkpatch issues")

commit 8fe58d58941f440140986f444c3d040b5e350c87
Author: Phil Sutter <phil@nwl.cc>
Date:   Thu Jun 16 16:19:40 2016 +0200

    iplink: Check address length via netlink

    This is a feature which was lost during the conversion to netlink
    interface: If the device exists and a user tries to change the link
    layer address, query the kernel for the old address first and reject the
    new one if sizes differ.

    This patch adds the same check when setting VF address by assuming same
    length as PF device.

    Note that at least for VFs the check can't be done in kernel space since
    struct ifla_vf_mac lacks a length field and due to netlink padding the
    exact size can't be communicated to the kernel.

    Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 ip/iplink.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 50 insertions(+), 2 deletions(-)

diff --git a/ip/iplink.c b/ip/iplink.c
index 7b070f4..a8bcc09 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -232,6 +232,36 @@ struct iplink_req {
 	char			buf[1024];
 };
 
+static int nl_get_ll_addr_len(unsigned int dev_index)
+{
+	int len;
+	struct iplink_req req = {
+		.n = {
+			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+			.nlmsg_type = RTM_GETLINK,
+			.nlmsg_flags = NLM_F_REQUEST
+		},
+		.i = {
+			.ifi_family = preferred_family,
+			.ifi_index = dev_index,
+		}
+	};
+	struct rtattr *tb[IFLA_MAX+1];
+
+	if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
+		return -1;
+
+	len = req.n.nlmsg_len - NLMSG_LENGTH(sizeof(req.i));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(&req.i), len, NLA_F_NESTED);
+	if (!tb[IFLA_ADDRESS])
+		return -1;
+
+	return RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+}
+
 static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
 			   struct iplink_req *req, int dev_index)
 {
@@ -269,11 +299,18 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
 		NEXT_ARG();
 		if (matches(*argv, "mac") == 0) {
 			struct ifla_vf_mac ivm = { 0 };
+			int halen = nl_get_ll_addr_len(dev_index);
 			NEXT_ARG();
 			ivm.vf = vf;
 			len = ll_addr_a2n((char *)ivm.mac, 32, *argv);
 			if (len < 0)
 				return -1;
+			if (halen > 0 && len != halen) {
+				fprintf(stderr,
+					"Invalid address length %d - must be %d bytes\n",
+					len, halen);
+				return -1;
+			}
 			addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm));
 		} else if (matches(*argv, "vlan") == 0) {
 			struct ifla_vf_vlan ivv;
@@ -416,6 +453,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
 	int numrxqueues = -1;
 	int dev_index = 0;
 	int link_netnsid = -1;
+	int addr_len = 0;
 
 	*group = -1;
 	ret = argc;
@@ -435,10 +473,10 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
 			*link = *argv;
 		} else if (matches(*argv, "address") == 0) {
 			NEXT_ARG();
-			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+			addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
 			if (len < 0)
 				return -1;
-			addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
+			addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, addr_len);
 		} else if (matches(*argv, "broadcast") == 0 ||
 				strcmp(*argv, "brd") == 0) {
 			NEXT_ARG();
@@ -635,6 +673,16 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
 		argc--; argv++;
 	}
 
+	if (dev_index && addr_len) {
+		int halen = nl_get_ll_addr_len(dev_index);
+		if (halen >= 0 && halen != addr_len) {
+			fprintf(stderr,
+			        "Invalid address length %d - must be %d bytes\n",
+			        addr_len, halen);
+			return -1;
+		}
+	}
+
 	return ret - argc;
 }
 
-- 
1.8.3.1