[Libct] [PATCH v2 4/6] VZ containers: veth network implementation

Alexander Burluka aburluka at parallels.com
Wed Nov 12 04:52:18 PST 2014


Implementation + tests
---
 src/Makefile           |    2 +
 src/include/net.h      |    3 -
 src/include/net_util.h |   33 ++++++
 src/include/vz_net.h   |    8 ++
 src/net.c              |  297 ++++--------------------------------------------
 src/net_util.c         |  260 ++++++++++++++++++++++++++++++++++++++++++
 src/route.c            |    1 +
 src/vz.c               |    9 ++
 src/vz_net.c           |  183 +++++++++++++++++++++++++++++
 test/Makefile          |    2 +-
 test/vz_net_veth.c     |   54 +++++++++
 11 files changed, 572 insertions(+), 280 deletions(-)
 create mode 100644 src/include/net_util.h
 create mode 100644 src/include/vz_net.h
 create mode 100644 src/net_util.c
 create mode 100644 src/vz_net.c
 create mode 100644 test/vz_net_veth.c

diff --git a/src/Makefile b/src/Makefile
index ee98e2d..8d138bb 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -17,6 +17,8 @@ obj-y			+= devnodes.o
 obj-y			+= route.o
 obj-y			+= process.o
 obj-y			+= vz.o
+obj-y			+= vz_net.o
+obj-y			+= net_util.o
 obj-y			+= readelf.o
 
 cflags-y		+= -fPIC -Wa,--noexecstack -fno-stack-protector
diff --git a/src/include/net.h b/src/include/net.h
index 55dedcb..20c61e4 100644
--- a/src/include/net.h
+++ b/src/include/net.h
@@ -67,8 +67,5 @@ struct ct_net {
 
 extern const struct ct_net_ops *net_get_ops(enum ct_net_type);
 
-extern struct nl_sock *net_sock_open();
-extern void net_sock_close(struct nl_sock *sk);
-extern struct nl_cache *net_get_link_cache(struct nl_sock *sk);
 
 #endif /* __LIBCT_NET_H__ */
diff --git a/src/include/net_util.h b/src/include/net_util.h
new file mode 100644
index 0000000..cf54086
--- /dev/null
+++ b/src/include/net_util.h
@@ -0,0 +1,33 @@
+#ifndef __LIBCT_NET_UTILS_H__
+#define __LIBCT_NET_UTILS_H__
+
+#include "net.h"
+
+struct ct_net_host_nic {
+	struct ct_net n;
+	char *name;
+};
+
+struct ct_net_veth {
+	struct ct_net n;
+	struct ct_net peer;
+};
+
+extern void ct_net_init(ct_net_t n, const struct ct_net_ops *ops);
+extern void ct_net_clean(ct_net_t n);
+
+extern int net_dev_set_mtu(ct_net_t n, int mtu);
+extern int net_dev_set_mac_addr(ct_net_t n, char *addr);
+extern int net_dev_set_master(ct_net_t n, char *master);
+extern int net_dev_add_ip_addr(ct_net_t n, char *addr);
+extern void veth_stop(struct container *ct, struct ct_net *n);
+extern int veth_match(struct ct_net *n, void *arg);
+extern void veth_free(struct ct_net_veth *vn);
+extern struct ct_net_veth *cn2vn(struct ct_net *n);
+
+extern struct nl_sock *net_sock_open();
+extern void net_sock_close(struct nl_sock *sk);
+extern struct nl_cache *net_get_link_cache(struct nl_sock *sk);
+extern int net_link_apply(ct_net_t n);
+extern int net_add_ip_addrs(struct nl_sock *sk, ct_net_t n);
+#endif /* __LIBCT_NET_UTILS_H__ */
diff --git a/src/include/vz_net.h b/src/include/vz_net.h
new file mode 100644
index 0000000..c6a8d6c
--- /dev/null
+++ b/src/include/vz_net.h
@@ -0,0 +1,8 @@
+#ifndef __LIBCT_VZ_NET_H__
+#define __LIBCT_VZ_NET_H__
+
+#include "net.h"
+
+extern const struct ct_net_ops *vz_net_get_ops(enum ct_net_type);
+
+#endif /* __LIBCT_VZ_NET_H__ */
diff --git a/src/net.c b/src/net.c
index 81f288a..03bece0 100644
--- a/src/net.c
+++ b/src/net.c
@@ -4,7 +4,6 @@
 #include <time.h>
 
 #include <netinet/ether.h>
-
 #include <netlink/netlink.h>
 #include <netlink/route/link.h>
 #include <netlink/route/link/veth.h>
@@ -21,56 +20,11 @@
 #include "util.h"
 #include "err.h"
 #include "net.h"
+#include "net_util.h"
+#include "vz_net.h"
 #include "ct.h"
 
 /*
- * Generic Linux networking management
- */
-
-struct nl_sock *net_sock_open()
-{
-	struct nl_sock *sk;
-	int err;
-
-	sk = nl_socket_alloc();
-	if (sk == NULL)
-		return NULL;
-
-	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
-		nl_socket_free(sk);
-		pr_err("Unable to connect socket: %s", nl_geterror(err));
-		return NULL;
-	}
-
-	return sk;
-}
-
-void net_sock_close(struct nl_sock *sk)
-{
-	if (sk == NULL)
-		return;
-
-	nl_close(sk);
-	nl_socket_free(sk);
-
-	return;
-}
-
-struct nl_cache *net_get_link_cache(struct nl_sock *sk)
-{
-	struct nl_cache *cache;
-	int err;
-
-	err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache);
-	if (err) {
-		pr_err("Unable to alloc link cache: %s", nl_geterror(err));
-		return NULL;
-	}
-
-	return cache;
-}
-
-/*
  * VETH creation/removal
  */
 
@@ -109,8 +63,10 @@ int net_start(struct container *ct)
 			goto err;
 	}
 
+#ifndef VZ
 	if (net_route_setup(ct))
 		goto err;
+#endif
 
 	return 0;
 
@@ -128,134 +84,14 @@ void net_stop(struct container *ct)
 		cn->ops->stop(ct, cn);
 }
 
-static int __net_add_ip_addr(struct nl_sock *sk, ct_net_t n, char *saddr)
-{
-	struct rtnl_addr *addr;
-	struct nl_addr *l;
-	int err, ret = -1;
-
-	err = nl_addr_parse(saddr, AF_UNSPEC, &l);
-	if (err) {
-		pr_err("Unable to parse address: %s\n", nl_geterror(err));
-		return -1;
-	}
-
-	addr = rtnl_addr_alloc();
-	if (addr == NULL)
-		goto err;
-
-	rtnl_addr_set_local(addr, l);
-	rtnl_addr_set_ifindex(addr, n->ifidx);
-	err = rtnl_addr_add(sk, addr, 0);
-	if (err) {
-		pr_err("Unable to add %s: %s\n", saddr, nl_geterror(err));
-		goto err;
-	}
-
-	ret = 0;
-err:
-	nl_addr_put(l);
-	rtnl_addr_put(addr);
-	return ret;
-}
-
-static int net_add_ip_addrs(struct nl_sock *sk, ct_net_t n)
-{
-	struct ct_net_ip_addr *addr;
-
-	list_for_each_entry(addr, &n->ip_addrs, l) {
-		if (__net_add_ip_addr(sk, n, addr->addr))
-			goto err;
-	}
-
-	return 0;
-err:
-	// FIXME rollback
-	return -1;
-}
-
-static int __net_link_apply(char *name, ct_net_t n)
-{
-	struct rtnl_link *link = NULL, *orig = NULL;
-	struct nl_cache *cache = NULL;
-	struct nl_sock *sk;
-	int err = -1;
-
-	sk = net_sock_open();
-	if (sk == NULL)
-		return -1;
-
-	cache = net_get_link_cache(sk);
-	if (sk == NULL)
-		goto free;
-
-	orig = rtnl_link_get_by_name(cache, name);
-	if (orig == NULL)
-		goto free;
-
-	link = rtnl_link_alloc();
-	if (link == NULL)
-		goto free;
-
-	rtnl_link_set_name(link, n->name);
-
-	if (n->addr) {
-		struct nl_addr* addr;
-
-		addr = nl_addr_build(AF_LLC, ether_aton(n->addr), ETH_ALEN);
-		if (addr == NULL)
-			goto free;
-		rtnl_link_set_addr(link, addr);
-	}
-
-	if (n->master) {
-		int idx;
-
-		idx = rtnl_link_name2i(cache, n->master);
-		if (idx == 0)
-			goto free;
-
-		rtnl_link_set_master(link, idx);
-	}
-
-	if (n->mtu)
-		rtnl_link_set_mtu(link, n->mtu);
-
-	rtnl_link_set_flags(link, IFF_UP);
-
-	err = rtnl_link_change(sk, orig, link, 0);
-	if (err) {
-		pr_err("Unable to change link %s: %s", n->name, nl_geterror(err));
-		goto free;
-	}
-
-	err = -1;
-	if (nl_cache_refill(sk, cache))
-		goto free;
-
-	n->ifidx = rtnl_link_name2i(cache, n->name);
-	if ( n->ifidx == 0)
-		goto free;
-
-	if (net_add_ip_addrs(sk, n))
-		goto free;
-	err = 0;
-free:
-	rtnl_link_put(link);
-	rtnl_link_put(orig);
-	nl_cache_put(cache);
-	net_sock_close(sk);
-	return err;
-}
-
-static int net_link_apply(char *name, ct_net_t n, int pid)
+static int local_net_link_apply(ct_net_t n, int pid)
 {
 	int rst, ret;
 
 	if (pid > 0 && switch_ns(pid, &net_ns, &rst))
 		return -1;
 
-	ret = __net_link_apply(name, n);
+	ret = net_link_apply(n);
 
 	if (pid > 0)
 		restore_ns(rst, &net_ns);
@@ -279,7 +115,11 @@ ct_net_t local_net_add(ct_handler_t h, enum ct_net_type ntype, void *arg)
 	if (ntype == CT_NET_NONE)
 		return 0;
 
+#ifndef VZ
 	nops = net_get_ops(ntype);
+#else
+	nops = vz_net_get_ops(ntype);
+#endif
 	if (!nops)
 		return ERR_PTR(-LCTERR_BADTYPE);
 
@@ -320,42 +160,6 @@ int local_net_del(ct_handler_t h, enum ct_net_type ntype, void *arg)
 	return -LCTERR_NOTFOUND;
 }
 
-int local_net_dev_set_mac_addr(ct_net_t n, char *addr)
-{
-	return set_string(&n->addr, addr);
-}
-
-int local_net_dev_set_master(ct_net_t n, char *master)
-{
-	return set_string(&n->master, master);
-}
-
-int local_net_dev_add_ip_addr(ct_net_t n, char *addr)
-{
-	struct ct_net_ip_addr *a;
-
-	a = xzalloc(sizeof(*a));
-	if (a == NULL)
-		return -1;
-
-	a->addr = xstrdup(addr);
-	if (a->addr == NULL) {
-		xfree(a);
-		return -1;
-	}
-
-	list_add(&a->l, &n->ip_addrs);
-
-	return 0;
-}
-
-int local_net_dev_set_mtu(ct_net_t n, int mtu)
-{
-	n->mtu = mtu;
-
-	return 0;
-}
-
 ct_net_t libct_net_add(ct_handler_t ct, enum ct_net_type ntype, void *arg)
 {
 	return ct->ops->net_add(ct, ntype, arg);
@@ -386,35 +190,11 @@ int libct_net_dev_set_mtu(ct_net_t n, int mtu)
 	return n->ops->set_mtu(n, mtu);
 }
 
-static void ct_net_init(ct_net_t n, const struct ct_net_ops *ops)
-{
-	INIT_LIST_HEAD(&n->ip_addrs);
-	n->name = NULL;
-	n->ops = ops;
-}
-
-static void ct_net_clean(ct_net_t n)
-{
-	struct ct_net_ip_addr *addr, *t;
 
-	xfree(n->name);
-	xfree(n->addr);
-	xfree(n->master);
-
-	list_for_each_entry_safe(addr, t, &n->ip_addrs, l) {
-		xfree(addr->addr);
-		xfree(addr);
-	}
-}
 /*
  * CT_NET_HOSTNIC management
  */
 
