[PATCH 3/4] add veth link support

Cong Wang xiyou.wangcong at gmail.com
Fri Nov 1 19:58:48 EDT 2013


Signed-off-by: Cong Wang <xiyou.wangcong at gmail.com>
---
 include/linux/if_link.h           |   8 ++
 include/netlink/route/link.h      |   2 +
 include/netlink/route/link/veth.h |  35 +++++
 lib/Makefile.am                   |   2 +-
 lib/route/link.c                  |   2 +-
 lib/route/link/veth.c             | 281 ++++++++++++++++++++++++++++++++++++++
 tests/Makefile.am                 |   2 +
 tests/test-create-veth.c          |  41 ++++++
 8 files changed, 371 insertions(+), 2 deletions(-)
 create mode 100644 include/netlink/route/link/veth.h
 create mode 100644 lib/route/link/veth.c
 create mode 100644 tests/test-create-veth.c

diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 565b7f6..4a7cace 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -312,6 +312,14 @@ struct ifla_vxlan_port_range {
 	__be16	high;
 };
 
+enum {
+	VETH_INFO_UNSPEC,
+	VETH_INFO_PEER,
+
+	__VETH_INFO_MAX
+#define VETH_INFO_MAX   (__VETH_INFO_MAX - 1)
+};
+
 /* SR-IOV virtual function management section */
 
 enum {
diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h
index 0010a5f..b86cb7a 100644
--- a/include/netlink/route/link.h
+++ b/include/netlink/route/link.h
@@ -97,6 +97,8 @@ typedef enum {
 
 #define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1)
 
+extern struct nla_policy link_policy[];
+
 extern struct rtnl_link *rtnl_link_alloc(void);
 extern void	rtnl_link_put(struct rtnl_link *);
 
diff --git a/include/netlink/route/link/veth.h b/include/netlink/route/link/veth.h
new file mode 100644
index 0000000..7ca7d32
--- /dev/null
+++ b/include/netlink/route/link/veth.h
@@ -0,0 +1,35 @@
+/*
+ * netlink/route/link/veth.h		VETH interface
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2013 Cong Wang <xiyou.wangcong at gmail.com>
+ */
+
+#ifndef NETLINK_LINK_VETH_H_
+#define NETLINK_LINK_VETH_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_veth_alloc(void);
+extern void rtnl_link_veth_release(struct rtnl_link *);
+
+extern int rtnl_link_is_veth(struct rtnl_link *);
+
+extern struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *);
+extern int rtnl_link_veth_add(struct nl_sock *sock, const char *name,
+			      const char *peer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b400a62..af2aa55 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -76,7 +76,7 @@ libnl_route_3_la_SOURCES = \
 	route/link/api.c route/link/vlan.c route/link/dummy.c \
 	route/link/bridge.c route/link/inet6.c route/link/inet.c \
 	route/link/bonding.c route/link/can.c route/link/macvlan.c \
-	route/link/vxlan.c \
+	route/link/vxlan.c route/link/veth.c \
 	\
 	route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \
 	route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \
diff --git a/lib/route/link.c b/lib/route/link.c
index 9bf2db7..bb4b932 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -260,7 +260,7 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src)
 	return 0;
 }
 
-static struct nla_policy link_policy[IFLA_MAX+1] = {
+struct nla_policy link_policy[IFLA_MAX+1] = {
 	[IFLA_IFNAME]		= { .type = NLA_STRING,
 				    .maxlen = IFNAMSIZ },
 	[IFLA_MTU]		= { .type = NLA_U32 },
diff --git a/lib/route/link/veth.c b/lib/route/link/veth.c
new file mode 100644
index 0000000..dba1294
--- /dev/null
+++ b/lib/route/link/veth.c
@@ -0,0 +1,281 @@
+/*
+ * lib/route/link/veth.c	Virtual Ethernet
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2013 Cong Wang <xiyou.wangcong at gmail.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup veth VETH
+ * Virtual Ethernet
+ *
+ * @details
+ * \b Link Type Name: "veth"
+ *
+ * @route_doc{link_veth, VETH Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/veth.h>
+
+#include <linux/if_link.h>
+
+static struct nla_policy veth_policy[VETH_INFO_MAX+1] = {
+	[VETH_INFO_PEER]	= { .minlen = sizeof(struct ifinfomsg) },
+};
+
+static int veth_parse(struct rtnl_link *link, struct nlattr *data,
+		      struct nlattr *xstats)
+{
+	struct nlattr *tb[VETH_INFO_MAX+1];
+	struct nlattr *peer_tb[IFLA_MAX + 1];
+	struct rtnl_link *peer = link->l_info;
+	int err;
+
+	NL_DBG(3, "Parsing veth link info");
+
+	if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0)
+		goto errout;
+
+	if (tb[VETH_INFO_PEER]) {
+		struct nlattr *nla_peer;
+		struct ifinfomsg *ifi;
+
+		nla_peer = tb[VETH_INFO_PEER];
+		ifi = nla_data(nla_peer);
+
+		peer->l_family = ifi->ifi_family;
+		peer->l_arptype = ifi->ifi_type;
+		peer->l_index = ifi->ifi_index;
+		peer->l_flags = ifi->ifi_flags;
+		peer->l_change = ifi->ifi_change;
+		err = nla_parse(peer_tb, IFLA_MAX,
+				nla_data(nla_peer) + sizeof(struct ifinfomsg),
+				nla_len(nla_peer) - sizeof(struct ifinfomsg),
+				link_policy);
+		if (err < 0)
+			goto errout;
+
+		err = rtnl_link_info_parse(peer, peer_tb);
+		if (err < 0)
+			goto errout;
+	}
+
+	err = 0;
+
+errout:
+	return err;
+}
+
+static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+}
+
+static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	struct rtnl_link *peer = link->l_info;
+	char *name;
+	name = rtnl_link_get_name(peer);
+	nl_dump(p, "      peer ");
+	if (name)
+		nl_dump_line(p, "%s\n", name);
+	else
+		nl_dump_line(p, "%u\n", peer->l_index);
+}
+
+static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct rtnl_link *dst_peer , *src_peer = src->l_info;
+	int err;
+
+	dst_peer = dst->l_info = rtnl_link_alloc();
+	if (!dst_peer || !src_peer)
+		return -NLE_NOMEM;
+	if ((err = rtnl_link_set_type(dst, "veth")) < 0) {
+		rtnl_link_put(dst_peer);
+		return err;
+	}
+
+	memcpy(dst_peer, src_peer, sizeof(struct rtnl_link));
+
+	return 0;
+}
+
+static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+	struct rtnl_link *peer = link->l_info;
+	struct ifinfomsg ifi;
+	struct nlattr *data, *info_peer;
+
+	memset(&ifi, 0, sizeof ifi);
+	ifi.ifi_family = peer->l_family;
+	ifi.ifi_type = peer->l_arptype;
+	ifi.ifi_index = peer->l_index;
+	ifi.ifi_flags = peer->l_flags;
+	ifi.ifi_change = peer->l_change;
+
+	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+		return -NLE_MSGSIZE;
+	if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER)))
+		return -NLE_MSGSIZE;
+	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+		return -NLE_MSGSIZE;
+	rtnl_link_fill_info(msg, peer);
+	nla_nest_end(msg, info_peer);
+	nla_nest_end(msg, data);
+
+	return 0;
+}
+
+static struct rtnl_link_info_ops veth_info_ops = {
+	.io_name		= "veth",
+	.io_parse		= veth_parse,
+	.io_dump = {
+	    [NL_DUMP_LINE]	= veth_dump_line,
+	    [NL_DUMP_DETAILS]	= veth_dump_details,
+	},
+	.io_clone		= veth_clone,
+	.io_put_attrs		= veth_put_attrs,
+};
+
+/** @cond SKIP */
+
+#define IS_VETH_LINK_ASSERT(link) \
+	if ((link)->l_info_ops != &veth_info_ops) { \
+		APPBUG("Link is not a veth link. set type \"veth\" first."); \
+		return NULL; \
+	}
+/** @endcond */
+
+/**
+ * @name VETH Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type veth
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_veth_alloc(void)
+{
+	struct rtnl_link *link, *peer;
+	int err;
+
+	if (!(link = rtnl_link_alloc()))
+		return NULL;
+	if (!(peer = rtnl_link_alloc())) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	if ((err = rtnl_link_set_type(link, "veth")) < 0) {
+		rtnl_link_put(peer);
+		rtnl_link_put(link);
+		return NULL;
+	}
+	if ((err = rtnl_link_set_type(peer, "veth")) < 0) {
+		rtnl_link_put(peer);
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	link->l_info = peer;
+	peer->l_info = link;
+	return link;
+}
+
+/**
+ * Get the peer link of a veth link
+ *
+ * @return the peer link object.
+ */
+struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link)
+{
+	IS_VETH_LINK_ASSERT(link);
+	return link->l_info;
+}
+
+/**
+ * Release a veth link and its peer
+ *
+ */
+void rtnl_link_veth_release(struct rtnl_link *link)
+{
+	struct rtnl_link *peer = rtnl_link_veth_get_peer(link);
+	rtnl_link_put(peer);
+	rtnl_link_put(link);
+}
+
+/**
+ * Check if link is a veth link
+ * @arg link		Link object
+ *
+ * @return True if link is a veth link, otherwise false is returned.
+ */
+int rtnl_link_is_veth(struct rtnl_link *link)
+{
+	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth");
+}
+
+/**
+ * Create a new kernel veth device
+ * @arg sock		netlink socket
+ * @arg name		name of the veth device or NULL
+ * @arg peer_name	name of its peer or NULL
+ *
+ * Creates a new veth device pair in the kernel. If no name is
+ * provided, the kernel will automatically pick a name of the
+ * form "veth%d" (e.g. veth0, veth1, etc.)
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_veth_add(struct nl_sock *sock, const char *name,
+                       const char *peer_name)
+{
+	struct rtnl_link *link, *peer;
+	int err = -NLE_NOMEM;
+
+	if (!(link = rtnl_link_veth_alloc()))
+		return -NLE_NOMEM;
+	peer = rtnl_link_veth_get_peer(link);
+
+	if (name && peer_name) {
+		rtnl_link_set_name(link, name);
+		rtnl_link_set_name(peer, peer_name);
+	}
+
+	err = rtnl_link_add(sock, link, NLM_F_CREATE);
+
+	rtnl_link_put(peer);
+	rtnl_link_put(link);
+
+        return err;
+}
+
+/** @} */
+
+static void __init veth_init(void)
+{
+	rtnl_link_register_info(&veth_info_ops);
+}
+
+static void __exit veth_exit(void)
+{
+	rtnl_link_unregister_info(&veth_info_ops);
+}
+
+/** @} */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9d72b88..3e1af27 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,6 +22,7 @@ check_PROGRAMS = \
 	test-create-bond \
 	test-create-vlan \
 	test-create-vxlan \
+	test-create-veth \
 	test-create-bridge \
 	test-delete-link \
 	test-socket-creation \
@@ -43,6 +44,7 @@ test_cache_mngr_SOURCES = test-cache-mngr.c
 test_create_bond_SOURCES = test-create-bond.c
 test_create_vlan_SOURCES = test-create-vlan.c
 test_create_vxlan_SOURCES = test-create-vxlan.c
+test_create_veth_SOURCES = test-create-veth.c
 test_create_bridge_SOURCES = test-create-bridge.c
 test_delete_link_SOURCES = test-delete-link.c
 test_genl_SOURCES = test-genl.c
diff --git a/tests/test-create-veth.c b/tests/test-create-veth.c
new file mode 100644
index 0000000..038353f
--- /dev/null
+++ b/tests/test-create-veth.c
@@ -0,0 +1,41 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/veth.h>
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_link *link;
+	struct nl_sock *sk;
+	int err;
+	struct rtnl_link *peer;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+#if 0
+	rtnl_link_veth_add(sk, "veth2", "veth3");
+#else
+	link = rtnl_link_veth_alloc();
+	if (!link) {
+		nl_perror(err, "Unable to alloc link");
+		return err;
+	}
+		
+	rtnl_link_set_name(link, "veth8");
+	peer = rtnl_link_veth_get_peer(link);
+	rtnl_link_set_name(peer, "veth9");
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+	printf("peer is %s\n", rtnl_link_get_name(peer));
+	rtnl_link_put(link);
+#endif
+	nl_close(sk);
+
+	return 0;
+}
-- 
1.8.1.4




More information about the libnl mailing list