diff mbox

[LEDE-DEV,netifd,v3] system-linux: add VXLAN support

Message ID 55471ddc9a83943fb108bae10bff918d29a1b3d5.1488275712.git.mschiffer@universe-factory.net
State Accepted
Headers show

Commit Message

Matthias Schiffer Feb. 28, 2017, 9:57 a.m. UTC
VXLAN shares many attributes with the tunnel devices, so it is implemented
as a new tunnel type. The 'remote' attribute can be used for an unicast
peer or a multicast group.

The IANA-assigned port 4789 is used by default, instead of the non-standard
port Linux defaults to.

Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
---

v2: Updated to use a nested JSON object for the VXLAN-specific attributes -
although one could argue that MAC address, port and "ID" aren't very
specific after all, at least I can see us using the same attributes if we
ever add L2TPv3 support to netifd...
v3: changed an incorrect __TUNNEL_ATTR_MAX to __VXLAN_DATA_ATTR_MAX

 system-linux.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 system.c       |  12 +++++
 system.h       |  10 ++++
 3 files changed, 181 insertions(+), 1 deletion(-)

Comments

Hans Dedecker March 5, 2017, 9:30 p.m. UTC | #1
On Tuesday, 28 February 2017 10:57:05 CET Matthias Schiffer wrote:
> VXLAN shares many attributes with the tunnel devices, so it is implemented
> as a new tunnel type. The 'remote' attribute can be used for an unicast
> peer or a multicast group.
> 
> The IANA-assigned port 4789 is used by default, instead of the non-standard
> port Linux defaults to.
> 
> Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
Acked-by: Hans Dedecker <dedeckeh@gmail.com>
> ---
> 
> v2: Updated to use a nested JSON object for the VXLAN-specific attributes -
> although one could argue that MAC address, port and "ID" aren't very
> specific after all, at least I can see us using the same attributes if we
> ever add L2TPv3 support to netifd...
> v3: changed an incorrect __TUNNEL_ATTR_MAX to __VXLAN_DATA_ATTR_MAX
> 
>  system-linux.c | 160
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- system.c       | 
> 12 +++++
>  system.h       |  10 ++++
>  3 files changed, 181 insertions(+), 1 deletion(-)
> 
> diff --git a/system-linux.c b/system-linux.c
> index f4d6c25..8888047 100644
> --- a/system-linux.c
> +++ b/system-linux.c
> @@ -4,6 +4,7 @@
>   * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
>   * Copyright (C) 2013 Steven Barth <steven@midlink.org>
>   * Copyright (C) 2014 Gioacchino Mazzurco <gio@eigenlab.org>
> + * Copyright (C) 2017 Matthias Schiffer <mschiffer@universe-factory.net>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License version 2
> @@ -25,6 +26,7 @@
>  #include <net/if_arp.h>
> 
>  #include <arpa/inet.h>
> +#include <netinet/ether.h>
>  #include <netinet/in.h>
> 
>  #include <linux/rtnetlink.h>
> @@ -2540,6 +2542,155 @@ failure:
>  }
>  #endif
> 
> +#ifdef IFLA_VXLAN_MAX
> +static int system_add_vxlan(const char *name, const unsigned int link,
> struct blob_attr **tb, bool v6) +{
> +	struct blob_attr *tb_data[__VXLAN_DATA_ATTR_MAX];
> +	struct nl_msg *msg;
> +	struct nlattr *linkinfo, *data;
> +	struct ifinfomsg iim = { .ifi_family = AF_UNSPEC, };
> +	struct blob_attr *cur;
> +	int ret = 0;
> +
> +	if ((cur = tb[TUNNEL_ATTR_DATA]))
> +		blobmsg_parse(vxlan_data_attr_list.params, __VXLAN_DATA_ATTR_MAX,
> tb_data, +			blobmsg_data(cur), blobmsg_len(cur));
> +	else
> +		return -EINVAL;
> +
> +	msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE |
> NLM_F_EXCL); +
> +	if (!msg)
> +		return -1;
> +
> +	nlmsg_append(msg, &iim, sizeof(iim), 0);
> +
> +	nla_put_string(msg, IFLA_IFNAME, name);
> +
> +	if ((cur = tb_data[VXLAN_DATA_ATTR_MACADDR])) {
> +		struct ether_addr *ea = ether_aton(blobmsg_get_string(cur));
> +		if (!ea) {
> +			ret = -EINVAL;
> +			goto failure;
> +		}
> +
> +		nla_put(msg, IFLA_ADDRESS, ETH_ALEN, ea);
> +	}
> +
> +	if ((cur = tb[TUNNEL_ATTR_MTU])) {
> +		uint32_t mtu = blobmsg_get_u32(cur);
> +		nla_put_u32(msg, IFLA_MTU, mtu);
> +	}
> +
> +	if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) {
> +		ret = -ENOMEM;
> +		goto failure;
> +	}
> +
> +	nla_put_string(msg, IFLA_INFO_KIND, "vxlan");
> +
> +	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) {
> +		ret = -ENOMEM;
> +		goto failure;
> +	}
> +
> +	if (link)
> +		nla_put_u32(msg, IFLA_VXLAN_LINK, link);
> +
> +	if ((cur = tb_data[VXLAN_DATA_ATTR_ID])) {
> +		uint32_t id = blobmsg_get_u32(cur);
> +		if (id >= (1u << 24) - 1) {
> +			ret = -EINVAL;
> +			goto failure;
> +		}
> +
> +		nla_put_u32(msg, IFLA_VXLAN_ID, id);
> +	}
> +
> +	if (v6) {
> +		struct in6_addr in6buf;
> +		if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
> +			if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
> +				ret = -EINVAL;
> +				goto failure;
> +			}
> +			nla_put(msg, IFLA_VXLAN_LOCAL6, sizeof(in6buf), &in6buf);
> +		}
> +
> +		if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
> +			if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
> +				ret = -EINVAL;
> +				goto failure;
> +			}
> +			nla_put(msg, IFLA_VXLAN_GROUP6, sizeof(in6buf), &in6buf);
> +		}
> +	} else {
> +		struct in_addr inbuf;
> +
> +		if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
> +			if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
> +				ret = -EINVAL;
> +				goto failure;
> +			}
> +			nla_put(msg, IFLA_VXLAN_LOCAL, sizeof(inbuf), &inbuf);
> +		}
> +
> +		if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
> +			if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
> +				ret = -EINVAL;
> +				goto failure;
> +			}
> +			nla_put(msg, IFLA_VXLAN_GROUP, sizeof(inbuf), &inbuf);
> +		}
> +	}
> +
> +	uint32_t port = 4789;
> +	if ((cur = tb_data[VXLAN_DATA_ATTR_PORT])) {
> +		port = blobmsg_get_u32(cur);
> +		if (port < 1 || port > 65535) {
> +			ret = -EINVAL;
> +			goto failure;
> +		}
> +	}
> +	nla_put_u16(msg, IFLA_VXLAN_PORT, htons(port));
> +
> +	if ((cur = tb[TUNNEL_ATTR_TOS])) {
> +		char *str = blobmsg_get_string(cur);
> +		unsigned tos = 1;
> +
> +		if (strcmp(str, "inherit")) {
> +			if (!system_tos_aton(str, &tos))
> +				return -EINVAL;
> +		}
> +
> +		nla_put_u8(msg, IFLA_VXLAN_TOS, tos);
> +	}
> +
> +	if ((cur = tb[TUNNEL_ATTR_TTL])) {
> +		uint32_t ttl = blobmsg_get_u32(cur);
> +		if (ttl < 1 || ttl > 255) {
> +			ret = -EINVAL;
> +			goto failure;
> +		}
> +
> +		nla_put_u8(msg, IFLA_VXLAN_TTL, ttl);
> +	}
> +
> +	nla_nest_end(msg, data);
> +	nla_nest_end(msg, linkinfo);
> +
> +	ret = system_rtnl_call(msg);
> +	if (ret)
> +		D(SYSTEM, "Error adding vxlan '%s': %d\n", name, ret);
> +
> +	return ret;
> +
> +failure:
> +	nlmsg_free(msg);
> +	return ret;
> +}
> +#endif
> +
>  static int system_add_proto_tunnel(const char *name, const uint8_t proto,
> const unsigned int link, struct blob_attr **tb) {
>  	struct blob_attr *cur;
> @@ -2609,7 +2760,8 @@ static int __system_del_ip_tunnel(const char *name,
> struct blob_attr **tb)
> 
>  	if (!strcmp(str, "greip") || !strcmp(str, "gretapip") ||
>  	    !strcmp(str, "greip6") || !strcmp(str, "gretapip6") ||
> -	    !strcmp(str, "vtiip") || !strcmp(str, "vtiip6"))
> +	    !strcmp(str, "vtiip") || !strcmp(str, "vtiip6") ||
> +	    !strcmp(str, "vxlan") || !strcmp(str, "vxlan6"))
>  		return system_link_del(name);
>  	else
>  		return tunnel_ioctl(name, SIOCDELTUNNEL, NULL);
> @@ -2833,6 +2985,12 @@ failure:
>  	} else if (!strcmp(str, "vtiip6")) {
>  		return system_add_vti_tunnel(name, "vti6", link, tb, true);
>  #endif
> +#ifdef IFLA_VXLAN_MAX
> +	} else if(!strcmp(str, "vxlan")) {
> +		return system_add_vxlan(name, link, tb, false);
> +	} else if(!strcmp(str, "vxlan6")) {
> +		return system_add_vxlan(name, link, tb, true);
> +#endif
>  #endif
>  	} else if (!strcmp(str, "ipip")) {
>  		return system_add_proto_tunnel(name, IPPROTO_IPIP, link, tb);
> diff --git a/system.c b/system.c
> index e57084f..52172c3 100644
> --- a/system.c
> +++ b/system.c
> @@ -28,6 +28,7 @@ static const struct blobmsg_policy
> tunnel_attrs[__TUNNEL_ATTR_MAX] = { [TUNNEL_ATTR_LINK] = { .name = "link",
> .type = BLOBMSG_TYPE_STRING }, [TUNNEL_ATTR_FMRS] = { .name = "fmrs", .type
> = BLOBMSG_TYPE_ARRAY }, [TUNNEL_ATTR_INFO] = { .name = "info", .type =
> BLOBMSG_TYPE_STRING }, +	[TUNNEL_ATTR_DATA] = { .name = "data", .type =
> BLOBMSG_TYPE_TABLE }, };
> 
>  const struct uci_blob_param_list tunnel_attr_list = {
> @@ -35,6 +36,17 @@ const struct uci_blob_param_list tunnel_attr_list = {
>  	.params = tunnel_attrs,
>  };
> 
> +static const struct blobmsg_policy vxlan_data_attrs[__VXLAN_DATA_ATTR_MAX]
> = { +	[VXLAN_DATA_ATTR_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
> +	[VXLAN_DATA_ATTR_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 },
> +	[VXLAN_DATA_ATTR_MACADDR] = { .name = "macaddr", .type =
> BLOBMSG_TYPE_STRING }, +};
> +
> +const struct uci_blob_param_list vxlan_data_attr_list = {
> +	.n_params = __VXLAN_DATA_ATTR_MAX,
> +	.params = vxlan_data_attrs,
> +};
> +
>  void system_fd_set_cloexec(int fd)
>  {
>  #ifdef FD_CLOEXEC
> diff --git a/system.h b/system.h
> index 6077b95..6501d15 100644
> --- a/system.h
> +++ b/system.h
> @@ -35,11 +35,21 @@ enum tunnel_param {
>  	TUNNEL_ATTR_LINK,
>  	TUNNEL_ATTR_FMRS,
>  	TUNNEL_ATTR_INFO,
> +	TUNNEL_ATTR_DATA,
>  	__TUNNEL_ATTR_MAX
>  };
> 
>  extern const struct uci_blob_param_list tunnel_attr_list;
> 
> +enum vxlan_data {
> +	VXLAN_DATA_ATTR_ID,
> +	VXLAN_DATA_ATTR_PORT,
> +	VXLAN_DATA_ATTR_MACADDR,
> +	__VXLAN_DATA_ATTR_MAX
> +};
> +
> +extern const struct uci_blob_param_list vxlan_data_attr_list;
> +
>  enum bridge_opt {
>  	/* stp and forward delay always set */
>  	BRIDGE_OPT_AGEING_TIME		    = (1 << 0),
diff mbox

Patch

diff --git a/system-linux.c b/system-linux.c
index f4d6c25..8888047 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -4,6 +4,7 @@ 
  * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
  * Copyright (C) 2013 Steven Barth <steven@midlink.org>
  * Copyright (C) 2014 Gioacchino Mazzurco <gio@eigenlab.org>
+ * Copyright (C) 2017 Matthias Schiffer <mschiffer@universe-factory.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
@@ -25,6 +26,7 @@ 
 #include <net/if_arp.h>
 
 #include <arpa/inet.h>
+#include <netinet/ether.h>
 #include <netinet/in.h>
 
 #include <linux/rtnetlink.h>
@@ -2540,6 +2542,155 @@  failure:
 }
 #endif
 
+#ifdef IFLA_VXLAN_MAX
+static int system_add_vxlan(const char *name, const unsigned int link, struct blob_attr **tb, bool v6)
+{
+	struct blob_attr *tb_data[__VXLAN_DATA_ATTR_MAX];
+	struct nl_msg *msg;
+	struct nlattr *linkinfo, *data;
+	struct ifinfomsg iim = { .ifi_family = AF_UNSPEC, };
+	struct blob_attr *cur;
+	int ret = 0;
+
+	if ((cur = tb[TUNNEL_ATTR_DATA]))
+		blobmsg_parse(vxlan_data_attr_list.params, __VXLAN_DATA_ATTR_MAX, tb_data,
+			blobmsg_data(cur), blobmsg_len(cur));
+	else
+		return -EINVAL;
+
+	msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+
+	if (!msg)
+		return -1;
+
+	nlmsg_append(msg, &iim, sizeof(iim), 0);
+
+	nla_put_string(msg, IFLA_IFNAME, name);
+
+	if ((cur = tb_data[VXLAN_DATA_ATTR_MACADDR])) {
+		struct ether_addr *ea = ether_aton(blobmsg_get_string(cur));
+		if (!ea) {
+			ret = -EINVAL;
+			goto failure;
+		}
+
+		nla_put(msg, IFLA_ADDRESS, ETH_ALEN, ea);
+	}
+
+	if ((cur = tb[TUNNEL_ATTR_MTU])) {
+		uint32_t mtu = blobmsg_get_u32(cur);
+		nla_put_u32(msg, IFLA_MTU, mtu);
+	}
+
+	if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) {
+		ret = -ENOMEM;
+		goto failure;
+	}
+
+	nla_put_string(msg, IFLA_INFO_KIND, "vxlan");
+
+	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) {
+		ret = -ENOMEM;
+		goto failure;
+	}
+
+	if (link)
+		nla_put_u32(msg, IFLA_VXLAN_LINK, link);
+
+	if ((cur = tb_data[VXLAN_DATA_ATTR_ID])) {
+		uint32_t id = blobmsg_get_u32(cur);
+		if (id >= (1u << 24) - 1) {
+			ret = -EINVAL;
+			goto failure;
+		}
+
+		nla_put_u32(msg, IFLA_VXLAN_ID, id);
+	}
+
+	if (v6) {
+		struct in6_addr in6buf;
+		if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+			if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+				ret = -EINVAL;
+				goto failure;
+			}
+			nla_put(msg, IFLA_VXLAN_LOCAL6, sizeof(in6buf), &in6buf);
+		}
+
+		if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+			if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+				ret = -EINVAL;
+				goto failure;
+			}
+			nla_put(msg, IFLA_VXLAN_GROUP6, sizeof(in6buf), &in6buf);
+		}
+	} else {
+		struct in_addr inbuf;
+
+		if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+			if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+				ret = -EINVAL;
+				goto failure;
+			}
+			nla_put(msg, IFLA_VXLAN_LOCAL, sizeof(inbuf), &inbuf);
+		}
+
+		if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+			if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+				ret = -EINVAL;
+				goto failure;
+			}
+			nla_put(msg, IFLA_VXLAN_GROUP, sizeof(inbuf), &inbuf);
+		}
+	}
+
+	uint32_t port = 4789;
+	if ((cur = tb_data[VXLAN_DATA_ATTR_PORT])) {
+		port = blobmsg_get_u32(cur);
+		if (port < 1 || port > 65535) {
+			ret = -EINVAL;
+			goto failure;
+		}
+	}
+	nla_put_u16(msg, IFLA_VXLAN_PORT, htons(port));
+
+	if ((cur = tb[TUNNEL_ATTR_TOS])) {
+		char *str = blobmsg_get_string(cur);
+		unsigned tos = 1;
+
+		if (strcmp(str, "inherit")) {
+			if (!system_tos_aton(str, &tos))
+				return -EINVAL;
+		}
+
+		nla_put_u8(msg, IFLA_VXLAN_TOS, tos);
+	}
+
+	if ((cur = tb[TUNNEL_ATTR_TTL])) {
+		uint32_t ttl = blobmsg_get_u32(cur);
+		if (ttl < 1 || ttl > 255) {
+			ret = -EINVAL;
+			goto failure;
+		}
+
+		nla_put_u8(msg, IFLA_VXLAN_TTL, ttl);
+	}
+
+	nla_nest_end(msg, data);
+	nla_nest_end(msg, linkinfo);
+
+	ret = system_rtnl_call(msg);
+	if (ret)
+		D(SYSTEM, "Error adding vxlan '%s': %d\n", name, ret);
+
+	return ret;
+
+failure:
+	nlmsg_free(msg);
+	return ret;
+}
+#endif
+
 static int system_add_proto_tunnel(const char *name, const uint8_t proto, const unsigned int link, struct blob_attr **tb)
 {
 	struct blob_attr *cur;
@@ -2609,7 +2760,8 @@  static int __system_del_ip_tunnel(const char *name, struct blob_attr **tb)
 
 	if (!strcmp(str, "greip") || !strcmp(str, "gretapip") ||
 	    !strcmp(str, "greip6") || !strcmp(str, "gretapip6") ||
-	    !strcmp(str, "vtiip") || !strcmp(str, "vtiip6"))
+	    !strcmp(str, "vtiip") || !strcmp(str, "vtiip6") ||
+	    !strcmp(str, "vxlan") || !strcmp(str, "vxlan6"))
 		return system_link_del(name);
 	else
 		return tunnel_ioctl(name, SIOCDELTUNNEL, NULL);