-struct ct_net_host_nic {
-	struct ct_net n;
-	char *name;
-};
-
 static inline struct ct_net_host_nic *cn2hn(struct ct_net *n)
 {
 	return container_of(n, struct ct_net_host_nic, n);
@@ -486,7 +266,7 @@ static int host_nic_start(struct container *ct, struct ct_net *n)
 		goto free;
 	}
 
-	if (net_link_apply(n->name, n, ct->root_pid))
+	if (local_net_link_apply(n, ct->root_pid))
 		return -1;
 free:
 	rtnl_link_put(link);
@@ -518,33 +298,16 @@ static const struct ct_net_ops host_nic_ops = {
 	.start		= host_nic_start,
 	.stop		= host_nic_stop,
 	.match		= host_nic_match,
-	.set_mac_addr	= local_net_dev_set_mac_addr,
-	.set_master	= local_net_dev_set_master,
-	.add_ip_addr	= local_net_dev_add_ip_addr,
-	.set_mtu	= local_net_dev_set_mtu,
+	.set_mac_addr	= net_dev_set_mac_addr,
+	.set_master	= net_dev_set_master,
+	.add_ip_addr	= net_dev_add_ip_addr,
+	.set_mtu	= net_dev_set_mtu,
 };
 
 /*
  * CT_NET_VETH management
  */
 
-struct ct_net_veth {
-	struct ct_net n;
-	struct ct_net peer;
-};
-
-static struct ct_net_veth *cn2vn(struct ct_net *n)
-{
-	return container_of(n, struct ct_net_veth, n);
-}
-
-static void veth_free(struct ct_net_veth *vn)
-{
-	ct_net_clean(&vn->n);
-	ct_net_clean(&vn->peer);
-	xfree(vn);
-}
-
 static struct ct_net *veth_create(void *arg, const struct ct_net_ops *ops)
 {
 	struct ct_net_veth_arg *va = arg;
@@ -608,9 +371,9 @@ static int veth_start(struct container *ct, struct ct_net *n)
 		goto err;
 	}
 
-	if (net_link_apply(name, n, ct->root_pid))
+	if (local_net_link_apply(n, ct->root_pid))
 		goto err;
-	if (net_link_apply(vn->peer.name, &vn->peer, -1))
+	if (local_net_link_apply(&vn->peer, -1))
 		goto err; /* FIXME rollback */
 
 	ret = 0;
@@ -620,34 +383,16 @@ err:
 	return ret;
 }
 
-static void veth_stop(struct container *ct, struct ct_net *n)
-{
-	/* 
-	 * FIXME -- don't destroy veth here, keep it across
-	 * container's restarts. This needs checks in the
-	 * veth_pair_create() for existance.
-	 */
-}
-
-static int veth_match(struct ct_net *n, void *arg)
-{
-	struct ct_net_veth *vn = cn2vn(n);
-	struct ct_net_veth_arg *va = arg;
-
-	/* Matching hostname should be enough */
-	return !strcmp(vn->peer.name, va->host_name);
-}
-
 static const struct ct_net_ops veth_nic_ops = {
 	.create		= veth_create,
 	.destroy	= veth_destroy,
 	.start		= veth_start,
 	.stop		= veth_stop,
 	.match		= veth_match,
-	.set_mac_addr	= local_net_dev_set_mac_addr,
-	.set_master	= local_net_dev_set_master,
-	.add_ip_addr	= local_net_dev_add_ip_addr,
-	.set_mtu	= local_net_dev_set_mtu,
+	.set_mac_addr	= net_dev_set_mac_addr,
+	.set_master	= net_dev_set_master,
+	.add_ip_addr	= net_dev_add_ip_addr,
+	.set_mtu	= net_dev_set_mtu,
 };
 
 const struct ct_net_ops *net_get_ops(enum ct_net_type ntype)
diff --git a/src/net_util.c b/src/net_util.c
new file mode 100644
index 0000000..881ed98
--- /dev/null
+++ b/src/net_util.c
@@ -0,0 +1,260 @@
+#include <stdlib.h>
+#include <netinet/ether.h>
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/veth.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/route.h>
+#include <netlink/route/nexthop.h>
+
+#include "net_util.h"
+#include "xmalloc.h"
+#include "util.h"
+
+/*
+ * Generic Linux networking management
+ */
+
+struct nl_sock *net_sock_open()
+{
+	struct nl_sock *sk;
+	int err;
+
+	sk = nl_socket_alloc();
+	if (sk == NULL)
+		return NULL;
+
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_socket_free(sk);
+		pr_err("Unable to connect socket: %s", nl_geterror(err));
+		return NULL;
+	}
+
+	return sk;
+}
+
+void net_sock_close(struct nl_sock *sk)
+{
+	if (sk == NULL)
+		return;
+
+	nl_close(sk);
+	nl_socket_free(sk);
+
+	return;
+}
+
+struct nl_cache *net_get_link_cache(struct nl_sock *sk)
+{
+	struct nl_cache *cache;
+	int err;
+
+	err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache);
+	if (err) {
+		pr_err("Unable to alloc link cache: %s", nl_geterror(err));
+		return NULL;
+	}
+
+	return cache;
+}
+
+static int __net_add_ip_addr(struct nl_sock *sk, ct_net_t n, char *saddr)
+{
+	struct rtnl_addr *addr;
+	struct nl_addr *l;
+	int err, ret = -1;
+
+	err = nl_addr_parse(saddr, AF_UNSPEC, &l);
+	if (err) {
+		pr_err("Unable to parse address: %s\n", nl_geterror(err));
+		return -1;
+	}
+
+	addr = rtnl_addr_alloc();
+	if (addr == NULL)
+		goto err;
+
+	rtnl_addr_set_local(addr, l);
+	rtnl_addr_set_ifindex(addr, n->ifidx);
+	err = rtnl_addr_add(sk, addr, 0);
+	if (err) {
+		pr_err("Unable to add %s: %s\n", saddr, nl_geterror(err));
+		goto err;
+	}
+
+	ret = 0;
+err:
+	nl_addr_put(l);
+	rtnl_addr_put(addr);
+	return ret;
+}
+
+int net_link_apply(ct_net_t n)
+{
+	struct rtnl_link *link = NULL, *orig = NULL;
+	struct nl_cache *cache = NULL;
+	struct nl_sock *sk;
+	int err = -1;
+
+	sk = net_sock_open();
+	if (sk == NULL)
+		return -1;
+
+	cache = net_get_link_cache(sk);
+	if (sk == NULL)
+		goto free;
+
+	orig = rtnl_link_get_by_name(cache, n->name);
+	if (orig == NULL)
+		goto free;
+
+	link = rtnl_link_alloc();
+	if (link == NULL)
+		goto free;
+
+	rtnl_link_set_name(link, n->name);
+
+	if (n->addr) {
+		struct nl_addr* addr;
+
+		addr = nl_addr_build(AF_LLC, ether_aton(n->addr), ETH_ALEN);
+		if (addr == NULL)
+			goto free;
+		rtnl_link_set_addr(link, addr);
+	}
+
+	if (n->master) {
+		int idx;
+
+		idx = rtnl_link_name2i(cache, n->master);
+		if (idx == 0)
+			goto free;
+
+		rtnl_link_set_master(link, idx);
+	}
+
+	rtnl_link_set_flags(link, IFF_UP);
+
+	err = rtnl_link_change(sk, orig, link, 0);
+	if (err) {
+		pr_err("Unable to change link %s: %s", n->name, nl_geterror(err));
+		goto free;
+	}
+
+	err = -1;
+	if (nl_cache_refill(sk, cache))
+		goto free;
+
+	n->ifidx = rtnl_link_name2i(cache, n->name);
+	if ( n->ifidx == 0)
+		goto free;
+
+	if (net_add_ip_addrs(sk, n))
+		goto free;
+	err = 0;
+free:
+	rtnl_link_put(link);
+	rtnl_link_put(orig);
+	nl_cache_put(cache);
+	net_sock_close(sk);
+	return err;
+}
+
+
+int net_add_ip_addrs(struct nl_sock *sk, ct_net_t n)
+{
+	struct ct_net_ip_addr *addr;
+
+	list_for_each_entry(addr, &n->ip_addrs, l) {
+		if (__net_add_ip_addr(sk, n, addr->addr))
+			goto err;
+	}
+
+	return 0;
+err:
+	// FIXME rollback
+	return -1;
+}
+
+
+void ct_net_init(ct_net_t n, const struct ct_net_ops *ops)
+{
+	INIT_LIST_HEAD(&n->ip_addrs);
+	n->name = NULL;
+	n->ops = ops;
+}
+
+void ct_net_clean(ct_net_t n)
+{
+	struct ct_net_ip_addr *addr, *t;
+
+	xfree(n->name);
+	xfree(n->addr);
+	xfree(n->master);
+
+	list_for_each_entry_safe(addr, t, &n->ip_addrs, l) {
+		xfree(addr->addr);
+		xfree(addr);
+	}
+}
+
+int net_dev_set_mtu(ct_net_t n, int mtu)
+{
+	n->mtu = mtu;
+
+	return 0;
+}
+
+int net_dev_set_mac_addr(ct_net_t n, char *addr)
+{
+	return set_string(&n->addr, addr);
+}
+
+int net_dev_set_master(ct_net_t n, char *master)
+{
+	return set_string(&n->master, master);
+}
+
+int net_dev_add_ip_addr(ct_net_t n, char *addr)
+{
+	struct ct_net_ip_addr *a;
+
+	a = xzalloc(sizeof(*a));
+	if (a == NULL)
+		return -1;
+
+	a->addr = xstrdup(addr);
+	if (a->addr == NULL) {
+		xfree(a);
+		return -1;
+	}
+
+	list_add(&a->l, &n->ip_addrs);
+
+	return 0;
+}
+
+void veth_stop(struct container *ct, struct ct_net *n)
+{
+	/* TODO: Ask Pavel about veth stop algo
+	 * FIXME -- don't destroy veth here, keep it across
+	 * container's restarts. This needs checks in the
+	 * veth_pair_create() for existance.
+	 */
+}
+
+int veth_match(struct ct_net *n, void *arg)
+{
+	struct ct_net_veth *vn = cn2vn(n);
+	struct ct_net_veth_arg *va = arg;
+
+	/* Matching hostname should be enough */
+	return !strcmp(vn->peer.name, va->host_name);
+}
+
+void veth_free(struct ct_net_veth *vn)
+{
+	ct_net_clean(&vn->n);
+	ct_net_clean(&vn->peer);
+	xfree(vn);
+}
diff --git a/src/route.c b/src/route.c
index fd1a3b0..db9c44d 100644
--- a/src/route.c
+++ b/src/route.c
@@ -12,6 +12,7 @@
 #include "err.h"
 #include "net.h"
 #include "ct.h"
+#include "net_util.h"
 
 void net_route_nh_free(ct_net_route_nh_t nh)
 {
diff --git a/src/vz.c b/src/vz.c
index 2b6e150..ea0d0a7 100644
--- a/src/vz.c
+++ b/src/vz.c
@@ -34,6 +34,7 @@
 #include "cgroups.h"
 #include "net.h"
 #include "util.h"
+#include "vz_net.h"
 
 #define MAX_SHTD_TM 			120
 #define VZCTLDEV			"/dev/vzctl"
@@ -156,6 +157,7 @@ static void vz_ct_destroy(ct_handler_t h)
 	struct container *ct = cth2ct(h);
 
 	fs_free(ct);
+	net_release(ct);
 
 	xfree(ct->name);
 	xfree(ct->hostname);
@@ -734,6 +736,12 @@ static int vz_spawn_execve(ct_handler_t h, ct_process_desc_t p, char *path, char
 		goto err_net;
 	}
 
+	ret = net_start(ct);
+	if (ret) {
+		pr_err("Unable to start network");
+		goto err_net;
+	}
+
 	proc_wake(child_wait, 0);
 
 	/* Wait while network would be configured inside container */
@@ -747,6 +755,7 @@ static int vz_spawn_execve(ct_handler_t h, ct_process_desc_t p, char *path, char
 	return ct->root_pid;
 
 err_wait:
+	net_stop(ct);
 err_net:
 	proc_wake_close(child_wait, -1);
 err_fork:
diff --git a/src/vz_net.c b/src/vz_net.c
new file mode 100644
index 0000000..d8fc402
--- /dev/null
+++ b/src/vz_net.c
@@ -0,0 +1,183 @@
+#include <linux/vzctl_veth.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/veth.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/route.h>
+#include <netlink/route/nexthop.h>
+
+#include "vz_net.h"
+#include "vz.h"
+#include "log.h"
+#include "xmalloc.h"
+#include "net_util.h"
+#include "util.h"
+#include "ct.h"
+
+#define ETH_ALEN 6
+
+static int gen_hwaddr(unsigned char *buf, int size)
+{
+	int res = -1;
+	int fd = -1;
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd < 0)
+		return errno;
+
+	res = read(fd, buf, size);
+	if (res < 0) {
+		int _errno = errno;
+		close(fd);
+		return _errno;
+	}
+
+	close(fd);
+	if (res != size)
+		return EINVAL;
+
+	/* use locally administrated address */
+	buf[0] = 0xfe;
+
+	return 0;
+}
+
+static int vz_veth_ioctl(int op_type, struct container *ct, const char *pair0, const char *pair1)
+{
+	struct vzctl_ve_hwaddr veth;
+	int ret = -1;
+	unsigned int veid = 0;
+	if (ct) {
+		ret = parse_uint(ct->name, &veid);
+		if (ret) {
+			pr_err("Unable to parse container's ID");
+			return -1;
+		}
+	} else {
+		veid = 0;
+	}
+
+	veth.op = op_type;
+	veth.veid = veid;
+	veth.addrlen = ETH_ALEN;
+	veth.addrlen_ve = ETH_ALEN;
+
+	ret = gen_hwaddr(veth.dev_addr, ETH_ALEN);
+	if (ret) {
+		pr_err("Failed to gen_hwaddr: err=%d", ret);
+		return -1;
+	}
+	ret = gen_hwaddr(veth.dev_addr_ve, ETH_ALEN);
+	if (ret) {
+		pr_err("Failed to gen_hwaddr: err=%d", ret);
+		return -1;
+	}
+	memcpy(veth.dev_name, pair0, sizeof(veth.dev_name));
+	memcpy(veth.dev_name_ve, pair1, sizeof(veth.dev_name_ve));
+
+	ret = ioctl(get_vzctlfd(), VETHCTL_VE_HWADDR, &veth);
+	if (ret) {
+		if (errno == ENOTTY) {
+			pr_err("veth feature is"
+					" not supported by the kernel");
+		} else {
+			pr_err("Unable to perform operation %d on veth device"
+					" pair %s %s err=%d",
+					op_type, pair0, pair1, errno);
+			pr_perror("Error");
+		}
+		return -1;
+	}
+	return 0;
+}
+
+static struct ct_net *vz_veth_create(void *arg, struct ct_net_ops const *ops)
+{
+	struct ct_net_veth_arg *va = arg;
+	struct ct_net_veth *vn = NULL;
+
+	if (!arg || !va->host_name || !va->ct_name)
+		return NULL;
+
+	vn = xzalloc(sizeof(*vn));
+	if (!vn)
+		return NULL;
+
+	ct_net_init(&vn->n, ops);
+	ct_net_init(&vn->peer, ops);
+
+	vn->peer.name = xstrdup(va->host_name);
+	vn->n.name = xstrdup(va->ct_name);
+	if (!vn->peer.name || !vn->n.name) {
+		xfree(vn->peer.name);
+		xfree(vn->n.name);
+		veth_free(vn);
+		return NULL;
+	}
+
+	return &vn->n;
+
+	return NULL;
+}
+
+static int vz_veth_start(struct container *ct, struct ct_net *n)
+{
+	struct ct_net_veth *vn = NULL;
+	int ret = -1;
+
+	if (!n)
+		return -LCTERR_BADARG;
+	vn = cn2vn(n);
+
+	ret = vz_veth_ioctl(VE_ETH_ADD, ct, vn->peer.name, vn->n.name);
+	if (ret)
+		return ret;
+	ret = vz_veth_ioctl(VE_ETH_ALLOW_MAC_CHANGE, ct, vn->peer.name, vn->n.name);
+	return ret;
+}
+
+static void vz_veth_destroy(struct ct_net *n)
+{
+	struct ct_net_veth *vn = NULL;
+	if (!n)
+		return;
+	vn = cn2vn(n);
+	/* TODO: Ask Pavel: should we explicitly remove veth */
+	/*vz_veth_ioctl(VE_ETH_DEL, NULL, vn->n.name, vn->peer.name);*/
+	veth_free(vn);
+}
+
+static const struct ct_net_ops vz_veth_nic_ops = {
+	.create		= vz_veth_create,
+	.destroy	= vz_veth_destroy,
+	.start		= vz_veth_start,
+	.stop		= veth_stop,
+	.match		= veth_match,
+	.set_mac_addr	= net_dev_set_mac_addr,
+	.set_master	= net_dev_set_master,
+	.add_ip_addr	= net_dev_add_ip_addr,
+	.set_mtu	= net_dev_set_mtu,
+};
+
+const struct ct_net_ops *vz_net_get_ops(enum ct_net_type ntype)
+{
+	switch (ntype) {
+	case CT_NET_VETH:
+		return &vz_veth_nic_ops;
+	default:
+		return NULL;
+	}
+}
+
+struct ct_net_veth *cn2vn(struct ct_net *n)
+{
+	return container_of(n, struct ct_net_veth, n);
+}
diff --git a/test/Makefile b/test/Makefile
index ac477da..2b879d7 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -4,7 +4,7 @@ TESTS =	ct_create ct_enter ct_proc ct_root ct_root_enter \
 	ct_ext_mount ct_private_subdir_ns \
 	ct_cgroup_sub ct_service ct_kill_nons ct_pid_enter \
 	ct_userns ct_caps \
-	vz_create_exec
+	vz_create_exec vz_net_veth
 
 PIGS  = file_piggy
 
diff --git a/test/vz_net_veth.c b/test/vz_net_veth.c
new file mode 100644
index 0000000..53a0bcc
--- /dev/null
+++ b/test/vz_net_veth.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <libct.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include "test.h"
+
+#define VETH_HOST_NAME	"hveth0"
+#define VETH_CT_NAME	"cveth0"
+
+int main(int argc, char *argv[])
+{
+	libct_session_t s;
+	ct_handler_t ct;
+	ct_net_t nd;
+	ct_process_desc_t p;
+	struct ct_net_veth_arg va = {
+		.host_name = VETH_HOST_NAME,
+		.ct_name = VETH_CT_NAME
+	};
+	char *ip_a[] = { "ip", "link", "show", NULL};
+	int fds[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
+
+	s = libct_session_open_local();
+
+	ct = libct_container_create(s, "1337");
+	p = libct_process_desc_create(s);
+	libct_container_set_nsmask(ct,
+			CLONE_NEWNS |
+			CLONE_NEWUTS |
+			CLONE_NEWIPC |
+			CLONE_NEWNET |
+			CLONE_NEWPID);
+
+	nd = libct_net_add(ct, CT_NET_VETH, &va);
+	if (libct_handle_is_err(nd))
+		return err("Can't add hostnic");
+
+	if (libct_net_dev_set_mac_addr(nd, "00:11:22:33:44:55"))
+		return err("Can't set mac");
+
+	if (libct_container_spawn_execvfds(ct, p, "/sbin/ip", ip_a, fds) <= 0)
+		goto err;
+
+	libct_container_wait(ct);
+	libct_container_destroy(ct);
+
+	libct_session_close(s);
+
+	return pass("All is ok");;
+err:
+	return fail("Something wrong");
+}
-- 
1.7.1



More information about the Libct mailing list