[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