Commit 37131e51 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

sockets: Restore unbound inet sockets v5

v2:
 - Use do_dump_opt in dump_socket to not call
   lookup_socket redundantly

v3:
 - use getsockopt to check that unconnected
   socket has no peername and it's not listening
 - do test only socket protocol since family and
   type will be called in dump_one_inet_fd.

v4:
 - Move proto tests to can_dump_inet_sk
 - Use SOL_TCP/TCP_INFO to gather info about tcp sockets

v5:
 - hash unconnected sockets to run BUG_ON(already-dumped) logic
 - no default value for sk->wqlen, use zero from xzalloc
 - drop bogus xfree on error
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 877f8c82
...@@ -53,4 +53,6 @@ extern int dump_one_unix(struct fd_parms *p, int lfd, const struct cr_fdset *set ...@@ -53,4 +53,6 @@ extern int dump_one_unix(struct fd_parms *p, int lfd, const struct cr_fdset *set
extern int inet_collect_one(struct nlmsghdr *h, int family, int type, int proto); extern int inet_collect_one(struct nlmsghdr *h, int family, int type, int proto);
extern int unix_receive_one(struct nlmsghdr *h); extern int unix_receive_one(struct nlmsghdr *h);
extern int do_dump_opt(int sk, int name, void *val, int len);
#endif /* CR_SOCKETS_H__ */ #endif /* CR_SOCKETS_H__ */
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <unistd.h> #include <unistd.h>
...@@ -81,25 +82,99 @@ static int can_dump_inet_sk(const struct inet_sk_desc *sk) ...@@ -81,25 +82,99 @@ static int can_dump_inet_sk(const struct inet_sk_desc *sk)
return 0; return 0;
} }
break; break;
case TCP_CLOSE:
/* Trivial case, we just need to create a socket on restore */
break;
default: default:
pr_err("Unknown state %d\n", sk->state); pr_err("Unknown state %d\n", sk->state);
return 0; return 0;
} }
/* Make sure it's a proto we support */
switch (sk->proto) {
case IPPROTO_IP:
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
break;
default:
pr_err("Unsupported socket proto %d\n", sk->proto);
return 0;
}
return 1; return 1;
} }
#define tcp_connection(sk) (((sk)->proto == IPPROTO_TCP) && \ #define tcp_connection(sk) (((sk)->proto == IPPROTO_TCP) && \
((sk)->state == TCP_ESTABLISHED)) ((sk)->state == TCP_ESTABLISHED))
static struct inet_sk_desc *gen_uncon_sk(int lfd, const struct fd_parms *p)
{
struct inet_sk_desc *sk;
char address[128];
socklen_t aux;
int ret;
sk = xzalloc(sizeof(*sk));
if (!sk)
goto err;
/* It should has no peer name */
aux = sizeof(address);
ret = getsockopt(lfd, SOL_SOCKET, SO_PEERNAME, address, &aux);
if (ret != -1 || errno != ENOTCONN) {
pr_err("Errno %d returned from unconnected socket\n", errno);
goto err;
}
sk->sd.ino = p->stat.st_ino;
ret = do_dump_opt(lfd, SO_DOMAIN, &sk->sd.family, sizeof(sk->sd.family));
ret |= do_dump_opt(lfd, SO_TYPE, &sk->type, sizeof(sk->type));
ret |= do_dump_opt(lfd, SO_PROTOCOL, &sk->proto, sizeof(sk->proto));
if (ret)
goto err;
if (sk->proto == IPPROTO_TCP) {
struct tcp_info info;
aux = sizeof(info);
ret = getsockopt(lfd, SOL_TCP, TCP_INFO, &info, &aux);
if (ret) {
pr_perror("Failt to obtain TCP_INFO");
goto err;
}
if (info.tcpi_state != TCP_CLOSE) {
pr_err("Socket state %d obtained but expected %d\n",
info.tcpi_state, TCP_CLOSE);
goto err;
}
sk->wqlen = info.tcpi_backoff;
}
sk->state = TCP_CLOSE;
sk_collect_one(sk->sd.ino, sk->sd.family, &sk->sd);
return sk;
err:
xfree(sk);
return NULL;
}
static int dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p) static int dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p)
{ {
struct inet_sk_desc *sk; struct inet_sk_desc *sk;
struct inet_sk_entry ie; struct inet_sk_entry ie;
sk = (struct inet_sk_desc *)lookup_socket(p->stat.st_ino); sk = (struct inet_sk_desc *)lookup_socket(p->stat.st_ino);
if (!sk) if (!sk) {
goto err; sk = gen_uncon_sk(lfd, p);
if (!sk)
goto err;
}
if (!can_dump_inet_sk(sk)) if (!can_dump_inet_sk(sk))
goto err; goto err;
...@@ -184,6 +259,18 @@ int inet_collect_one(struct nlmsghdr *h, int family, int type, int proto) ...@@ -184,6 +259,18 @@ int inet_collect_one(struct nlmsghdr *h, int family, int type, int proto)
return ret; return ret;
} }
static u32 zero_addr[4];
static bool is_bound(struct inet_sk_info *ii)
{
BUILD_BUG_ON(sizeof(zero_addr) <
max(sizeof(ii->ie.dst_addr), sizeof(ii->ie.src_addr)));
return memcmp(zero_addr, ii->ie.src_addr, sizeof(ii->ie.src_addr)) ||
memcmp(zero_addr, ii->ie.dst_addr, sizeof(ii->ie.dst_addr));
}
static int open_inet_sk(struct file_desc *d); static int open_inet_sk(struct file_desc *d);
static struct file_desc_ops inet_desc_ops = { static struct file_desc_ops inet_desc_ops = {
...@@ -265,8 +352,10 @@ static int open_inet_sk(struct file_desc *d) ...@@ -265,8 +352,10 @@ static int open_inet_sk(struct file_desc *d)
* bind() and listen(), and that's all. * bind() and listen(), and that's all.
*/ */
if (inet_bind(sk, ii)) if (is_bound(ii)) {
goto err; if (inet_bind(sk, ii))
goto err;
}
if (ii->ie.state == TCP_LISTEN) { if (ii->ie.state == TCP_LISTEN) {
if (ii->ie.proto != IPPROTO_TCP) { if (ii->ie.proto != IPPROTO_TCP) {
......
...@@ -75,7 +75,7 @@ int restore_socket_opts(int sk, struct sk_opts_entry *soe) ...@@ -75,7 +75,7 @@ int restore_socket_opts(int sk, struct sk_opts_entry *soe)
return ret; return ret;
} }
static int do_dump_opt(int sk, int name, void *val, int len) int do_dump_opt(int sk, int name, void *val, int len)
{ {
socklen_t aux = len; socklen_t aux = len;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment