[CRIU] [RFC] inet unbound sockets restore

Cyrill Gorcunov gorcunov at openvz.org
Sat May 12 10:24:10 EDT 2012


Pavel, take a look please, this patch brings ability to
restore unbound inet sockets, thus test unbound_sockets
become alive.

(ps. not for merge, I'll send out the whole series later
 once you confirm if this patch looks good for you)

	Cyrill
-------------- next part --------------
>From 91cd306e5a50cac2188e0e042978cd386305fcaa Mon Sep 17 00:00:00 2001
From: Cyrill Gorcunov <gorcunov at openvz.org>
Date: Sat, 12 May 2012 18:21:52 +0400
Subject: [PATCH] sockets: Restore unbound inet sockets

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 sk-inet.c |  185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 sockets.c |   10 +++-
 2 files changed, 184 insertions(+), 11 deletions(-)

diff --git a/sk-inet.c b/sk-inet.c
index 9b93d5e..56feb6f 100644
--- a/sk-inet.c
+++ b/sk-inet.c
@@ -1,3 +1,4 @@
+#include <sys/types.h>
 #include <sys/socket.h>
 #include <linux/netlink.h>
 #include <unistd.h>
@@ -79,6 +80,9 @@ static int can_dump_inet_sk(const struct inet_sk_desc *sk)
 			return 0;
 		}
 		break;
+	case TCP_CLOSE:
+		/* Trivial case, we just need to create a socket on restore */
+		break;
 	default:
 		pr_err("Unknown state %d\n", sk->state);
 		return 0;
@@ -90,20 +94,181 @@ static int can_dump_inet_sk(const struct inet_sk_desc *sk)
 #define tcp_connection(sk)	(((sk)->proto == IPPROTO_TCP) &&	\
 				 ((sk)->state == TCP_ESTABLISHED))
 
+static unsigned int self_sk_inode(int lfd)
+{
+	char path[64], buf[32], *tok;
+
+	snprintf(path, sizeof(path), "/proc/self/fd/%d", lfd);
+	readlink(path, buf, sizeof(buf));
+
+	tok = strtok(buf, "[");
+	tok = strtok(NULL, "]");
+	BUG_ON(!tok);
+
+	return atoi(tok);
+}
+
+static struct inet_sk_desc *gen_uncon_sk(int lfd, const struct fd_parms *p)
+{
+	struct inet_sk_desc *sk = xzalloc(sizeof(*sk));
+	union {
+		struct sockaddr_in	v4;
+		struct sockaddr_in6	v6;
+	} addr;
+	socklen_t len;
+	int ret = 0;
+
+	if (!sk)
+		goto err;
+
+#define __CR_GET_SOPT(lfd, n, v)					\
+	({								\
+		len = sizeof(*v);					\
+		int __r = getsockopt(lfd, SOL_SOCKET, n, v, &len);	\
+		if (__r)						\
+			pr_perror("getsockopt failed on %d", p->fd);	\
+		__r;							\
+	})
+
+	sk->sd.ino	= self_sk_inode(lfd);
+	sk->state	= TCP_CLOSE;
+
+	ret |= __CR_GET_SOPT(lfd, SO_DOMAIN, &sk->sd.family);
+	ret |= __CR_GET_SOPT(lfd, SO_TYPE, &sk->type);
+	ret |= __CR_GET_SOPT(lfd, SO_PROTOCOL, &sk->proto);
+
+#undef __CR_GET_SOPT
+
+	if (ret)
+		goto err;
+
+	switch (sk->sd.family) {
+	case AF_INET:
+	case AF_INET6:
+		break;
+	default:
+		pr_err("Unsupported socket family %d on %d\n",
+		       sk->sd.family, p->fd);
+		goto err;
+	}
+
+	switch (sk->type) {
+	case SOCK_DGRAM:
+	case SOCK_STREAM:
+		break;
+	default:
+		pr_err("Unsupported socket type %d on %d\n",
+		       sk->type, p->fd);
+		goto err;
+	}
+
+	switch (sk->proto) {
+	case IPPROTO_IP:
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+	case IPPROTO_UDPLITE:
+		break;
+	default:
+		pr_err("Unsupported socket proto %d on %d\n",
+		       sk->proto, p->fd);
+		goto err;
+	}
+
+	if (sk->sd.family == AF_INET) {
+		len = sizeof(addr.v4);
+		ret = getsockname(lfd, (struct sockaddr *)&addr.v4, &len);
+		if (ret) {
+			if (errno == ENOTCONN) {
+				ret = 0;
+				memzero(&addr.v6, sizeof(&addr.v6));
+			} else {
+				pr_perror("getsockname failed on %d", p->fd);
+				goto err;
+			}
+		}
+
+		sk->src_port = addr.v4.sin_port;
+		memcpy(sk->src_addr, &addr.v4.sin_addr.s_addr, sizeof(addr.v4.sin_addr.s_addr));
+
+		len = sizeof(addr.v4);
+		ret = getpeername(lfd, (struct sockaddr *)&addr.v4, &len);
+		if (ret) {
+			if (errno == ENOTCONN) {
+				ret = 0;
+				memzero(&addr.v6, sizeof(&addr.v6));
+			} else {
+				pr_perror("getpeername failed on %d", p->fd);
+				goto err;
+			}
+		}
+		sk->dst_port = addr.v4.sin_port;
+		memcpy(sk->dst_addr, &addr.v4.sin_addr.s_addr, sizeof(addr.v4.sin_addr.s_addr));
+	} else if (sk->sd.family == AF_INET6) {
+		len = sizeof(addr.v6);
+		ret = getsockname(lfd, (struct sockaddr *)&addr.v6, &len);
+		if (ret) {
+			if (errno == ENOTCONN) {
+				ret = 0;
+				memzero(&addr.v6, sizeof(&addr.v6));
+			} else {
+				pr_perror("getsockname failed on %d", p->fd);
+				goto err;
+			}
+		}
+
+		sk->src_port = addr.v6.sin6_port;
+		memcpy(sk->src_addr, &addr.v6.sin6_addr.s6_addr, sizeof(addr.v6.sin6_addr.s6_addr));
+
+		len = sizeof(addr.v6);
+		ret = getpeername(lfd, (struct sockaddr *)&addr.v6, &len);
+		if (ret) {
+			if (errno == ENOTCONN) {
+				ret = 0;
+				memzero(&addr.v6, sizeof(&addr.v6));
+			} else {
+				pr_perror("getpeername failed on %d", p->fd);
+				goto err;
+			}
+		}
+		sk->dst_port = addr.v6.sin6_port;
+		memcpy(sk->dst_addr, &addr.v6.sin6_addr.s6_addr, sizeof(addr.v6.sin6_addr.s6_addr));
+	} else
+		BUG_ON(1);
+
+	sk->wqlen = 16;
+
+	if (ret)
+		goto err;
+out:
+	return sk;
+err:
+	xfree(sk), sk = NULL;
+	goto out;
+}
+
 static int dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p)
 {
+	bool should_free_sk = false;
 	struct inet_sk_desc *sk;
 	struct inet_sk_entry ie;
+	int ret = -1;
 
 	sk = (struct inet_sk_desc *)lookup_socket(p->stat.st_ino);
-	if (!sk)
-		goto err;
+	if (!sk) {
+		sk = gen_uncon_sk(lfd, p);
+		if (!sk)
+			goto err;
+		//BUG_ON(1);
+		should_free_sk = true;
+	}
 
 	if (!can_dump_inet_sk(sk))
 		goto err;
 
-	if (sk->sd.already_dumped)
-		return 0;
+	if (sk->sd.already_dumped) {
+		ret = 0;
+		goto err;
+	}
 
 	memset(&ie, 0, sizeof(ie));
 
@@ -133,12 +298,14 @@ static int dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p)
 	sk->sd.already_dumped = 1;
 
 	if (tcp_connection(sk))
-		return dump_one_tcp(lfd, sk);
-
-	return 0;
+		ret = dump_one_tcp(lfd, sk);
+	else
+		ret = 0;
 
 err:
-	return -1;
+	if (should_free_sk)
+		xfree(sk);
+	return ret;
 }
 
 static const struct fdtype_ops inet_dump_ops = {
@@ -264,7 +431,7 @@ static int open_inet_sk(struct file_desc *d)
 	 * bind() and listen(), and that's all.
 	 */
 
-	if (inet_bind(sk, ii))
+	if (ii->ie.state != TCP_CLOSE && inet_bind(sk, ii))
 		goto err;
 
 	if (ii->ie.state == TCP_LISTEN) {
diff --git a/sockets.c b/sockets.c
index aa55c44..ef3355e 100644
--- a/sockets.c
+++ b/sockets.c
@@ -136,8 +136,14 @@ int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
 {
 	struct socket_desc *sk;
 	sk = lookup_socket(p->stat.st_ino);
-	if (!sk)
-		return -1;
+	if (!sk) {
+		/*
+		 * If socket is unconnected inet one, then
+		 * it might be not reported via netlink,
+		 * so try to dump it in a different way.
+		 */
+		return dump_one_inet(p, lfd, cr_fdset);
+	}
 
 	switch (sk->family) {
 	case AF_UNIX:
-- 
1.7.7.6



More information about the CRIU mailing list