@@ -2833,6 +2985,12 @@  failure:
 	} else if (!strcmp(str, "vtiip6")) {
 		return system_add_vti_tunnel(name, "vti6", link, tb, true);
 #endif
+#ifdef IFLA_VXLAN_MAX
+	} else if(!strcmp(str, "vxlan")) {
+		return system_add_vxlan(name, link, tb, false);
+	} else if(!strcmp(str, "vxlan6")) {
+		return system_add_vxlan(name, link, tb, true);
+#endif
 #endif
 	} else if (!strcmp(str, "ipip")) {
 		return system_add_proto_tunnel(name, IPPROTO_IPIP, link, tb);
diff --git a/system.c b/system.c
index e57084f..52172c3 100644
--- a/system.c
+++ b/system.c
@@ -28,6 +28,7 @@  static const struct blobmsg_policy tunnel_attrs[__TUNNEL_ATTR_MAX] = {
 	[TUNNEL_ATTR_LINK] = { .name = "link", .type = BLOBMSG_TYPE_STRING },
 	[TUNNEL_ATTR_FMRS] = { .name = "fmrs", .type = BLOBMSG_TYPE_ARRAY },
 	[TUNNEL_ATTR_INFO] = { .name = "info", .type = BLOBMSG_TYPE_STRING },
+	[TUNNEL_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
 };
 
 const struct uci_blob_param_list tunnel_attr_list = {
@@ -35,6 +36,17 @@  const struct uci_blob_param_list tunnel_attr_list = {
 	.params = tunnel_attrs,
 };
 
+static const struct blobmsg_policy vxlan_data_attrs[__VXLAN_DATA_ATTR_MAX] = {
+	[VXLAN_DATA_ATTR_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
+	[VXLAN_DATA_ATTR_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 },
+	[VXLAN_DATA_ATTR_MACADDR] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
+};
+
+const struct uci_blob_param_list vxlan_data_attr_list = {
+	.n_params = __VXLAN_DATA_ATTR_MAX,
+	.params = vxlan_data_attrs,
+};
+
 void system_fd_set_cloexec(int fd)
 {
 #ifdef FD_CLOEXEC
diff --git a/system.h b/system.h
index 6077b95..6501d15 100644
--- a/system.h
+++ b/system.h
@@ -35,11 +35,21 @@  enum tunnel_param {
 	TUNNEL_ATTR_LINK,
 	TUNNEL_ATTR_FMRS,
 	TUNNEL_ATTR_INFO,
+	TUNNEL_ATTR_DATA,
 	__TUNNEL_ATTR_MAX
 };
 
 extern const struct uci_blob_param_list tunnel_attr_list;
 
+enum vxlan_data {
+	VXLAN_DATA_ATTR_ID,
+	VXLAN_DATA_ATTR_PORT,
+	VXLAN_DATA_ATTR_MACADDR,
+	__VXLAN_DATA_ATTR_MAX
+};
+
+extern const struct uci_blob_param_list vxlan_data_attr_list;
+
 enum bridge_opt {
 	/* stp and forward delay always set */
 	BRIDGE_OPT_AGEING_TIME		    = (1 << 0),