[CRIU] [PATCH 02/12] kerndat: check whether a kernel supports netns id-s or not
Andrei Vagin
avagin at openvz.org
Tue Feb 28 15:52:58 PST 2017
From: Andrei Vagin <avagin at virtuozzo.com>
Each network namespaces has a list of ID-s for other namespaces,
so if we request infomation about a veth device, we get an id
for a namespace of a peer device.
These ID-s can be set by users or by kernel when they are required.
CRIU has to restore these ID-s for network namespaces. We have to
remember that one netns can have different id-s in different network
namespaces.
Signed-off-by: Andrei Vagin <avagin at virtuozzo.com>
---
criu/cr-check.c | 28 +++++++
criu/include/kerndat.h | 2 +
criu/include/net.h | 4 +
criu/kerndat.c | 26 ++++++
criu/net.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 276 insertions(+)
diff --git a/criu/cr-check.c b/criu/cr-check.c
index c826125..3398d11 100644
--- a/criu/cr-check.c
+++ b/criu/cr-check.c
@@ -1232,6 +1232,32 @@ static int check_tun(void)
return check_tun_cr(-1);
}
+static int check_nsid(void)
+{
+ if (kerndat_nsid() < 0)
+ return -1;
+
+ if (!kdat.has_nsid) {
+ pr_warn("NSID isn't supported\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_link_nsid(void)
+{
+ if (kerndat_link_nsid() < 0)
+ return -1;
+
+ if (!kdat.has_link_nsid) {
+ pr_warn("NSID isn't supported\n");
+ return -1;
+ }
+
+ return 0;
+}
+
struct feature_list {
char *name;
int (*func)();
@@ -1254,6 +1280,8 @@ static struct feature_list feature_list[] = {
{ "lazy_pages", check_uffd },
{ "compat_cr", check_compat_cr },
{ "sk_ns", check_sk_netns },
+ { "nsid", check_nsid },
+ { "link_nsid", check_link_nsid},
{ NULL, NULL },
};
diff --git a/criu/include/kerndat.h b/criu/include/kerndat.h
index e41768f..60c69c3 100644
--- a/criu/include/kerndat.h
+++ b/criu/include/kerndat.h
@@ -41,6 +41,8 @@ struct kerndat_s {
unsigned long mmap_min_addr;
bool has_tcp_half_closed;
unsigned long uffd_features;
+ bool has_nsid;
+ bool has_link_nsid;
};
extern struct kerndat_s kdat;
diff --git a/criu/include/net.h b/criu/include/net.h
index 49eca36..9af6f44 100644
--- a/criu/include/net.h
+++ b/criu/include/net.h
@@ -43,4 +43,8 @@ extern int veth_pair_add(char *in, char *out);
extern int macvlan_ext_add(struct external *ext);
extern int move_veth_to_bridge(void);
+extern int kerndat_link_nsid(void);
+extern int net_get_nsid(int rtsk, int fd, int *nsid);
+extern int kerndat_nsid(void);
+
#endif /* __CR_NET_H__ */
diff --git a/criu/kerndat.c b/criu/kerndat.c
index 162ac28..354c2a5 100644
--- a/criu/kerndat.c
+++ b/criu/kerndat.c
@@ -28,6 +28,7 @@
#include "config.h"
#include "sk-inet.h"
#include "sockets.h"
+#include "net.h"
#include <compel/plugins/std/syscall-codes.h>
#include <compel/compel.h>
#include "linux/userfaultfd.h"
@@ -560,6 +561,27 @@ err:
return exit_code;
}
+int kerndat_nsid(void)
+{
+ int nsid, sk;
+
+ sk = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sk < 0) {
+ pr_perror("Unable to create a netlink socket");
+ return -1;
+ }
+
+ if (net_get_nsid(sk, getpid(), &nsid) < 0) {
+ pr_err("NSID is not supported\n");
+ close(sk);
+ return -1;
+ }
+
+ kdat.has_nsid = true;
+ close(sk);
+ return 0;
+}
+
static int kerndat_compat_restore(void)
{
int ret = kdat_compat_sigreturn_test();
@@ -637,6 +659,8 @@ int kerndat_init(void)
ret = kerndat_compat_restore();
if (!ret)
ret = kerndat_socket_netns();
+ if (!ret)
+ ret = kerndat_nsid();
kerndat_lsm();
kerndat_mmap_min_addr();
@@ -675,6 +699,8 @@ int kerndat_init_rst(void)
ret = kerndat_uffd(opts.lazy_pages);
if (!ret)
ret = kerndat_socket_netns();
+ if (!ret)
+ ret = kerndat_nsid();
kerndat_lsm();
kerndat_mmap_min_addr();
diff --git a/criu/net.c b/criu/net.c
index 309e2bd..1dc0157 100644
--- a/criu/net.c
+++ b/criu/net.c
@@ -13,7 +13,9 @@
#include <sys/types.h>
#include <net/if.h>
#include <linux/sockios.h>
+#include <libnl3/netlink/attr.h>
#include <libnl3/netlink/msg.h>
+#include <libnl3/netlink/netlink.h>
#include "../soccr/soccr.h"
@@ -41,6 +43,8 @@
#ifndef IFLA_LINK_NETNSID
#define IFLA_LINK_NETNSID 37
+#undef IFLA_MAX
+#define IFLA_MAX IFLA_LINK_NETNSID
#endif
#ifndef RTM_NEWNSID
@@ -2230,3 +2234,215 @@ int move_veth_to_bridge(void)
return ret;
}
+
+#if NLA_TYPE_MAX < 14
+#define NLA_S32 14
+#endif
+
+#ifndef NETNSA_MAX
+/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */
+enum {
+ NETNSA_NONE,
+#define NETNSA_NSID_NOT_ASSIGNED -1
+ NETNSA_NSID,
+ NETNSA_PID,
+ NETNSA_FD,
+ __NETNSA_MAX,
+};
+
+#define NETNSA_MAX (__NETNSA_MAX - 1)
+#endif
+
+static struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = {
+ [NETNSA_NONE] = { .type = NLA_UNSPEC },
+ [NETNSA_NSID] = { .type = NLA_S32 },
+ [NETNSA_PID] = { .type = NLA_U32 },
+ [NETNSA_FD] = { .type = NLA_U32 },
+};
+
+static int nsid_cb(struct nlmsghdr *msg, struct ns_id *ns, void *arg)
+{
+ struct nlattr *tb[NETNSA_MAX + 1];
+ int err;
+
+ err = nlmsg_parse(msg, sizeof(struct rtgenmsg), tb,
+ NETNSA_MAX, rtnl_net_policy);
+ if (err < 0)
+ return NL_STOP;
+
+ if (tb[NETNSA_NSID])
+ *((int *)arg) = nla_get_s32(tb[NETNSA_NSID]);
+
+ return 0;
+}
+
+int net_get_nsid(int rtsk, int pid, int *nsid)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ char msg[128];
+ } req;
+ int32_t id = INT_MIN;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+ req.nlh.nlmsg_type = RTM_GETNSID;
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nlh.nlmsg_seq = CR_NLMSG_SEQ;
+ if (addattr_l(&req.nlh, sizeof(req), NETNSA_PID, &pid, sizeof(pid)))
+ return -1;
+
+ if (do_rtnl_req(rtsk, &req, req.nlh.nlmsg_len, nsid_cb, NULL, NULL, (void *) &id) < 0)
+ return -1;
+
+ if (id == INT_MIN)
+ return -1;
+
+ *nsid = id;
+
+ return 0;
+}
+
+
+static int nsid_link_info(NetDeviceEntry *nde, struct newlink_req *req)
+{
+ struct rtattr *veth_data, *peer_data;
+ struct ifinfomsg ifm;
+
+ addattr_l(&req->h, sizeof(*req), IFLA_INFO_KIND, "veth", 4);
+
+ veth_data = NLMSG_TAIL(&req->h);
+ addattr_l(&req->h, sizeof(*req), IFLA_INFO_DATA, NULL, 0);
+ peer_data = NLMSG_TAIL(&req->h);
+ memset(&ifm, 0, sizeof(ifm));
+
+ ifm.ifi_index = nde->peer_ifindex;
+ addattr_l(&req->h, sizeof(*req), VETH_INFO_PEER, &ifm, sizeof(ifm));
+
+ addattr_l(&req->h, sizeof(*req), IFLA_NET_NS_FD, &nde->peer_nsid, sizeof(int));
+ peer_data->rta_len = (void *)NLMSG_TAIL(&req->h) - (void *)peer_data;
+ veth_data->rta_len = (void *)NLMSG_TAIL(&req->h) - (void *)veth_data;
+
+ return 0;
+}
+
+static int check_one_link_nsid(struct nlmsghdr *hdr, struct ns_id *ns, void *arg)
+{
+ bool *has_link_nsid = arg;
+ struct ifinfomsg *ifi;
+ int len = hdr->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
+ struct nlattr *tb[IFLA_MAX + 1];
+
+ ifi = NLMSG_DATA(hdr);
+
+ if (len < 0) {
+ pr_err("No iflas for link %d\n", ifi->ifi_index);
+ return -1;
+ }
+
+ nlmsg_parse(hdr, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL);
+ pr_info("\tLD: Got link %d, type %d\n", ifi->ifi_index, ifi->ifi_type);
+
+ if (tb[IFLA_LINK_NETNSID])
+ *has_link_nsid = true;
+
+ return 0;
+}
+
+static int check_link_nsid(int rtsk, void *args)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+
+ pr_info("Dumping netns links\n");
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = RTM_GETLINK;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = CR_NLMSG_SEQ;
+ req.g.rtgen_family = AF_PACKET;
+
+ return do_rtnl_req(rtsk, &req, sizeof(req), check_one_link_nsid, NULL, NULL, args);
+}
+
+int kerndat_link_nsid()
+{
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ pr_perror("Unable to fork a process");
+ return -1;
+ }
+
+ if (pid == 0) {
+ NetDeviceEntry nde = NET_DEVICE_ENTRY__INIT;
+ int nsfd, sk, ret;
+
+ sk = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sk < 0) {
+ pr_perror("Unable to create a netlink socket");
+ exit(1);
+ }
+
+ if (unshare(CLONE_NEWNET)) {
+ pr_perror("Unable create a network namespace");
+ exit(1);
+ }
+
+ nsfd = open_proc(PROC_SELF, "ns/net");
+ if (nsfd < 0)
+ exit(1);
+
+ if (unshare(CLONE_NEWNET)) {
+ pr_perror("Unable create a network namespace");
+ exit(1);
+ }
+
+ nde.type = ND_TYPE__VETH;
+ nde.name = "veth";
+ nde.ifindex = 10;
+ nde.mtu = 1500;
+ nde.peer_nsid = nsfd;
+ nde.peer_ifindex = 11;
+ nde.has_peer_ifindex = true;
+ nde.has_peer_nsid = true;
+
+ ret = restore_one_link(&nde, sk, nsid_link_info, NULL);
+ if (ret) {
+ pr_err("Unable to create a veth pair: %d\n", ret);
+ exit(1);
+ }
+
+ bool has_link_nsid = false;
+ if (check_link_nsid(sk, &has_link_nsid))
+ exit(1);
+
+ if (!has_link_nsid)
+ exit(5);
+
+ close(sk);
+
+ exit(0);
+ }
+
+ if (waitpid(pid, &status, 0) != pid) {
+ pr_perror("Unable to wait a process");
+ return -1;
+ }
+
+ if (status) {
+ pr_warn("NSID isn't reported for network links\n");
+ return -1;
+ }
+
+ kdat.has_link_nsid = true;
+
+ return 0;
+}
--
2.7.4
More information about the CRIU
mailing list