[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