ip link: Prevent duplication of table id for vrf tables

Message ID 20200307205916.15646-1-sharpd@cumulusnetworks.com
State New
Headers show
Series
  • ip link: Prevent duplication of table id for vrf tables
Related show

Commit Message

Donald Sharp March 7, 2020, 8:59 p.m.
Creation of different vrf's with duplicate table id's creates
a situation where two different routing entities believe
they have exclusive access to a particular table.  This
leads to situations where different routing processes
clash for control of a route due to inadvertent table
id overlap.  Prevent end user from making this mistake
on accident.

sharpd@eva ~/i/ip (master)> ip vrf show
Name              Table
-----------------------
BLUE              1300
GREEN             1301
sharpd@eva ~/i/ip (master)> sudo ./ip link add ORANGE type vrf table 1300
Error: argument "1300" is wrong: table specified is already being used

sharpd@eva ~/i/ip (master) [255]> sudo ./ip link add ORANGE type vrf table 1302
sharpd@eva ~/i/ip (master)> ip vrf show
Name              Table
-----------------------
BLUE              1300
GREEN             1301
ORANGE            1302

Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
---
 ip/ip_common.h  |  2 ++
 ip/iplink_vrf.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 ip/ipvrf.c      |  4 ++--
 3 files changed, 51 insertions(+), 2 deletions(-)

Patch

diff --git a/ip/ip_common.h b/ip/ip_common.h
index 879287e3..4cc825e9 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -148,6 +148,8 @@  void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details);
 
 /* iplink_vrf.c */
 __u32 ipvrf_get_table(const char *name);
+__u32 vrf_table_linkinfo(struct rtattr *li[]);
+int ipvrf_filter_req(struct nlmsghdr *nhl, int reqlen);
 int name_is_vrf(const char *name);
 
 #ifndef	INFINITY_LIFE_TIME
diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c
index 5d20f29d..968593f6 100644
--- a/ip/iplink_vrf.c
+++ b/ip/iplink_vrf.c
@@ -29,17 +29,64 @@  static void explain(void)
 	vrf_explain(stderr);
 }
 
+static bool vrf_find_table(struct nlmsghdr *n, __u32 table)
+{
+	struct ifinfomsg *ifi = NLMSG_DATA(n);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *li[IFLA_INFO_MAX + 1];
+	int len = n->nlmsg_len;
+	__u32 vrf_table;
+
+	len -= NLMSG_LENGTH(sizeof(*ifi));
+	if (len < 0)
+		return false;
+
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+
+	if (!tb[IFLA_LINKINFO])
+		return false;
+
+	parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+	if (!li[IFLA_INFO_KIND])
+		return false;
+
+	vrf_table = vrf_table_linkinfo(li);
+
+	if (vrf_table == table)
+		return true;
+
+	return false;
+}
+
 static int vrf_parse_opt(struct link_util *lu, int argc, char **argv,
 			    struct nlmsghdr *n)
 {
 	while (argc > 0) {
 		if (matches(*argv, "table") == 0) {
 			__u32 table;
+			bool found = false;
+			struct nlmsg_chain linfo = { NULL, NULL };
 
 			NEXT_ARG();
 
 			if (rtnl_rttable_a2n(&table, *argv))
 				invarg("invalid table ID\n", *argv);
+
+			if (ip_link_list(ipvrf_filter_req, &linfo) == 0) {
+				struct nlmsg_list *l;
+
+				for (l = linfo.head; l; l = l->next) {
+					if (vrf_find_table(&l->h, table)) {
+						found = true;
+						break;
+					}
+				}
+			}
+			if (found)
+				invarg("table specified is already being used\n",
+				       *argv);
+
 			addattr32(n, 1024, IFLA_VRF_TABLE, table);
 		} else if (matches(*argv, "help") == 0) {
 			explain();
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index b9a43675..ff0c492c 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -477,7 +477,7 @@  void vrf_reset(void)
 	vrf_switch("default");
 }
 
-static int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen)
+int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen)
 {
 	struct rtattr *linkinfo;
 	int err;
@@ -497,7 +497,7 @@  static int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen)
 }
 
 /* input arg is linkinfo */
-static __u32 vrf_table_linkinfo(struct rtattr *li[])
+__u32 vrf_table_linkinfo(struct rtattr *li[])
 {
 	struct rtattr *attr[IFLA_VRF_MAX + 1];