[CRIU] [RFC] sockets: Use soket operations and socket collectors

Cyrill Gorcunov gorcunov at openvz.org
Sun Apr 22 18:31:38 EDT 2012


Hi guys,

here is early draft on how I imagine sockets.c could
be splitted. Please review. Comments are welcome.
The patch is big one so maybe better to review it
after application to some custome branch.

Also the socket tests are failed with it at moment,
but overall idea remains valid. And I guess socket
operations might be simplified even more.

n.b.: Patch not for inclusion.

	Cyrill
-------------- next part --------------
>From df421608c9f22c8e5cc407f6a0c1c4d4c8252470 Mon Sep 17 00:00:00 2001
From: Cyrill Gorcunov <gorcunov at openvz.org>
Date: Mon, 23 Apr 2012 02:26:22 +0400
Subject: [RFC] sockets: Use soket operations and socket collectors

The initial idea is to split sockets.c fike into per-family handling.
To achieve this the socket.c has been splitted into three files

 - socket.c
 - sk-inet.c
 - sk-unix.c

While socket.c remains as "main" socket engine file, the sk-unix
and sk-inet serves for AF_UNIX, AF_INET/AF_INET6 sockets handling.

The following structures were introduced

 - struct sk_rt_collector

     Used for run-time sockets collecting during dumping.
     One structure per socket type/family/protocol. This
     structure holds netlink message request needed to retrieve
     informaion about sockets.

 - struct sk_ops

    This is main structure which keeps pointers on operations over
    sockets. sk_rt_collector embedded into it.

    At checkpoint time the program traverse `rt_collector' and collects
    all sockets in system, then if socket is found under fd/ the `dump_one'
    function is called.

   At restore time the `img_collector' is started, and then (depending
   on type of a socket) `resolve_peers' and `connect_delayed' yielded.
   These methods are important for unix sockets.

Other changes are mostly code splitting from sockets.c to sk-inet.c
and sk-unix.c.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 Makefile             |    2 +
 cr-restore.c         |    8 +-
 cr-show.c            |    3 +
 crtools.c            |    3 +
 files.c              |    2 +-
 include/libnetlink.h |    2 +-
 include/sk-inet.h    |   12 +
 include/sk-unix.h    |   11 +
 include/sockets.h    |  139 +++++-
 libnetlink.c         |    4 +-
 sk-inet.c            |  449 ++++++++++++++++
 sk-unix.c            |  837 ++++++++++++++++++++++++++++++
 sockets.c            | 1385 +++-----------------------------------------------
 13 files changed, 1522 insertions(+), 1335 deletions(-)
 create mode 100644 include/sk-inet.h
 create mode 100644 include/sk-unix.h
 create mode 100644 sk-inet.c
 create mode 100644 sk-unix.c

diff --git a/Makefile b/Makefile
index 0ef29a4..9ded082 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,8 @@ OBJS		+= rbtree.o
 OBJS		+= log.o
 OBJS		+= libnetlink.o
 OBJS		+= sockets.o
+OBJS		+= sk-unix.o
+OBJS		+= sk-inet.o
 OBJS		+= sk-queue.o
 OBJS		+= files.o
 OBJS		+= pipes.o
diff --git a/cr-restore.c b/cr-restore.c
index 2bac361..5ef0ae0 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -287,10 +287,7 @@ static int prepare_shared(void)
 	if (collect_pipes())
 		return -1;
 
-	if (collect_inet_sockets())
-		return -1;
-
-	if (collect_unix_sockets())
+	if (collect_sockets())
 		return -1;
 
 	if (collect_mount_info())
@@ -310,7 +307,8 @@ static int prepare_shared(void)
 	}
 
 	mark_pipe_master();
-	ret = resolve_unix_peers();
+
+	ret = resolve_sockets_peers();
 
 	if (!ret) {
 		show_saved_shmems();
diff --git a/cr-show.c b/cr-show.c
index d612523..cd7058c 100644
--- a/cr-show.c
+++ b/cr-show.c
@@ -19,6 +19,9 @@
 #include "crtools.h"
 #include "util.h"
 #include "sockets.h"
+#include "sk-queue.h"
+#include "sk-unix.h"
+#include "sk-inet.h"
 #include "image.h"
 #include "uts_ns.h"
 #include "ipc_ns.h"
diff --git a/crtools.c b/crtools.c
index 0cddcaf..c192514 100644
--- a/crtools.c
+++ b/crtools.c
@@ -19,6 +19,9 @@
 #include "util.h"
 #include "log.h"
 #include "sockets.h"
+#include "sk-queue.h"
+#include "sk-unix.h"
+#include "sk-inet.h"
 #include "syscall.h"
 #include "uts_ns.h"
 #include "ipc_ns.h"
diff --git a/files.c b/files.c
index 34be21f..127c8c3 100644
--- a/files.c
+++ b/files.c
@@ -652,7 +652,7 @@ int prepare_fds(struct pstree_item *me)
 				goto done;
 		}
 
-	ret = run_unix_connections();
+	ret = connect_delayed_sockets();
 done:
 	return ret;
 }
diff --git a/include/libnetlink.h b/include/libnetlink.h
index de986cb..2e4dd2c 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -62,6 +62,6 @@ struct rtattr {
 #define CR_NLMSG_SEQ		24680	/* arbitrary chosen */
 
 extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
-extern int nlmsg_receive(char *buf, int len, int (*cb)(struct nlmsghdr *));
+extern int nlmsg_receive(char *buf, int len, void *aux, int (*cb)(struct nlmsghdr *h, void *aux));
 
 #endif /* LIBNETLINK_H__ */
diff --git a/include/sk-inet.h b/include/sk-inet.h
new file mode 100644
index 0000000..a237004
--- /dev/null
+++ b/include/sk-inet.h
@@ -0,0 +1,12 @@
+#ifndef SK_INET_H__
+#define SK_INET_H__
+
+#include "crtools.h"
+#include "sockets.h"
+
+extern struct sk_ops inet_sk_ops;
+extern struct sk_ops inet6_sk_ops;
+
+void show_inetsk(int fd, struct cr_options *o);
+
+#endif /* SK_INET_H__ */
diff --git a/include/sk-unix.h b/include/sk-unix.h
new file mode 100644
index 0000000..888c4f8
--- /dev/null
+++ b/include/sk-unix.h
@@ -0,0 +1,11 @@
+#ifndef SK_UNIX_H__
+#define SK_UNIX_H__
+
+#include "crtools.h"
+#include "sockets.h"
+
+extern struct sk_ops unix_sk_ops;
+
+extern void show_unixsk(int fd, struct cr_options *o);
+
+#endif /* SK_UNIX_H__ */
diff --git a/include/sockets.h b/include/sockets.h
index 9d78370..c1e83be 100644
--- a/include/sockets.h
+++ b/include/sockets.h
@@ -1,27 +1,136 @@
 #ifndef CR_SOCKETS_H__
 #define CR_SOCKETS_H__
 
+#include <stdio.h>
 #include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
 #include <unistd.h>
 #include <stdbool.h>
 
-struct cr_fdset;
-struct fd_parms;
-extern int dump_socket(struct fd_parms *p, int lfd,
-		const struct cr_fdset *cr_fdset);
+#include "unix_diag.h"
+#include "inet_diag.h"
+#include "crtools.h"
+#include "files.h"
+#include "libnetlink.h"
 
-struct fdinfo_list_entry;
-struct file_desc;
-struct fdinfo_entry;
+#define SK_HASH_SIZE		32
+#define SK_HASH_LINK(head, key, elem)					\
+	do {								\
+		(elem)->next = (head)[(key) % SK_HASH_SIZE];		\
+		(head)[(key) % SK_HASH_SIZE] = (elem);			\
+	} while (0)
+
+#define __gen_lookup_func(ret, name, head, _member, _type, _name)	\
+	ret *name(_type _name) {					\
+		ret *d;							\
+		for (d = head[_name % SK_HASH_SIZE]; d; d = d->next) {	\
+			if (d->_member == _name)			\
+				break;					\
+		}							\
+		return d;						\
+	}
+
+struct socket_desc {
+	struct socket_desc	*next;
+	unsigned int		family;
+	unsigned int		ino;
+	int			already_dumped;
+	bool			external;
+};
+
+struct proto_req {
+  union {
+	struct unix_diag_req	u;
+	struct inet_diag_req_v2	i;
+  } __u;
+};
+
+struct diag_req {
+	struct nlmsghdr		hdr;
+	struct proto_req	r;
+};
+
+struct sk_rt_collector {
+	char			*name;
+	int			type;
+	struct proto_req	r;
+	int			(*collect)(struct nlmsghdr *h, void *aux);
+};
+
+struct sk_ops {
+	struct sk_rt_collector	*rt_collector;
+	unsigned int		nr_rt_collectors;
+	u8			family;
+	int			(*dump_one)(const struct socket_desc *_sk, struct fd_parms *p,
+					    int lfd, const struct cr_fdset *cr_fdset);
+	int			(*dump_external)(struct socket_desc *sd);
+	int			(*img_collector)(void);
+	int			(*resolve_peers)(void);
+	int			(*connect_delayed)(void);
+};
+
+static inline char *unknown(u32 val)
+{
+	static char unk[32];
+	snprintf(unk, sizeof(unk), "unk:%d", val);
+	return unk;
+}
+
+static inline char *skfamily2s(u32 f)
+{
+	if (f == AF_INET)
+		return " inet";
+	else if (f == AF_INET6)
+		return "inet6";
+	else
+		return unknown(f);
+}
+
+static inline char *sktype2s(u32 t)
+{
+	if (t == SOCK_STREAM)
+		return "stream";
+	else if (t == SOCK_DGRAM)
+		return " dgram";
+	else
+		return unknown(t);
+}
+
+static inline char *skproto2s(u32 p)
+{
+	if (p == IPPROTO_UDP)
+		return "udp";
+	else if (p == IPPROTO_UDPLITE)
+		return "udpl";
+	else if (p == IPPROTO_TCP)
+		return "tcp";
+	else
+		return unknown(p);
+}
+
+static inline char *skstate2s(u32 state)
+{
+	if (state == TCP_ESTABLISHED)
+		return " estab";
+	else if (state == TCP_CLOSE)
+		return "closed";
+	else if (state == TCP_LISTEN)
+		return "listen";
+	else
+		return unknown(state);
+}
+
+extern void sk_collect_one(unsigned int ino, unsigned int family, struct socket_desc *d);
+extern struct socket_desc *lookup_socket(unsigned int ino);
+extern int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset);
 extern int collect_sockets(void);
 extern int dump_external_sockets(void);
-extern int collect_inet_sockets(void);
-extern int collect_unix_sockets(void);
-extern int resolve_unix_peers(void);
-extern int run_unix_connections(void);
-struct cr_options;
-extern void show_unixsk(int fd, struct cr_options *);
-extern void show_inetsk(int fd, struct cr_options *);
-extern void show_sk_queues(int fd, struct cr_options *);
+extern int resolve_sockets_peers(void);
+extern int connect_delayed_sockets(void);
 
 #endif /* CR_SOCKETS_H__ */
diff --git a/libnetlink.c b/libnetlink.c
index 9de230e..29f9e20 100644
--- a/libnetlink.c
+++ b/libnetlink.c
@@ -20,7 +20,7 @@ int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
 	return 0;
 }
 
-int nlmsg_receive(char *buf, int len, int (*cb)(struct nlmsghdr *))
+int nlmsg_receive(char *buf, int len, void *aux, int (*cb)(struct nlmsghdr *h, void *aux))
 {
 	struct nlmsghdr *hdr;
 
@@ -33,7 +33,7 @@ int nlmsg_receive(char *buf, int len, int (*cb)(struct nlmsghdr *))
 			pr_err("Error getting sockets list\n");
 			return -1;
 		}
-		if (cb(hdr))
+		if (cb(hdr, aux))
 			return -1;
 	}
 
diff --git a/sk-inet.c b/sk-inet.c
new file mode 100644
index 0000000..f4eb73e
--- /dev/null
+++ b/sk-inet.c
@@ -0,0 +1,449 @@
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/tcp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <sys/sendfile.h>
+
+#include "types.h"
+#include "libnetlink.h"
+#include "sockets.h"
+#include "sk-unix.h"
+#include "sk-queue.h"
+#include "unix_diag.h"
+#include "image.h"
+#include "crtools.h"
+#include "util.h"
+#include "inet_diag.h"
+#include "files.h"
+#include "util-net.h"
+
+#define INET_ADDR_LEN		40
+
+struct inet_sk_desc {
+	struct socket_desc	sd;
+	unsigned int		type;
+	unsigned int		proto;
+	unsigned int		src_port;
+	unsigned int		dst_port;
+	unsigned int		state;
+	unsigned int		rqlen;
+	unsigned int		wqlen;
+	unsigned int		src_addr[4];
+	unsigned int		dst_addr[4];
+};
+
+struct inet_sk_info {
+	struct file_desc	d;
+	struct inet_sk_entry	ie;
+};
+
+static void show_one_inet(const char *act, const struct inet_sk_desc *sk)
+{
+	char src_addr[INET_ADDR_LEN] = "<unknown>";
+
+	if (inet_ntop(sk->sd.family, (void *)sk->src_addr, src_addr,
+		      INET_ADDR_LEN) == NULL) {
+		pr_perror("Failed to translate address");
+	}
+
+	pr_debug("\t%s: ino 0x%8x family %4d type %4d port %8d "
+		"state %2d src_addr %s\n",
+		act, sk->sd.ino, sk->sd.family, sk->type, sk->src_port,
+		sk->state, src_addr);
+}
+
+static void show_one_inet_img(const char *act, const struct inet_sk_entry *e)
+{
+	char src_addr[INET_ADDR_LEN] = "<unknown>";
+
+	if (inet_ntop(e->family, (void *)e->src_addr, src_addr,
+		      INET_ADDR_LEN) == NULL) {
+		pr_perror("Failed to translate address");
+	}
+
+	pr_debug("\t%s: family %d type %d proto %d port %d "
+		"state %d src_addr %s\n",
+		act, e->family, e->type, e->proto, e->src_port,
+		e->state, src_addr);
+}
+
+void show_inetsk(int fd, struct cr_options *o)
+{
+	struct inet_sk_entry ie;
+	int ret = 0;
+
+	pr_img_head(CR_FD_INETSK);
+
+	while (1) {
+		char src_addr[INET_ADDR_LEN] = "<unknown>";
+		char dst_addr[INET_ADDR_LEN] = "<unknown>";
+
+		ret = read_img_eof(fd, &ie);
+		if (ret <= 0)
+			goto out;
+
+		if (inet_ntop(ie.family, (void *)ie.src_addr, src_addr,
+			      INET_ADDR_LEN) == NULL) {
+			pr_perror("Failed to translate src address");
+		}
+
+		if (ie.state == TCP_ESTABLISHED) {
+			if (inet_ntop(ie.family, (void *)ie.dst_addr, dst_addr,
+				      INET_ADDR_LEN) == NULL) {
+				pr_perror("Failed to translate dst address");
+			}
+		}
+
+		pr_msg("id 0x%x family %s type %s proto %s state %s %s:%d <-> %s:%d flags 0x%2x\n",
+			ie.id, skfamily2s(ie.family), sktype2s(ie.type), skproto2s(ie.proto),
+			skstate2s(ie.state), src_addr, ie.src_port, dst_addr, ie.dst_port, ie.flags);
+		pr_msg("\t"), show_fown_cont(&ie.fown), pr_msg("\n");
+	}
+
+out:
+	if (ret)
+		pr_info("\n");
+	pr_img_tail(CR_FD_INETSK);
+}
+
+static int can_dump_inet_sk(const struct inet_sk_desc *sk)
+{
+	if (sk->sd.family != AF_INET && sk->sd.family != AF_INET6) {
+		pr_err("Only IPv4/6 sockets for now\n");
+		return 0;
+	}
+
+	if (sk->type == SOCK_DGRAM)
+		return 1;
+
+	if (sk->type != SOCK_STREAM) {
+		pr_err("Only stream and dgram inet sockets for now\n");
+		return 0;
+	}
+
+	switch (sk->state) {
+	case TCP_LISTEN:
+		if (sk->rqlen != 0) {
+			/*
+			 * Currently the ICONS nla reports the conn
+			 * requests for listen sockets. Need to pick
+			 * those up and fix the connect job respectively
+			 */
+			pr_err("In-flight connection (l)\n");
+			return 0;
+		}
+		break;
+	default:
+		pr_err("Unknown state %d\n", sk->state);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int inet_dump_one(const struct socket_desc *_sk, struct fd_parms *p,
+			 int lfd, const struct cr_fdset *cr_fdset)
+{
+	struct inet_sk_desc *sk = (struct inet_sk_desc *)_sk;
+	struct inet_sk_entry ie;
+	struct fdinfo_entry fe;
+
+	if (!can_dump_inet_sk(sk))
+		goto err;
+
+	fe.fd		= p->fd;
+	fe.type		= FDINFO_INETSK;
+	fe.id		= sk->sd.ino;
+	fe.flags	= p->fd_flags;
+
+	if (write_img(fdset_fd(cr_fdset, CR_FD_FDINFO), &fe))
+		goto err;
+
+	if (sk->sd.already_dumped)
+		return 0;
+
+	memzero(&ie, sizeof(ie));
+
+	ie.id		= sk->sd.ino;
+	ie.family	= sk->sd.family;
+	ie.type		= sk->type;
+	ie.proto	= sk->proto;
+	ie.state	= sk->state;
+	ie.src_port	= sk->src_port;
+	ie.dst_port	= sk->dst_port;
+	ie.backlog	= sk->wqlen;
+	ie.flags	= p->flags;
+	ie.fown		= p->fown;
+	memcpy(ie.src_addr, sk->src_addr, sizeof(ie.src_addr));
+	memcpy(ie.dst_addr, sk->dst_addr, sizeof(ie.dst_addr));
+
+	if (write_img(fdset_fd(glob_fdset, CR_FD_INETSK), &ie))
+		goto err;
+
+	pr_info("Dumping inet socket at %d\n", p->fd);
+	show_one_inet("Dumping", sk);
+	show_one_inet_img("Dumped", &ie);
+	sk->sd.already_dumped = 1;
+	return 0;
+err:
+	return -1;
+}
+
+static int inet_collect(struct nlmsghdr *h, void *aux)
+{
+	struct inet_diag_msg *m = NLMSG_DATA(h);
+	struct rtattr *tb[INET_DIAG_MAX+1];
+
+	struct sk_rt_collector *c = aux;
+	struct inet_sk_desc *d;
+
+	parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(m + 1),
+		     h->nlmsg_len - NLMSG_LENGTH(sizeof(*m)));
+
+	d = xzalloc(sizeof(*d));
+	if (!d)
+		return -1;
+
+	d->type		= c->type;
+	d->proto	= c->r.__u.i.sdiag_protocol;
+	d->src_port	= ntohs(m->id.idiag_sport);
+	d->dst_port	= ntohs(m->id.idiag_dport);
+	d->state	= m->idiag_state;
+	d->rqlen	= m->idiag_rqueue;
+	d->wqlen	= m->idiag_wqueue;
+	memcpy(d->src_addr, m->id.idiag_src, sizeof(d->src_addr));
+	memcpy(d->dst_addr, m->id.idiag_dst, sizeof(d->dst_addr));
+
+	sk_collect_one(m->idiag_inode, m->idiag_family, &d->sd);
+
+	show_one_inet("Collected", d);
+
+	return 0;
+}
+
+static int inet_open_sk(struct file_desc *d)
+{
+	union {
+		struct sockaddr_in	v4;
+		struct sockaddr_in6	v6;
+	} addr;
+	struct inet_sk_info *ii;
+	int sk, addr_size;
+
+	ii = container_of(d, struct inet_sk_info, d);
+
+	show_one_inet_img("Restore", &ii->ie);
+
+	if (ii->ie.family != AF_INET && ii->ie.family != AF_INET6) {
+		pr_err("Unsupported socket family: %d\n", ii->ie.family);
+		return -1;
+	}
+
+	if ((ii->ie.type != SOCK_STREAM) && (ii->ie.type != SOCK_DGRAM)) {
+		pr_err("Unsupported socket type: %d\n", ii->ie.type);
+		return -1;
+	}
+
+	sk = socket(ii->ie.family, ii->ie.type, ii->ie.proto);
+	if (sk < 0) {
+		pr_perror("Can't create unix socket");
+		return -1;
+	}
+
+	if (restore_fown(sk, &ii->ie.fown))
+		goto err;
+
+	/*
+	 * Listen sockets are easiest ones -- simply
+	 * bind() and listen(), and that's all.
+	 */
+	memzero(&addr, sizeof(addr));
+	if (ii->ie.family == AF_INET) {
+		addr.v4.sin_family = ii->ie.family;
+		addr.v4.sin_port = htons(ii->ie.src_port);
+		memcpy(&addr.v4.sin_addr.s_addr, ii->ie.src_addr, sizeof(ii->ie.src_addr));
+		addr_size = sizeof(addr.v4);
+	} else if (ii->ie.family == AF_INET6) {
+		addr.v6.sin6_family = ii->ie.family;
+		addr.v6.sin6_port = htons(ii->ie.src_port);
+		memcpy(&addr.v6.sin6_addr.s6_addr, ii->ie.src_addr, sizeof(ii->ie.src_addr));
+		addr_size = sizeof(addr.v6);
+	} else
+		BUG_ON(1);
+
+	if (bind(sk, (struct sockaddr *)&addr, addr_size) == -1) {
+		pr_perror("Can't bind to a socket");
+		goto err;
+	}
+
+	if (ii->ie.state == TCP_LISTEN) {
+		if (ii->ie.proto != IPPROTO_TCP) {
+			pr_err("Wrong socket in listen state %d\n", ii->ie.proto);
+			goto err;
+		}
+
+		if (listen(sk, ii->ie.backlog) == -1) {
+			pr_perror("Can't listen on a socket");
+			goto err;
+		}
+	}
+
+	if (ii->ie.state == TCP_ESTABLISHED) {
+		if (ii->ie.proto == IPPROTO_TCP) {
+			pr_err("Connected TCP socket in image\n");
+			goto err;
+		}
+
+		memzero(&addr, sizeof(addr));
+		if (ii->ie.family == AF_INET) {
+			addr.v4.sin_family = ii->ie.family;
+			addr.v4.sin_port = htons(ii->ie.dst_port);
+			memcpy(&addr.v4.sin_addr.s_addr, ii->ie.dst_addr, sizeof(ii->ie.dst_addr));
+			addr_size = sizeof(addr.v4);
+		} else if (ii->ie.family == AF_INET6) {
+			addr.v6.sin6_family = ii->ie.family;
+			addr.v6.sin6_port = htons(ii->ie.dst_port);
+			memcpy(&addr.v6.sin6_addr.s6_addr, ii->ie.dst_addr, sizeof(ii->ie.dst_addr));
+			addr_size = sizeof(addr.v6);
+		} else
+			BUG_ON(1);
+
+		if (connect(sk, (struct sockaddr *)&addr, addr_size) == -1) {
+			pr_perror("Can't connect UDP socket back");
+			goto err;
+		}
+	}
+
+	if (set_fd_flags(sk, ii->ie.flags))
+		return -1;
+
+	return sk;
+
+err:
+	close(sk);
+	return -1;
+}
+
+static struct file_desc_ops inet_desc_ops = {
+	.open = inet_open_sk,
+};
+
+int inet_img_collector(void)
+{
+	struct inet_sk_info *ii = NULL;
+	int fd, ret = -1;
+
+	fd = open_image_ro(CR_FD_INETSK);
+	if (fd < 0)
+		return -1;
+
+	while (1) {
+		ii = xmalloc(sizeof(*ii));
+		if (!ii) {
+			ret = -1;
+			break;
+		}
+
+		ret = read_img_eof(fd, &ii->ie);
+		if (ret <= 0)
+			break;
+
+		file_desc_add(&ii->d, FDINFO_INETSK, ii->ie.id,
+				&inet_desc_ops);
+	}
+
+	xfree(ii);
+	close(fd);
+	return ret;
+}
+
+static struct sk_rt_collector inet_collector[] = {
+  {
+	.name		= "IPv4/TCP",
+	.type		= SOCK_STREAM,
+	.r		= {
+		.__u.i.sdiag_family	= AF_INET,
+		.__u.i.sdiag_protocol	= IPPROTO_TCP,
+		.__u.i.idiag_states	= 1 << TCP_LISTEN,
+	},
+	.collect	= inet_collect,
+  },
+  {
+	.name		= "IPv4/UDP",
+	.type		= SOCK_DGRAM,
+	.r		= {
+		.__u.i.sdiag_family	= AF_INET,
+		.__u.i.sdiag_protocol	= IPPROTO_UDP,
+		.__u.i.idiag_states	= -1,		/* All */
+	},
+	.collect	= inet_collect,
+  },
+  {
+	.name		= "IPv4/UDPLite",
+	.type		= SOCK_DGRAM,
+	.r		= {
+		.__u.i.sdiag_family	= AF_INET,
+		.__u.i.sdiag_protocol	= IPPROTO_UDPLITE,
+		.__u.i.idiag_states	= -1,		/* All */
+	},
+	.collect	= inet_collect,
+  },
+};
+
+static struct sk_rt_collector inet6_collector[] = {
+  {
+	.name		= "IPv6/TCP",
+	.type		= SOCK_STREAM,
+	.r		= {
+		.__u.i.sdiag_family	= AF_INET6,
+		.__u.i.sdiag_protocol	= IPPROTO_TCP,
+		.__u.i.idiag_states	= 1 << TCP_LISTEN,
+	},
+	.collect	= inet_collect,
+  },
+  {
+	.name		= "IPv6/UDP",
+	.type		= SOCK_DGRAM,
+	.r		= {
+		.__u.i.sdiag_family	= AF_INET6,
+		.__u.i.sdiag_protocol	= IPPROTO_UDP,
+		.__u.i.idiag_states	= -1,		/* All */
+	},
+	.collect	= inet_collect,
+  },
+  {
+	.name		= "IPv6/UDPLite",
+	.type		= SOCK_DGRAM,
+	.r		= {
+		.__u.i.sdiag_family	= AF_INET6,
+		.__u.i.sdiag_protocol	= IPPROTO_UDPLITE,
+		.__u.i.idiag_states	= -1,		/* All */
+	},
+	.collect	= inet_collect,
+  },
+};
+
+struct sk_ops inet_sk_ops = {
+	.family			= AF_INET,
+	.rt_collector		= inet_collector,
+	.nr_rt_collectors	= ARRAY_SIZE(inet_collector),
+	.dump_one		= inet_dump_one,
+	.img_collector		= inet_img_collector,
+};
+
+struct sk_ops inet6_sk_ops = {
+	.family			= AF_INET6,
+	.rt_collector		= inet6_collector,
+	.nr_rt_collectors	= ARRAY_SIZE(inet6_collector),
+	.dump_one		= inet_dump_one,
+	.img_collector		= inet_img_collector,
+};
diff --git a/sk-unix.c b/sk-unix.c
new file mode 100644
index 0000000..26b8158
--- /dev/null
+++ b/sk-unix.c
@@ -0,0 +1,837 @@
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/tcp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <sys/sendfile.h>
+
+#include "types.h"
+#include "libnetlink.h"
+#include "sockets.h"
+#include "sk-unix.h"
+#include "sk-queue.h"
+#include "unix_diag.h"
+#include "image.h"
+#include "crtools.h"
+#include "util.h"
+#include "inet_diag.h"
+#include "files.h"
+#include "util-net.h"
+
+struct unix_sk_desc {
+	struct socket_desc		sd;
+	unsigned int			type;
+	unsigned int			state;
+	unsigned int			peer_ino;
+	unsigned int			rqlen;
+	unsigned int			wqlen;
+	unsigned int			namelen;
+	char				*name;
+	unsigned int			nr_icons;
+	unsigned int			*icons;
+};
+
+struct unix_sk_listen_icon {
+	unsigned int			peer_ino;
+	struct unix_sk_desc		*sk_desc;
+	struct unix_sk_listen_icon	*next;
+};
+
+#define USK_PAIR_MASTER			0x1
+#define USK_PAIR_SLAVE			0x2
+
+struct unix_sk_info {
+	struct file_desc		d;
+	struct list_head		list;
+	struct unix_sk_entry		ue;
+	struct unix_sk_info		*peer;
+	char				*name;
+	unsigned int			flags;
+};
+
+struct unix_conn_job {
+	struct unix_sk_info		*sk;
+	struct unix_conn_job		*next;
+};
+
+static struct unix_conn_job *conn_jobs;
+static LIST_HEAD(unix_sockets);
+
+static struct unix_sk_listen_icon *unix_listen_icons[SK_HASH_SIZE];
+__gen_lookup_func(static struct unix_sk_listen_icon,
+		  lookup_unix_listen_icons,
+		  unix_listen_icons,
+		  peer_ino, unsigned int, ino);
+
+static void show_one_unix(char *act, const struct unix_sk_desc *sk)
+{
+	pr_debug("\t%s: ino 0x%8x family %4d type %4d state %2d name %s\n",
+		act, sk->sd.ino, sk->sd.family, sk->type, sk->state, sk->name);
+
+	if (sk->nr_icons) {
+		int i;
+
+		for (i = 0; i < sk->nr_icons; i++)
+			pr_debug("\t\ticon: %4d\n", sk->icons[i]);
+	}
+}
+
+static void show_one_unix_img(const char *act, const struct unix_sk_entry *e)
+{
+	pr_info("\t%s: id %u type %d state %d name %d bytes\n",
+		act, e->id, e->type, e->state, e->namelen);
+}
+
+void show_unixsk(int fd, struct cr_options *o)
+{
+	struct unix_sk_entry ue;
+	char *buf = NULL, *p;
+	int ret = 0;
+
+	pr_img_head(CR_FD_UNIXSK);
+
+	while (1) {
+		ret = read_img_eof(fd, &ue);
+		if (ret <= 0)
+			goto out;
+
+		pr_msg("id 0x%8x type %s state %s namelen %4d backlog %4d peer 0x%8x flags 0x%2x uflags 0x%2x",
+			ue.id, sktype2s(ue.type), skstate2s(ue.state),
+			ue.namelen, ue.backlog, ue.peer, ue.flags, ue.uflags);
+
+		if (ue.namelen) {
+			p = xrealloc(buf, ue.namelen);
+			if (!p)
+				goto out;
+			buf = p;
+			ret = read_img_buf(fd, buf, ue.namelen);
+			if (ret < 0) {
+				pr_info("\n");
+				goto out;
+			}
+			if (!buf[0])
+				buf[0] = '@';
+			pr_msg(" --> %s\n", buf);
+		} else
+			pr_msg("\n");
+		pr_msg("\t"), show_fown_cont(&ue.fown), pr_msg("\n");
+	}
+out:
+	xfree(buf);
+	pr_img_tail(CR_FD_UNIXSK);
+}
+
+static int unix_can_dump_sk(const struct unix_sk_desc *sk)
+{
+	if (sk->type != SOCK_STREAM &&
+	    sk->type != SOCK_DGRAM) {
+		pr_err("Only stream/dgram sockets for now\n");
+		return 0;
+	}
+
+	switch (sk->state) {
+	case TCP_LISTEN:
+	case TCP_ESTABLISHED:
+		break;
+	case TCP_CLOSE:
+		if (sk->type != SOCK_DGRAM)
+			return 0;
+		break;
+	default:
+		pr_err("Unknown state %d\n", sk->state);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int unix_dump_one(const struct socket_desc *_sk,
+			 struct fd_parms *p, int lfd,
+			 const struct cr_fdset *cr_fdset)
+{
+	struct unix_sk_desc *sk = (struct unix_sk_desc *)_sk;
+	struct fdinfo_entry fe;
+	struct unix_sk_entry ue;
+
+	if (!unix_can_dump_sk(sk))
+		goto err;
+
+	fe.fd		= p->fd;
+	fe.type		= FDINFO_UNIXSK;
+	fe.id		= sk->sd.ino;
+	fe.flags	= p->fd_flags;
+
+	if (write_img(fdset_fd(cr_fdset, CR_FD_FDINFO), &fe))
+		goto err;
+
+	if (sk->sd.already_dumped)
+		return 0;
+
+	ue.id		= sk->sd.ino;
+	ue.type		= sk->type;
+	ue.state	= sk->state;
+	ue.namelen	= sk->namelen;
+	ue.flags	= p->flags;
+	ue.backlog	= sk->wqlen;
+	ue.peer		= sk->peer_ino;
+	ue.fown		= p->fown;
+	ue.uflags	= 0;
+
+	if (ue.peer) {
+		struct unix_sk_desc *peer;
+
+		peer = (struct unix_sk_desc *)lookup_socket(ue.peer);
+		if (!peer) {
+			pr_err("Unix socket 0x%x without peer 0x%x\n",
+					ue.id, ue.peer);
+			goto err;
+		}
+
+		/*
+		 * Peer should have us as peer or have a name by which
+		 * we can access one.
+		 */
+		if (peer->peer_ino != ue.id) {
+			if (!peer->name) {
+				pr_err("Unix socket 0x%x with unreachable peer 0x%x (0x%x/%s)\n",
+				       ue.id, ue.peer, peer->peer_ino, peer->name);
+				goto err;
+			}
+
+			/*
+			 * It can be external socket, so we defer dumping
+			 * until all sockets the program owns are processed.
+			 */
+			peer->sd.external = true;
+		}
+	} else if (ue.state == TCP_ESTABLISHED) {
+		const struct unix_sk_listen_icon *e;
+
+		/*
+		 * If this is in-flight connection we need to figure
+		 * out where to connect it on restore. Thus, tune up peer
+		 * id by searching an existing listening socket.
+		 *
+		 * Note the socket name will be found at restore stage,
+		 * not now, just to reduce size of dump files.
+		 */
+
+		e = lookup_unix_listen_icons(ue.id);
+		if (!e) {
+			pr_err("Dangling in-flight connection %d\n", ue.id);
+			goto err;
+		}
+
+		/* e->sk_desc is _never_ NULL */
+		if (e->sk_desc->state != TCP_LISTEN) {
+			pr_err("In-flight connection on "
+				"non-listening socket %d\n", ue.id);
+			goto err;
+		}
+
+		ue.peer = e->sk_desc->sd.ino;
+
+		pr_debug("\t\tFixed inflight socket 0x%x peer 0x%x)\n",
+				ue.id, ue.peer);
+	}
+
+	if (write_img(fdset_fd(glob_fdset, CR_FD_UNIXSK), &ue))
+		goto err;
+	if (write_img_buf(fdset_fd(glob_fdset, CR_FD_UNIXSK), sk->name, ue.namelen))
+		goto err;
+
+	if (sk->rqlen != 0 && !(sk->type == SOCK_STREAM &&
+				sk->state == TCP_LISTEN))
+		if (dump_sk_queue(lfd, ue.id))
+			goto err;
+
+	pr_info("Dumping unix socket at %d\n", p->fd);
+	show_one_unix("Dumping", sk);
+	show_one_unix_img("Dumped", &ue);
+
+	sk->sd.already_dumped = 1;
+	return 0;
+
+err:
+	return -1;
+}
+
+static int schedule_conn_job(struct unix_sk_info *ui)
+{
+	struct unix_conn_job *cj;
+
+	cj = xmalloc(sizeof(*cj));
+	if (!cj)
+		return -1;
+
+	cj->sk = ui;
+	cj->next = conn_jobs;
+	conn_jobs = cj;
+
+	return 0;
+}
+
+static int unix_connect_delayed(void)
+{
+	struct unix_conn_job *cj;
+
+	pr_info("Running delayed unix connections\n");
+
+	cj = conn_jobs;
+	while (cj) {
+		int attempts = 8;
+		struct unix_sk_info *ui = cj->sk;
+		struct unix_sk_info *peer = ui->peer;
+		struct fdinfo_list_entry *fle;
+		struct sockaddr_un addr;
+
+		pr_info("\tConnect 0x%x to 0x%x\n", ui->ue.id, peer->ue.id);
+
+		fle = file_master(&ui->d);
+
+		memset(&addr, 0, sizeof(addr));
+		addr.sun_family = AF_UNIX;
+		memcpy(&addr.sun_path, peer->name, peer->ue.namelen);
+try_again:
+		if (connect(fle->fe.fd, (struct sockaddr *)&addr,
+					sizeof(addr.sun_family) +
+					peer->ue.namelen) < 0) {
+			if (attempts) {
+				usleep(1000);
+				attempts--;
+				goto try_again; /* FIXME use futex waiters */
+			}
+
+			pr_perror("Can't connect 0x%x socket", ui->ue.id);
+			return -1;
+		}
+
+		if (restore_sk_queue(fle->fe.fd, peer->ue.id))
+			return -1;
+
+		if (set_fd_flags(fle->fe.fd, ui->ue.flags))
+			return -1;
+
+		cj = cj->next;
+	}
+
+	return 0;
+}
+
+static int bind_unix_sk(int sk, struct unix_sk_info *ui)
+{
+	struct sockaddr_un addr;
+
+	if ((ui->ue.type == SOCK_STREAM) && (ui->ue.state != TCP_LISTEN))
+		/*
+		 * FIXME this can be done, but for doing this properly we
+		 * need to bind socket to its name, then rename one to
+		 * some temporary unique one and after all the sockets are
+		 * restored we should walk those temp names and rename
+		 * some of them back to real ones.
+		 */
+		goto done;
+
+	memzero(&addr, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(&addr.sun_path, ui->name, ui->ue.namelen);
+
+	if (bind(sk, (struct sockaddr *)&addr,
+		 sizeof(addr.sun_family) + ui->ue.namelen)) {
+		pr_perror("Can't bind socket");
+		return -1;
+	}
+done:
+	return 0;
+}
+
+static int open_unixsk_pair_master(struct unix_sk_info *ui)
+{
+	int sk[2], tsk;
+	struct unix_sk_info *peer = ui->peer;
+	struct fdinfo_list_entry *fle;
+
+	pr_info("Opening pair master (id 0x%x peer 0x%x)\n",
+			ui->ue.id, ui->ue.peer);
+
+	if (socketpair(PF_UNIX, ui->ue.type, 0, sk) < 0) {
+		pr_perror("Can't make socketpair");
+		return -1;
+	}
+
+	if (restore_sk_queue(sk[0], peer->ue.id))
+		return -1;
+	if (restore_sk_queue(sk[1], ui->ue.id))
+		return -1;
+
+	if (set_fd_flags(sk[0], ui->ue.flags))
+		return -1;
+	if (set_fd_flags(sk[1], peer->ue.flags))
+		return -1;
+
+	if (restore_fown(sk[0], &ui->ue.fown))
+		return -1;
+	if (restore_fown(sk[1], &peer->ue.fown))
+		return -1;
+
+	if (bind_unix_sk(sk[0], ui))
+		return -1;
+
+	tsk = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (tsk < 0) {
+		pr_perror("Can't make transport socket");
+		return -1;
+	}
+
+	fle = file_master(&peer->d);
+	if (send_fd_to_peer(sk[1], fle, tsk)) {
+		pr_err("Can't send pair slave\n");
+		return -1;
+	}
+
+	close(tsk);
+	close(sk[1]);
+
+	return sk[0];
+}
+
+static int open_unixsk_pair_slave(struct unix_sk_info *ui)
+{
+	struct fdinfo_list_entry *fle;
+	int sk;
+
+	fle = file_master(&ui->d);
+
+	pr_info("Opening pair slave (id 0x%x peer 0x%x) on %d\n",
+			ui->ue.id, ui->ue.peer, fle->fe.fd);
+
+	sk = recv_fd(fle->fe.fd);
+	if (sk < 0) {
+		pr_err("Can't recv pair slave");
+		return -1;
+	}
+	close(fle->fe.fd);
+
+	if (bind_unix_sk(sk, ui))
+		return -1;
+
+	return sk;
+}
+
+static int open_unixsk_standalone(struct unix_sk_info *ui)
+{
+	int sk;
+
+	pr_info("Opening standalone socket (id 0x%x peer 0x%x)\n",
+			ui->ue.id, ui->ue.peer);
+
+	sk = socket(PF_UNIX, ui->ue.type, 0);
+	if (sk < 0) {
+		pr_perror("Can't make unix socket");
+		return -1;
+	}
+
+	if (restore_fown(sk, &ui->ue.fown))
+		return -1;
+
+	if (bind_unix_sk(sk, ui))
+		return -1;
+
+	if (ui->ue.state == TCP_LISTEN) {
+		pr_info("\tPutting 0x%x into listen state\n", ui->ue.id);
+		if (listen(sk, ui->ue.backlog) < 0) {
+			pr_perror("Can't make usk listen");
+			return -1;
+		}
+	} else if (ui->peer) {
+		pr_info("\tWill connect 0x%x to 0x%x later\n", ui->ue.id, ui->ue.peer);
+		if (schedule_conn_job(ui))
+			return -1;
+	}
+
+	return sk;
+}
+
+static int unix_open_sk(struct file_desc *d)
+{
+	struct unix_sk_info *ui;
+
+	ui = container_of(d, struct unix_sk_info, d);
+	if (ui->flags & USK_PAIR_MASTER)
+		return open_unixsk_pair_master(ui);
+	else if (ui->flags & USK_PAIR_SLAVE)
+		return open_unixsk_pair_slave(ui);
+	else
+		return open_unixsk_standalone(ui);
+}
+
+static int unix_want_transport(struct fdinfo_entry *fe, struct file_desc *d)
+{
+	struct unix_sk_info *ui;
+
+	ui = container_of(d, struct unix_sk_info, d);
+	return ui->flags & USK_PAIR_SLAVE;
+}
+
+static struct file_desc_ops unix_desc_ops = {
+	.open		= unix_open_sk,
+	.want_transport	= unix_want_transport,
+};
+
+static int unix_img_collector(void)
+{
+	int fd, ret;
+
+	pr_info("Reading unix sockets in\n");
+
+	fd = open_image_ro(CR_FD_UNIXSK);
+	if (fd < 0) {
+		if (errno == ENOENT)
+			return 0;
+		else
+			return -1;
+	}
+
+	while (1) {
+		struct unix_sk_info *ui;
+
+		ui = xmalloc(sizeof(*ui));
+		ret = -1;
+		if (ui == NULL)
+			break;
+
+		ret = read_img_eof(fd, &ui->ue);
+		if (ret <= 0) {
+			xfree(ui);
+			break;
+		}
+
+		if (ui->ue.namelen) {
+			ret = -1;
+
+			if (!ui->ue.namelen || ui->ue.namelen >= UNIX_PATH_MAX) {
+				pr_err("Bad unix name len %d\n", ui->ue.namelen);
+				break;
+			}
+
+			ui->name = xmalloc(ui->ue.namelen);
+			if (ui->name == NULL)
+				break;
+
+			ret = read_img_buf(fd, ui->name, ui->ue.namelen);
+			if (ret < 0)
+				break;
+
+			/*
+			 * Make FS clean from sockets we're about to
+			 * restore. See for how we bind them for details
+			 */
+			if (ui->name[0] != '\0' &&
+			    !(ui->ue.uflags & USK_EXTERN))
+				unlink(ui->name);
+		} else
+			ui->name = NULL;
+
+		ui->peer = NULL;
+		ui->flags = 0;
+		pr_info(" `- Got %u peer %u\n", ui->ue.id, ui->ue.peer);
+		file_desc_add(&ui->d, FDINFO_UNIXSK, ui->ue.id,
+				&unix_desc_ops);
+		list_add_tail(&ui->list, &unix_sockets);
+	}
+
+	close(fd);
+
+	return read_sk_queues();
+}
+
+static struct unix_sk_info *unix_find_sk(int id)
+{
+	struct file_desc *d;
+
+	d = find_file_desc_raw(FDINFO_UNIXSK, id);
+	if (d)
+		return container_of(d, struct unix_sk_info, d);
+	return NULL;
+}
+
+static int unix_resolve_peers(void)
+{
+	struct unix_sk_info *ui, *peer;
+	struct fdinfo_list_entry *fle, *fle_peer;
+
+	list_for_each_entry(ui, &unix_sockets, list) {
+		if (ui->peer)
+			continue;
+		if (!ui->ue.peer)
+			continue;
+
+		peer = unix_find_sk(ui->ue.peer);
+
+		/*
+		 * Connect to external sockets requires
+		 * special option to be passed.
+		 */
+		if (peer &&
+		    (peer->ue.uflags & USK_EXTERN) &&
+		    !(opts.ext_unix_sk))
+			peer = NULL;
+
+		if (!peer) {
+			pr_err("FATAL: Peer 0x%x unresolved for 0x%x\n",
+					ui->ue.peer, ui->ue.id);
+			return -1;
+		}
+
+		ui->peer = peer;
+		if (ui == peer)
+			/* socket connected to self %) */
+			continue;
+		if (peer->ue.peer != ui->ue.id)
+			continue;
+
+		/* socketpair or interconnected sockets */
+		peer->peer = ui;
+
+		/*
+		 * Select who will restore the pair. Check is identical to
+		 * the one in pipes.c and makes sure tasks wait for each other
+		 * in pids sorting order (ascending).
+		 */
+
+		fle = file_master(&ui->d);
+		fle_peer = file_master(&peer->d);
+
+		if ((fle->pid < fle_peer->pid) ||
+				(fle->pid == fle_peer->pid &&
+				 fle->fe.fd < fle_peer->fe.fd)) {
+			ui->flags |= USK_PAIR_MASTER;
+			peer->flags |= USK_PAIR_SLAVE;
+		} else {
+			peer->flags |= USK_PAIR_MASTER;
+			ui->flags |= USK_PAIR_SLAVE;
+		}
+	}
+
+	pr_info("Unix sockets:\n");
+	list_for_each_entry(ui, &unix_sockets, list) {
+		struct fdinfo_list_entry *fle;
+
+		pr_info("\t0x%x -> 0x%x (0x%x) flags 0x%x\n", ui->ue.id, ui->ue.peer,
+				ui->peer ? ui->peer->ue.id : 0, ui->flags);
+		list_for_each_entry(fle, &ui->d.fd_info_head, desc_list)
+			pr_info("\t\tfd %d in pid %d\n",
+					fle->fe.fd, fle->pid);
+
+	}
+
+	return 0;
+}
+
+static int unix_dump_external(struct socket_desc *sd)
+{
+	struct unix_sk_entry e = { };
+	struct unix_sk_desc *sk;
+
+
+	if (sd->already_dumped		||
+	    sd->external == false	||
+	    sd->family != AF_UNIX)
+		return 0;
+
+	sk = container_of(sd, struct unix_sk_desc, sd);
+	if (sk->type != SOCK_DGRAM)
+		return 0;
+
+	e.id		= sd->ino;
+	e.type		= SOCK_DGRAM;
+	e.state		= TCP_LISTEN;
+	e.namelen	= sk->namelen;
+	e.uflags	= USK_EXTERN;
+	e.peer		= 0;
+
+	show_one_unix("Dumping extern", sk);
+
+	if (write_img(fdset_fd(glob_fdset, CR_FD_UNIXSK), &e))
+		goto err;
+	if (write_img_buf(fdset_fd(glob_fdset, CR_FD_UNIXSK),
+			  sk->name, e.namelen))
+		goto err;
+
+	show_one_unix_img("Dumped extern", &e);
+
+	sd->already_dumped = 1;
+	return 0;
+err:
+	return -1;
+}
+
+static int unix_collect_one(const struct unix_diag_msg *m, struct rtattr **tb)
+{
+	struct unix_sk_desc *d, **h;
+
+	d = xzalloc(sizeof(*d));
+	if (!d)
+		return -1;
+
+	d->type	= m->udiag_type;
+	d->state= m->udiag_state;
+
+	if (tb[UNIX_DIAG_PEER])
+		d->peer_ino = *(int *)RTA_DATA(tb[UNIX_DIAG_PEER]);
+
+	if (tb[UNIX_DIAG_NAME]) {
+		int len		= RTA_PAYLOAD(tb[UNIX_DIAG_NAME]);
+		char *name	= xmalloc(len + 1);
+
+		if (!name)
+			goto err;
+
+		memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len);
+		name[len] = '\0';
+
+		if (name[0] != '\0') {
+			struct unix_diag_vfs *uv;
+			struct stat st;
+
+			if (name[0] != '/') {
+				pr_warn("Relative bind path '%s' "
+					"unsupported\n", name);
+				xfree(name);
+				xfree(d);
+				return 0;
+			}
+
+			if (!tb[UNIX_DIAG_VFS]) {
+				pr_err("Bound socket w/o inode %d\n",
+						m->udiag_ino);
+				goto err;
+			}
+
+			uv = RTA_DATA(tb[UNIX_DIAG_VFS]);
+			if (stat(name, &st)) {
+				pr_perror("Can't stat socket %d(%s)",
+						m->udiag_ino, name);
+				goto err;
+			}
+
+			if ((st.st_ino != uv->udiag_vfs_ino) ||
+			    (st.st_dev != kdev_to_odev(uv->udiag_vfs_dev))) {
+				pr_info("unix: Dropping path for "
+					"unlinked bound "
+					"sk 0x%x.0x%x real 0x%x.0x%x\n",
+					(int)st.st_dev,
+					(int)st.st_ino,
+					(int)uv->udiag_vfs_dev,
+					(int)uv->udiag_vfs_ino);
+				/*
+				 * When a socket is bound to unlinked file, we
+				 * just drop his name, since noone will access
+				 * it via one.
+				 */
+				xfree(name);
+				len = 0;
+				name = NULL;
+			}
+		}
+
+		d->namelen = len;
+		d->name = name;
+	}
+
+	if (tb[UNIX_DIAG_ICONS]) {
+		int len = RTA_PAYLOAD(tb[UNIX_DIAG_ICONS]);
+		int i;
+
+		d->icons = xmalloc(len);
+		if (!d->icons)
+			goto err;
+
+		memcpy(d->icons, RTA_DATA(tb[UNIX_DIAG_ICONS]), len);
+		d->nr_icons = len / sizeof(u32);
+
+		/*
+		 * Remember these sockets, we will need them
+		 * to fix up in-flight sockets peers.
+		 */
+		for (i = 0; i < d->nr_icons; i++) {
+			struct unix_sk_listen_icon *e;
+			int n;
+
+			e = xzalloc(sizeof(*e));
+			if (!e)
+				goto err;
+
+			SK_HASH_LINK(unix_listen_icons, d->icons[i], e);
+
+			pr_debug("\t\tCollected icon %d\n", d->icons[i]);
+
+			e->peer_ino	= d->icons[i];
+			e->sk_desc	= d;
+		}
+
+
+	}
+
+	if (tb[UNIX_DIAG_RQLEN]) {
+		struct unix_diag_rqlen *rq;
+
+		rq = (struct unix_diag_rqlen *)RTA_DATA(tb[UNIX_DIAG_RQLEN]);
+		d->rqlen = rq->udiag_rqueue;
+		d->wqlen = rq->udiag_wqueue;
+	}
+
+	sk_collect_one(m->udiag_ino, AF_UNIX, &d->sd);
+	show_one_unix("Collected", d);
+
+	return 0;
+
+err:
+	xfree(d->icons);
+	xfree(d->name);
+	xfree(d);
+	return -1;
+}
+
+static int unix_collect(struct nlmsghdr *h, void *aux)
+{
+	struct unix_diag_msg *m = NLMSG_DATA(h);
+	struct rtattr *tb[UNIX_DIAG_MAX+1];
+
+	parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr *)(m + 1),
+		     h->nlmsg_len - NLMSG_LENGTH(sizeof(*m)));
+
+	return unix_collect_one(m, tb);
+}
+
+static struct sk_rt_collector unix_collector = {
+	.name		= "af-unix",
+	.type		= -1,
+	.r		= {
+		.__u.u.sdiag_family	= AF_UNIX,
+		.__u.u.udiag_states	= -1,		/* All */
+		.__u.u.udiag_show	= UDIAG_SHOW_NAME | UDIAG_SHOW_VFS |
+					  UDIAG_SHOW_PEER | UDIAG_SHOW_ICONS |
+					  UDIAG_SHOW_RQLEN,
+	},
+	.collect	= unix_collect,
+};
+
+struct sk_ops unix_sk_ops = {
+	.family			= AF_UNIX,
+	.rt_collector		= &unix_collector,
+	.nr_rt_collectors	= 1,
+	.dump_one		= unix_dump_one,
+	.dump_external		= unix_dump_external,
+	.img_collector		= unix_img_collector,
+	.resolve_peers		= unix_resolve_peers,
+	.connect_delayed	= unix_connect_delayed,
+};
diff --git a/sockets.c b/sockets.c
index eb951b0..0a7da3c 100644
--- a/sockets.c
+++ b/sockets.c
@@ -25,6 +25,9 @@
 #include "files.h"
 #include "util-net.h"
 
+#include "sk-unix.h"
+#include "sk-inet.h"
+
 static char buf[4096];
 
 #ifndef NETLINK_SOCK_DIAG
@@ -35,360 +38,41 @@ static char buf[4096];
 #define SOCK_DIAG_BY_FAMILY 20
 #endif
 
-#ifndef SOCKFS_MAGIC
-#define SOCKFS_MAGIC	0x534F434B
-#endif
-
-struct socket_desc {
-	unsigned int		family;
-	unsigned int		ino;
-	struct socket_desc	*next;
-	int			already_dumped;
-	bool			external;
-};
-
-struct unix_sk_desc {
-	struct socket_desc	sd;
-	unsigned int		type;
-	unsigned int		state;
-	unsigned int		peer_ino;
-	unsigned int		rqlen;
-	unsigned int		wqlen;
-	unsigned int		namelen;
-	char			*name;
-	unsigned int		nr_icons;
-	unsigned int		*icons;
-};
-
-struct unix_sk_listen_icon {
-	unsigned int			peer_ino;
-	struct unix_sk_desc		*sk_desc;
-	struct unix_sk_listen_icon	*next;
+static struct sk_ops *sk_ops[] = {
+	&unix_sk_ops,
+	&inet_sk_ops,
+	&inet6_sk_ops,
 };
 
-#define INET_ADDR_LEN		40
-
-struct inet_sk_desc {
-	struct socket_desc	sd;
-	unsigned int		type;
-	unsigned int		proto;
-	unsigned int		src_port;
-	unsigned int		dst_port;
-	unsigned int		state;
-	unsigned int		rqlen;
-	unsigned int		wqlen;
-	unsigned int		src_addr[4];
-	unsigned int		dst_addr[4];
-};
-
-#define SK_HASH_SIZE		32
-#define SK_HASH_LINK(head, key, elem)					\
-	do {								\
-		(elem)->next = (head)[(key) % SK_HASH_SIZE];		\
-		(head)[(key) % SK_HASH_SIZE] = (elem);			\
-	} while (0)
-
-#define __gen_static_lookup_func(ret, name, head, _member, _type, _name)\
-	static ret *name(_type _name) {					\
-		ret *d;							\
-		for (d = head[_name % SK_HASH_SIZE]; d; d = d->next) {	\
-			if (d->_member == _name)			\
-				break;					\
-		}							\
-		return d;						\
-	}
+#define for_each_sk_op(i, ops)				\
+	for (i = 0; i < ARRAY_SIZE(ops); i++)
 
 static struct socket_desc *sockets[SK_HASH_SIZE];
-__gen_static_lookup_func(struct socket_desc, lookup_socket, sockets,
-			ino, int, ino);
+__gen_lookup_func(struct socket_desc, lookup_socket, sockets, ino, unsigned int, ino);
 
-static struct unix_sk_listen_icon *unix_listen_icons[SK_HASH_SIZE];
-__gen_static_lookup_func(struct unix_sk_listen_icon,
-			 lookup_unix_listen_icons,
-			 unix_listen_icons,
-			 peer_ino, unsigned int, ino);
-
-static int sk_collect_one(int ino, int family, struct socket_desc *d)
+void sk_collect_one(unsigned int ino, unsigned int family, struct socket_desc *d)
 {
 	d->ino		= ino;
 	d->family	= family;
-
 	SK_HASH_LINK(sockets, ino, d);
-
-	return 0;
-}
-
-static void show_one_inet(const char *act, const struct inet_sk_desc *sk)
-{
-	char src_addr[INET_ADDR_LEN] = "<unknown>";
-
-	if (inet_ntop(sk->sd.family, (void *)sk->src_addr, src_addr,
-		      INET_ADDR_LEN) == NULL) {
-		pr_perror("Failed to translate address");
-	}
-
-	pr_debug("\t%s: ino 0x%8x family %4d type %4d port %8d "
-		"state %2d src_addr %s\n",
-		act, sk->sd.ino, sk->sd.family, sk->type, sk->src_port,
-		sk->state, src_addr);
-}
-
-static void show_one_inet_img(const char *act, const struct inet_sk_entry *e)
-{
-	char src_addr[INET_ADDR_LEN] = "<unknown>";
-
-	if (inet_ntop(e->family, (void *)e->src_addr, src_addr,
-		      INET_ADDR_LEN) == NULL) {
-		pr_perror("Failed to translate address");
-	}
-
-	pr_debug("\t%s: family %d type %d proto %d port %d "
-		"state %d src_addr %s\n",
-		act, e->family, e->type, e->proto, e->src_port,
-		e->state, src_addr);
-}
-
-static void show_one_unix(char *act, const struct unix_sk_desc *sk)
-{
-	pr_debug("\t%s: ino 0x%8x family %4d type %4d state %2d name %s\n",
-		act, sk->sd.ino, sk->sd.family, sk->type, sk->state, sk->name);
-
-	if (sk->nr_icons) {
-		int i;
-
-		for (i = 0; i < sk->nr_icons; i++)
-			pr_debug("\t\ticon: %4d\n", sk->icons[i]);
-	}
 }
 
-static void show_one_unix_img(const char *act, const struct unix_sk_entry *e)
+static struct sk_ops *lookup_sk_ops(int family)
 {
-	pr_info("\t%s: id %u type %d state %d name %d bytes\n",
-		act, e->id, e->type, e->state, e->namelen);
-}
+	unsigned int i;
 
-static int can_dump_inet_sk(const struct inet_sk_desc *sk)
-{
-	if (sk->sd.family != AF_INET && sk->sd.family != AF_INET6) {
-		pr_err("Only IPv4/6 sockets for now\n");
-		return 0;
-	}
-
-	if (sk->type == SOCK_DGRAM)
-		return 1;
-
-	if (sk->type != SOCK_STREAM) {
-		pr_err("Only stream and dgram inet sockets for now\n");
-		return 0;
-	}
-
-	switch (sk->state) {
-	case TCP_LISTEN:
-		if (sk->rqlen != 0) {
-			/*
-			 * Currently the ICONS nla reports the conn
-			 * requests for listen sockets. Need to pick
-			 * those up and fix the connect job respectively
-			 */
-			pr_err("In-flight connection (l)\n");
-			return 0;
-		}
-		break;
-	default:
-		pr_err("Unknown state %d\n", sk->state);
-		return 0;
+	for_each_sk_op(i, sk_ops) {
+		if (sk_ops[i]->family == family)
+			return sk_ops[i];
 	}
 
-	return 1;
-}
-
-static int dump_one_inet(struct socket_desc *_sk, struct fd_parms *p,
-			 const struct cr_fdset *cr_fdset)
-{
-	struct inet_sk_desc *sk = (struct inet_sk_desc *)_sk;
-	struct inet_sk_entry ie;
-	struct fdinfo_entry fe;
-
-	if (!can_dump_inet_sk(sk))
-		goto err;
-
-	fe.fd = p->fd;
-	fe.type = FDINFO_INETSK;
-	fe.id = sk->sd.ino;
-	fe.flags = p->fd_flags;
-
-	if (write_img(fdset_fd(cr_fdset, CR_FD_FDINFO), &fe))
-		goto err;
-
-	if (sk->sd.already_dumped)
-		return 0;
-
-	memset(&ie, 0, sizeof(ie));
-
-	ie.id		= sk->sd.ino;
-	ie.family	= sk->sd.family;
-	ie.type		= sk->type;
-	ie.proto	= sk->proto;
-	ie.state	= sk->state;
-	ie.src_port	= sk->src_port;
-	ie.dst_port	= sk->dst_port;
-	ie.backlog	= sk->wqlen;
-	ie.flags	= p->flags;
-	ie.fown		= p->fown;
-	memcpy(ie.src_addr, sk->src_addr, sizeof(u32) * 4);
-	memcpy(ie.dst_addr, sk->dst_addr, sizeof(u32) * 4);
-
-	if (write_img(fdset_fd(glob_fdset, CR_FD_INETSK), &ie))
-		goto err;
-
-	pr_info("Dumping inet socket at %d\n", p->fd);
-	show_one_inet("Dumping", sk);
-	show_one_inet_img("Dumped", &ie);
-	sk->sd.already_dumped = 1;
-	return 0;
-
-err:
-	return -1;
-}
-
-static int can_dump_unix_sk(const struct unix_sk_desc *sk)
-{
-	if (sk->type != SOCK_STREAM &&
-	    sk->type != SOCK_DGRAM) {
-		pr_err("Only stream/dgram sockets for now\n");
-		return 0;
-	}
-
-	switch (sk->state) {
-	case TCP_LISTEN:
-		break;
-	case TCP_ESTABLISHED:
-		break;
-	case TCP_CLOSE:
-		if (sk->type != SOCK_DGRAM)
-			return 0;
-		break;
-	default:
-		pr_err("Unknown state %d\n", sk->state);
-		return 0;
-	}
-
-	return 1;
-}
-
-static int dump_one_unix(const struct socket_desc *_sk, struct fd_parms *p,
-		int lfd, const struct cr_fdset *cr_fdset)
-{
-	struct unix_sk_desc *sk = (struct unix_sk_desc *)_sk;
-	struct fdinfo_entry fe;
-	struct unix_sk_entry ue;
-
-	if (!can_dump_unix_sk(sk))
-		goto err;
-
-	fe.fd = p->fd;
-	fe.type = FDINFO_UNIXSK;
-	fe.id = sk->sd.ino;
-	fe.flags = p->fd_flags;
-
-	if (write_img(fdset_fd(cr_fdset, CR_FD_FDINFO), &fe))
-		goto err;
-
-	if (sk->sd.already_dumped)
-		return 0;
-
-	ue.id		= sk->sd.ino;
-	ue.type		= sk->type;
-	ue.state	= sk->state;
-	ue.namelen	= sk->namelen;
-	ue.flags	= p->flags;
-	ue.backlog	= sk->wqlen;
-	ue.peer		= sk->peer_ino;
-	ue.fown		= p->fown;
-	ue.uflags	= 0;
-
-	if (ue.peer) {
-		struct unix_sk_desc *peer;
-
-		peer = (struct unix_sk_desc *)lookup_socket(ue.peer);
-		if (!peer) {
-			pr_err("Unix socket 0x%x without peer 0x%x\n",
-					ue.id, ue.peer);
-			goto err;
-		}
-
-		/*
-		 * Peer should have us as peer or have a name by which
-		 * we can access one.
-		 */
-		if (peer->peer_ino != ue.id) {
-			if (!peer->name) {
-				pr_err("Unix socket 0x%x with unreachable peer 0x%x (0x%x/%s)\n",
-				       ue.id, ue.peer, peer->peer_ino, peer->name);
-				goto err;
-			}
-
-			/*
-			 * It can be external socket, so we defer dumping
-			 * until all sockets the program owns are processed.
-			 */
-			peer->sd.external = true;
-		}
-	} else if (ue.state == TCP_ESTABLISHED) {
-		const struct unix_sk_listen_icon *e;
-
-		/*
-		 * If this is in-flight connection we need to figure
-		 * out where to connect it on restore. Thus, tune up peer
-		 * id by searching an existing listening socket.
-		 *
-		 * Note the socket name will be found at restore stage,
-		 * not now, just to reduce size of dump files.
-		 */
-
-		e = lookup_unix_listen_icons(ue.id);
-		if (!e) {
-			pr_err("Dangling in-flight connection %d\n", ue.id);
-			goto err;
-		}
-
-		/* e->sk_desc is _never_ NULL */
-		if (e->sk_desc->state != TCP_LISTEN) {
-			pr_err("In-flight connection on "
-				"non-listening socket %d\n", ue.id);
-			goto err;
-		}
-
-		ue.peer = e->sk_desc->sd.ino;
-
-		pr_debug("\t\tFixed inflight socket 0x%x peer 0x%x)\n",
-				ue.id, ue.peer);
-	}
-
-	if (write_img(fdset_fd(glob_fdset, CR_FD_UNIXSK), &ue))
-		goto err;
-	if (write_img_buf(fdset_fd(glob_fdset, CR_FD_UNIXSK), sk->name, ue.namelen))
-		goto err;
-
-	if (sk->rqlen != 0 && !(sk->type == SOCK_STREAM &&
-				sk->state == TCP_LISTEN))
-		if (dump_sk_queue(lfd, ue.id))
-			goto err;
-
-	pr_info("Dumping unix socket at %d\n", p->fd);
-	show_one_unix("Dumping", sk);
-	show_one_unix_img("Dumped", &ue);
-
-	sk->sd.already_dumped = 1;
-	return 0;
-
-err:
-	return -1;
+	return NULL;
 }
 
 int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
 {
 	struct socket_desc *sk;
+	struct sk_ops *sk_ops;
 
 	sk = lookup_socket(p->stat.st_ino);
 	if (!sk) {
@@ -396,222 +80,17 @@ int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
 		return -1;
 	}
 
-	switch (sk->family) {
-	case AF_UNIX:
-		return dump_one_unix(sk, p, lfd, cr_fdset);
-	case AF_INET:
-	case AF_INET6:
-		return dump_one_inet(sk, p, cr_fdset);
-	default:
-		pr_err("BUG! Unknown socket collected\n");
-		break;
-	}
-
-	return -1;
-}
-
-static int inet_collect_one(struct nlmsghdr *h, int family, int type, int proto)
-{
-	struct inet_sk_desc *d;
-	struct inet_diag_msg *m = NLMSG_DATA(h);
-	struct rtattr *tb[INET_DIAG_MAX+1];
-	int ret;
-
-	parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(m + 1),
-		     h->nlmsg_len - NLMSG_LENGTH(sizeof(*m)));
-
-	d = xzalloc(sizeof(*d));
-	if (!d)
+	sk_ops = lookup_sk_ops(sk->family);
+	if (!sk_ops) {
+		pr_err("BUG! Unknown socket collected (family %d)\n", sk->family);
 		return -1;
-
-	d->type = type;
-	d->proto = proto;
-	d->src_port = ntohs(m->id.idiag_sport);
-	d->dst_port = ntohs(m->id.idiag_dport);
-	d->state = m->idiag_state;
-	d->rqlen = m->idiag_rqueue;
-	d->wqlen = m->idiag_wqueue;
-	memcpy(d->src_addr, m->id.idiag_src, sizeof(u32) * 4);
-	memcpy(d->dst_addr, m->id.idiag_dst, sizeof(u32) * 4);
-
-	ret = sk_collect_one(m->idiag_inode, family, &d->sd);
-
-	show_one_inet("Collected", d);
-
-	return ret;
-}
-
-static int inet_tcp_receive_one(struct nlmsghdr *h)
-{
-	return inet_collect_one(h, AF_INET, SOCK_STREAM, IPPROTO_TCP);
-}
-
-static int inet_udp_receive_one(struct nlmsghdr *h)
-{
-	return inet_collect_one(h, AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-}
-
-static int inet_udplite_receive_one(struct nlmsghdr *h)
-{
-	return inet_collect_one(h, AF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);
-}
-
-static int inet6_tcp_receive_one(struct nlmsghdr *h)
-{
-	return inet_collect_one(h, AF_INET6, SOCK_STREAM, IPPROTO_TCP);
-}
-
-static int inet6_udp_receive_one(struct nlmsghdr *h)
-{
-	return inet_collect_one(h, AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-}
-
-static int inet6_udplite_receive_one(struct nlmsghdr *h)
-{
-	return inet_collect_one(h, AF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
-}
-
-static int unix_collect_one(const struct unix_diag_msg *m,
-		struct rtattr **tb)
-{
-	struct unix_sk_desc *d, **h;
-
-	d = xzalloc(sizeof(*d));
-	if (!d)
-		return -1;
-
-	d->type	= m->udiag_type;
-	d->state= m->udiag_state;
-
-	if (tb[UNIX_DIAG_PEER])
-		d->peer_ino = *(int *)RTA_DATA(tb[UNIX_DIAG_PEER]);
-
-	if (tb[UNIX_DIAG_NAME]) {
-		int len		= RTA_PAYLOAD(tb[UNIX_DIAG_NAME]);
-		char *name	= xmalloc(len + 1);
-
-		if (!name)
-			goto err;
-
-		memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len);
-		name[len] = '\0';
-
-		if (name[0] != '\0') {
-			struct unix_diag_vfs *uv;
-			struct stat st;
-
-			if (name[0] != '/') {
-				pr_warn("Relative bind path '%s' "
-					"unsupported\n", name);
-				xfree(name);
-				xfree(d);
-				return 0;
-			}
-
-			if (!tb[UNIX_DIAG_VFS]) {
-				pr_err("Bound socket w/o inode %d\n",
-						m->udiag_ino);
-				goto err;
-			}
-
-			uv = RTA_DATA(tb[UNIX_DIAG_VFS]);
-			if (stat(name, &st)) {
-				pr_perror("Can't stat socket %d(%s)",
-						m->udiag_ino, name);
-				goto err;
-			}
-
-			if ((st.st_ino != uv->udiag_vfs_ino) ||
-			    (st.st_dev != kdev_to_odev(uv->udiag_vfs_dev))) {
-				pr_info("unix: Dropping path for "
-						"unlinked bound "
-						"sk 0x%x.0x%x real 0x%x.0x%x\n",
-						(int)st.st_dev,
-						(int)st.st_ino,
-						(int)uv->udiag_vfs_dev,
-						(int)uv->udiag_vfs_ino);
-				/*
-				 * When a socket is bound to unlinked file, we
-				 * just drop his name, since noone will access
-				 * it via one.
-				 */
-				xfree(name);
-				len = 0;
-				name = NULL;
-			}
-		}
-
-		d->namelen = len;
-		d->name = name;
-	}
-
-	if (tb[UNIX_DIAG_ICONS]) {
-		int len = RTA_PAYLOAD(tb[UNIX_DIAG_ICONS]);
-		int i;
-
-		d->icons = xmalloc(len);
-		if (!d->icons)
-			goto err;
-
-		memcpy(d->icons, RTA_DATA(tb[UNIX_DIAG_ICONS]), len);
-		d->nr_icons = len / sizeof(u32);
-
-		/*
-		 * Remember these sockets, we will need them
-		 * to fix up in-flight sockets peers.
-		 */
-		for (i = 0; i < d->nr_icons; i++) {
-			struct unix_sk_listen_icon *e;
-			int n;
-
-			e = xzalloc(sizeof(*e));
-			if (!e)
-				goto err;
-
-			SK_HASH_LINK(unix_listen_icons, d->icons[i], e);
-
-			pr_debug("\t\tCollected icon %d\n", d->icons[i]);
-
-			e->peer_ino	= d->icons[i];
-			e->sk_desc	= d;
-		}
-
-
-	}
-
-	if (tb[UNIX_DIAG_RQLEN]) {
-		struct unix_diag_rqlen *rq;
-
-		rq = (struct unix_diag_rqlen *)RTA_DATA(tb[UNIX_DIAG_RQLEN]);
-		d->rqlen = rq->udiag_rqueue;
-		d->wqlen = rq->udiag_wqueue;
 	}
 
-	sk_collect_one(m->udiag_ino, AF_UNIX, &d->sd);
-	show_one_unix("Collected", d);
-
-	return 0;
-
-err:
-	xfree(d->icons);
-	xfree(d->name);
-	xfree(d);
-	return -1;
-}
-
-static int unix_receive_one(struct nlmsghdr *h)
-{
-	struct unix_diag_msg *m = NLMSG_DATA(h);
-	struct rtattr *tb[UNIX_DIAG_MAX+1];
-
-	parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr *)(m + 1),
-		     h->nlmsg_len - NLMSG_LENGTH(sizeof(*m)));
-
-	return unix_collect_one(m, tb);
+	return sk_ops->dump_one(sk, p, lfd, cr_fdset);
 }
 
-static int collect_sockets_nl(int nl, void *req, int size,
-			      int (*receive_callback)(struct nlmsghdr *h))
+static int collect_sockets_nl(int nl, void *req, int size, void *aux,
+			      int (*receive)(struct nlmsghdr *h, void *aux))
 {
 	struct msghdr msg;
 	struct sockaddr_nl nladdr;
@@ -658,7 +137,7 @@ static int collect_sockets_nl(int nl, void *req, int size,
 		if (err == 0)
 			break;
 
-		err = nlmsg_receive(buf, err, receive_callback);
+		err = nlmsg_receive(buf, err, aux, receive);
 		if (err < 0)
 			goto err;
 		if (err == 0)
@@ -666,78 +145,16 @@ static int collect_sockets_nl(int nl, void *req, int size,
 	}
 
 	return 0;
-
-err:
-	return -1;
-}
-
-int dump_external_sockets(void)
-{
-	struct socket_desc *head, *sd;
-	int i, ret = -1;
-
-	if (!opts.ext_unix_sk)
-		return 0;
-
-	pr_debug("Dumping external sockets\n");
-
-	for (i = 0; i < SK_HASH_SIZE; i++) {
-		head = sockets[i];
-		if (!head)
-			continue;
-
-		for (sd = head; sd; sd = sd->next) {
-			struct unix_sk_entry e = { };
-			struct unix_sk_desc *sk;
-
-			if (sd->already_dumped		||
-			    sd->external == false	||
-			    sd->family != AF_UNIX)
-				continue;
-
-			sk = container_of(sd, struct unix_sk_desc, sd);
-
-			if (sk->type != SOCK_DGRAM)
-				continue;
-
-			e.id		= sd->ino;
-			e.type		= SOCK_DGRAM;
-			e.state		= TCP_LISTEN;
-			e.namelen	= sk->namelen;
-			e.uflags	= USK_EXTERN;
-			e.peer		= 0;
-
-			show_one_unix("Dumping extern", sk);
-
-			if (write_img(fdset_fd(glob_fdset, CR_FD_UNIXSK), &e))
-				goto err;
-			if (write_img_buf(fdset_fd(glob_fdset, CR_FD_UNIXSK),
-					  sk->name, e.namelen))
-				goto err;
-
-			show_one_unix_img("Dumped extern", &e);
-
-			sd->already_dumped = 1;
-		}
-	}
-
-	return 0;
 err:
 	return -1;
 }
 
 int collect_sockets(void)
 {
-	int err = 0, tmp;
-	int nl;
-	int supp_type = 0;
-	struct {
-		struct nlmsghdr hdr;
-		union {
-			struct unix_diag_req	u;
-			struct inet_diag_req_v2	i;
-		} r;
-	} req;
+	struct diag_req req = { };
+	struct sk_rt_collector *collector;
+	int err = 0;
+	int nl, i, j;
 
 	nl = socket(PF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
 	if (nl < 0) {
@@ -745,739 +162,85 @@ int collect_sockets(void)
 		return -1;
 	}
 
-	memset(&req, 0, sizeof(req));
 	req.hdr.nlmsg_len	= sizeof(req);
 	req.hdr.nlmsg_type	= SOCK_DIAG_BY_FAMILY;
 	req.hdr.nlmsg_flags	= NLM_F_DUMP | NLM_F_REQUEST;
 	req.hdr.nlmsg_seq	= CR_NLMSG_SEQ;
 
-	/* Collect UNIX sockets */
-	req.r.u.sdiag_family	= AF_UNIX;
-	req.r.u.udiag_states	= -1; /* All */
-	req.r.u.udiag_show	= UDIAG_SHOW_NAME | UDIAG_SHOW_VFS |
-				  UDIAG_SHOW_PEER | UDIAG_SHOW_ICONS |
-				  UDIAG_SHOW_RQLEN;
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), unix_receive_one);
-	if (tmp)
-		err = tmp;
-
-	/* Collect IPv4 TCP sockets */
-	req.r.i.sdiag_family	= AF_INET;
-	req.r.i.sdiag_protocol	= IPPROTO_TCP;
-	req.r.i.idiag_ext	= 0;
-	/* Only listening sockets supported yet */
-	req.r.i.idiag_states	= 1 << TCP_LISTEN;
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), inet_tcp_receive_one);
-	if (tmp)
-		err = tmp;
-
-	/* Collect IPv4 UDP sockets */
-	req.r.i.sdiag_family	= AF_INET;
-	req.r.i.sdiag_protocol	= IPPROTO_UDP;
-	req.r.i.idiag_ext	= 0;
-	req.r.i.idiag_states	= -1; /* All */
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), inet_udp_receive_one);
-	if (tmp)
-		err = tmp;
-
-	/* Collect IPv4 UDP-lite sockets */
-	req.r.i.sdiag_family	= AF_INET;
-	req.r.i.sdiag_protocol	= IPPROTO_UDPLITE;
-	req.r.i.idiag_ext	= 0;
-	req.r.i.idiag_states	= -1; /* All */
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), inet_udplite_receive_one);
-	if (tmp)
-		err = tmp;
-
-	/* Collect IPv6 TCP sockets */
-	req.r.i.sdiag_family	= AF_INET6;
-	req.r.i.sdiag_protocol	= IPPROTO_TCP;
-	req.r.i.idiag_ext	= 0;
-	/* Only listening sockets supported yet */
-	req.r.i.idiag_states	= 1 << TCP_LISTEN;
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), inet6_tcp_receive_one);
-	if (tmp)
-		err = tmp;
-
-	/* Collect IPv6 UDP sockets */
-	req.r.i.sdiag_family	= AF_INET6;
-	req.r.i.sdiag_protocol	= IPPROTO_UDP;
-	req.r.i.idiag_ext	= 0;
-	req.r.i.idiag_states	= -1; /* All */
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), inet6_udp_receive_one);
-	if (tmp)
-		err = tmp;
-
-	/* Collect IPv6 UDP-lite sockets */
-	req.r.i.sdiag_family	= AF_INET6;
-	req.r.i.sdiag_protocol	= IPPROTO_UDPLITE;
-	req.r.i.idiag_ext	= 0;
-	req.r.i.idiag_states	= -1; /* All */
-	tmp = collect_sockets_nl(nl, &req, sizeof(req), inet6_udplite_receive_one);
-	if (tmp)
-		err = tmp;
-out:
-	close(nl);
-	return err;
-}
-
-struct unix_sk_info {
-	struct unix_sk_entry ue;
-	struct list_head list;
-	char *name;
-	unsigned flags;
-	struct unix_sk_info *peer;
-	struct file_desc d;
-};
-
-#define USK_PAIR_MASTER		0x1
-#define USK_PAIR_SLAVE		0x2
-
-static LIST_HEAD(unix_sockets);
-
-static struct unix_sk_info *find_unix_sk(int id)
-{
-	struct file_desc *d;
-
-	d = find_file_desc_raw(FDINFO_UNIXSK, id);
-	if (d)
-		return container_of(d, struct unix_sk_info, d);
-	return NULL;
-}
-
-struct inet_sk_info {
-	struct inet_sk_entry ie;
-	struct file_desc d;
-};
-
-static int open_inet_sk(struct file_desc *d);
-
-static struct file_desc_ops inet_desc_ops = {
-	.open = open_inet_sk,
-};
-
-int collect_inet_sockets(void)
-{
-	struct inet_sk_info *ii = NULL;
-	int fd, ret = -1;
-
-	fd = open_image_ro(CR_FD_INETSK);
-	if (fd < 0)
-		return -1;
-
-	while (1) {
-		ii = xmalloc(sizeof(*ii));
-		ret = -1;
-		if (!ii)
-			break;
-
-		ret = read_img_eof(fd, &ii->ie);
-		if (ret <= 0)
-			break;
-
-		file_desc_add(&ii->d, FDINFO_INETSK, ii->ie.id,
-				&inet_desc_ops);
-	}
-
-	if (ii)
-		xfree(ii);
-
-	close(fd);
-	return 0;
-}
-
-static int open_inet_sk(struct file_desc *d)
-{
-	union {
-		struct sockaddr_in	v4;
-		struct sockaddr_in6	v6;
-	} addr;
-	struct inet_sk_info *ii;
-	int sk, addr_size;
-
-	ii = container_of(d, struct inet_sk_info, d);
-
-	show_one_inet_img("Restore", &ii->ie);
-
-	if (ii->ie.family != AF_INET && ii->ie.family != AF_INET6) {
-		pr_err("Unsupported socket family: %d\n", ii->ie.family);
-		return -1;
-	}
-
-	if ((ii->ie.type != SOCK_STREAM) && (ii->ie.type != SOCK_DGRAM)) {
-		pr_err("Unsupported socket type: %d\n", ii->ie.type);
-		return -1;
-	}
-
-	sk = socket(ii->ie.family, ii->ie.type, ii->ie.proto);
-	if (sk < 0) {
-		pr_perror("Can't create unix socket");
-		return -1;
-	}
-
-	if (restore_fown(sk, &ii->ie.fown))
-		goto err;
-
-	/*
-	 * Listen sockets are easiest ones -- simply
-	 * bind() and listen(), and that's all.
-	 */
-	memzero(&addr, sizeof(addr));
-	if (ii->ie.family == AF_INET) {
-		addr.v4.sin_family = ii->ie.family;
-		addr.v4.sin_port = htons(ii->ie.src_port);
-		memcpy(&addr.v4.sin_addr.s_addr, ii->ie.src_addr, sizeof(ii->ie.src_addr));
-		addr_size = sizeof(addr.v4);
-	} else if (ii->ie.family == AF_INET6) {
-		addr.v6.sin6_family = ii->ie.family;
-		addr.v6.sin6_port = htons(ii->ie.src_port);
-		memcpy(&addr.v6.sin6_addr.s6_addr, ii->ie.src_addr, sizeof(ii->ie.src_addr));
-		addr_size = sizeof(addr.v6);
-	} else
-		BUG_ON(1);
-
-	if (bind(sk, (struct sockaddr *)&addr, addr_size) == -1) {
-		pr_perror("Can't bind to a socket");
-		goto err;
-	}
-
-	if (ii->ie.state == TCP_LISTEN) {
-		if (ii->ie.proto != IPPROTO_TCP) {
-			pr_err("Wrong socket in listen state %d\n", ii->ie.proto);
-			goto err;
-		}
-
-		if (listen(sk, ii->ie.backlog) == -1) {
-			pr_perror("Can't listen on a socket");
-			goto err;
-		}
-	}
-
-	if (ii->ie.state == TCP_ESTABLISHED) {
-		if (ii->ie.proto == IPPROTO_TCP) {
-			pr_err("Connected TCP socket in image\n");
-			goto err;
-		}
-
-		memzero(&addr, sizeof(addr));
-		if (ii->ie.family == AF_INET) {
-			addr.v4.sin_family = ii->ie.family;
-			addr.v4.sin_port = htons(ii->ie.dst_port);
-			memcpy(&addr.v4.sin_addr.s_addr, ii->ie.dst_addr, sizeof(ii->ie.dst_addr));
-			addr_size = sizeof(addr.v4);
-		} else if (ii->ie.family == AF_INET6) {
-			addr.v6.sin6_family = ii->ie.family;
-			addr.v6.sin6_port = htons(ii->ie.dst_port);
-			memcpy(&addr.v6.sin6_addr.s6_addr, ii->ie.dst_addr, sizeof(ii->ie.dst_addr));
-			addr_size = sizeof(addr.v6);
-		} else
-			BUG_ON(1);
-
-		if (connect(sk, (struct sockaddr *)&addr, addr_size) == -1) {
-			pr_perror("Can't connect UDP socket back");
-			goto err;
+	for_each_sk_op(i, sk_ops) {
+		collector = sk_ops[i]->rt_collector;
+		for (j = 0; j < sk_ops[i]->nr_rt_collectors; j++) {
+			req.r = collector[j].r;
+			err = collect_sockets_nl(nl, &req, sizeof(req),
+						 &collector[j],
+						 collector->collect);
+			if (err)
+				return err;
 		}
 	}
-
-	if (set_fd_flags(sk, ii->ie.flags))
-		return -1;
-
-	return sk;
-
-err:
-	close(sk);
-	return -1;
-}
-
-static inline char *unknown(u32 val)
-{
-	static char unk[12];
-	snprintf(unk, sizeof(unk), "x%d", val);
-	return unk;
-}
-
-static inline char *skfamily2s(u32 f)
-{
-	if (f == AF_INET)
-		return " inet";
-	else if (f == AF_INET6)
-		return "inet6";
-	else
-		return unknown(f);
-}
-
-static inline char *sktype2s(u32 t)
-{
-	if (t == SOCK_STREAM)
-		return "stream";
-	else if (t == SOCK_DGRAM)
-		return " dgram";
-	else
-		return unknown(t);
-}
-
-static inline char *skproto2s(u32 p)
-{
-	if (p == IPPROTO_UDP)
-		return "udp";
-	else if (p == IPPROTO_UDPLITE)
-		return "udpl";
-	else if (p == IPPROTO_TCP)
-		return "tcp";
-	else
-		return unknown(p);
-}
-
-static inline char *skstate2s(u32 state)
-{
-	if (state == TCP_ESTABLISHED)
-		return " estab";
-	else if (state == TCP_CLOSE)
-		return "closed";
-	else if (state == TCP_LISTEN)
-		return "listen";
-	else
-		return unknown(state);
-}
-
-void show_inetsk(int fd, struct cr_options *o)
-{
-	struct inet_sk_entry ie;
-	int ret = 0;
-
-	pr_img_head(CR_FD_INETSK);
-
-	while (1) {
-		char src_addr[INET_ADDR_LEN] = "<unknown>";
-		char dst_addr[INET_ADDR_LEN] = "<unknown>";
-
-		ret = read_img_eof(fd, &ie);
-		if (ret <= 0)
-			goto out;
-
-		if (inet_ntop(ie.family, (void *)ie.src_addr, src_addr,
-			      INET_ADDR_LEN) == NULL) {
-			pr_perror("Failed to translate src address");
-		}
-
-		if (ie.state == TCP_ESTABLISHED) {
-			if (inet_ntop(ie.family, (void *)ie.dst_addr, dst_addr,
-				      INET_ADDR_LEN) == NULL) {
-				pr_perror("Failed to translate dst address");
-			}
-		}
-
-		pr_msg("id 0x%x family %s type %s proto %s state %s %s:%d <-> %s:%d flags 0x%2x\n",
-			ie.id, skfamily2s(ie.family), sktype2s(ie.type), skproto2s(ie.proto),
-			skstate2s(ie.state), src_addr, ie.src_port, dst_addr, ie.dst_port, ie.flags);
-		pr_msg("\t"), show_fown_cont(&ie.fown), pr_msg("\n");
-	}
-
 out:
-	if (ret)
-		pr_info("\n");
-	pr_img_tail(CR_FD_INETSK);
-}
-
-void show_unixsk(int fd, struct cr_options *o)
-{
-	struct unix_sk_entry ue;
-	int ret = 0;
-
-	pr_img_head(CR_FD_UNIXSK);
-
-	while (1) {
-		ret = read_img_eof(fd, &ue);
-		if (ret <= 0)
-			goto out;
-
-		pr_msg("id 0x%8x type %s state %s namelen %4d backlog %4d peer 0x%8x flags 0x%2x uflags 0x%2x",
-			ue.id, sktype2s(ue.type), skstate2s(ue.state),
-			ue.namelen, ue.backlog, ue.peer, ue.flags, ue.uflags);
-
-		if (ue.namelen) {
-			BUG_ON(ue.namelen > sizeof(buf));
-			ret = read_img_buf(fd, buf, ue.namelen);
-			if (ret < 0) {
-				pr_info("\n");
-				goto out;
-			}
-			if (!buf[0])
-				buf[0] = '@';
-			pr_msg(" --> %s\n", buf);
-		} else
-			pr_msg("\n");
-		pr_msg("\t"), show_fown_cont(&ue.fown), pr_msg("\n");
-	}
-out:
-	pr_img_tail(CR_FD_UNIXSK);
-}
-
-struct unix_conn_job {
-	struct unix_sk_info	*sk;
-	struct unix_conn_job	*next;
-};
-
-static struct unix_conn_job *conn_jobs;
-
-static int schedule_conn_job(struct unix_sk_info *ui)
-{
-	struct unix_conn_job *cj;
-
-	cj = xmalloc(sizeof(*cj));
-	if (!cj)
-		return -1;
-
-	cj->sk = ui;
-	cj->next = conn_jobs;
-	conn_jobs = cj;
-
+	close(nl);
 	return 0;
 }
 
-int run_unix_connections(void)
+int dump_external_sockets(void)
 {
-	struct unix_conn_job *cj;
-
-	pr_info("Running delayed unix connections\n");
+	struct socket_desc *head, *sd;
+	int i, j, err;
 
-	cj = conn_jobs;
-	while (cj) {
-		int attempts = 8;
-		struct unix_sk_info *ui = cj->sk;
-		struct unix_sk_info *peer = ui->peer;
-		struct fdinfo_list_entry *fle;
-		struct sockaddr_un addr;
+	if (!opts.ext_unix_sk)
+		return 0;
 
-		pr_info("\tConnect 0x%x to 0x%x\n", ui->ue.id, peer->ue.id);
+	pr_debug("Dumping external sockets\n");
 
-		fle = file_master(&ui->d);
+	for_each_sk_op(i, sk_ops) {
+		if (!sk_ops[i]->dump_external)
+			continue;
+		for (j = 0; j < SK_HASH_SIZE; j++) {
+			head = sockets[j];
+			if (!head)
+				continue;
 
-		memset(&addr, 0, sizeof(addr));
-		addr.sun_family = AF_UNIX;
-		memcpy(&addr.sun_path, peer->name, peer->ue.namelen);
-try_again:
-		if (connect(fle->fe.fd, (struct sockaddr *)&addr,
-					sizeof(addr.sun_family) +
-					peer->ue.namelen) < 0) {
-			if (attempts) {
-				usleep(1000);
-				attempts--;
-				goto try_again; /* FIXME use futex waiters */
+			for (sd = head; sd; sd = sd->next) {
+				err = sk_ops[j]->dump_external(head);
+				if (err)
+					return err;
 			}
-
-			pr_perror("Can't connect 0x%x socket", ui->ue.id);
-			return -1;
 		}
-
-		if (restore_sk_queue(fle->fe.fd, peer->ue.id))
-			return -1;
-
-		if (set_fd_flags(fle->fe.fd, ui->ue.flags))
-			return -1;
-
-		cj = cj->next;
 	}
 
 	return 0;
 }
 
-static int bind_unix_sk(int sk, struct unix_sk_info *ui)
-{
-	struct sockaddr_un addr;
-
-	if ((ui->ue.type == SOCK_STREAM) && (ui->ue.state != TCP_LISTEN))
-		/*
-		 * FIXME this can be done, but for doing this properly we
-		 * need to bind socket to its name, then rename one to
-		 * some temporary unique one and after all the sockets are
-		 * restored we should walk those temp names and rename
-		 * some of them back to real ones.
-		 */
-		goto done;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sun_family = AF_UNIX;
-	memcpy(&addr.sun_path, ui->name, ui->ue.namelen);
-
-	if (bind(sk, (struct sockaddr *)&addr,
-				sizeof(addr.sun_family) + ui->ue.namelen)) {
-		pr_perror("Can't bind socket");
-		return -1;
-	}
-done:
-	return 0;
-}
-
-static int unixsk_should_open_transport(struct fdinfo_entry *fe,
-				struct file_desc *d)
-{
-	struct unix_sk_info *ui;
-
-	ui = container_of(d, struct unix_sk_info, d);
-	return ui->flags & USK_PAIR_SLAVE;
-}
-
-static int open_unixsk_pair_master(struct unix_sk_info *ui)
-{
-	int sk[2], tsk;
-	struct unix_sk_info *peer = ui->peer;
-	struct fdinfo_list_entry *fle;
-
-	pr_info("Opening pair master (id 0x%x peer 0x%x)\n",
-			ui->ue.id, ui->ue.peer);
-
-	if (socketpair(PF_UNIX, ui->ue.type, 0, sk) < 0) {
-		pr_perror("Can't make socketpair");
-		return -1;
-	}
-
-	if (restore_sk_queue(sk[0], peer->ue.id))
-		return -1;
-	if (restore_sk_queue(sk[1], ui->ue.id))
-		return -1;
-
-	if (set_fd_flags(sk[0], ui->ue.flags))
-		return -1;
-	if (set_fd_flags(sk[1], peer->ue.flags))
-		return -1;
-
-	if (restore_fown(sk[0], &ui->ue.fown))
-		return -1;
-	if (restore_fown(sk[1], &peer->ue.fown))
-		return -1;
-
-	if (bind_unix_sk(sk[0], ui))
-		return -1;
-
-	tsk = socket(PF_UNIX, SOCK_DGRAM, 0);
-	if (tsk < 0) {
-		pr_perror("Can't make transport socket");
-		return -1;
-	}
-
-	fle = file_master(&peer->d);
-	if (send_fd_to_peer(sk[1], fle, tsk)) {
-		pr_err("Can't send pair slave\n");
-		return -1;
-	}
-
-	close(tsk);
-	close(sk[1]);
-
-	return sk[0];
-}
-
-static int open_unixsk_pair_slave(struct unix_sk_info *ui)
-{
-	struct fdinfo_list_entry *fle;
-	int sk;
-
-	fle = file_master(&ui->d);
-
-	pr_info("Opening pair slave (id 0x%x peer 0x%x) on %d\n",
-			ui->ue.id, ui->ue.peer, fle->fe.fd);
-
-	sk = recv_fd(fle->fe.fd);
-	if (sk < 0) {
-		pr_err("Can't recv pair slave");
-		return -1;
-	}
-	close(fle->fe.fd);
-
-	if (bind_unix_sk(sk, ui))
-		return -1;
-
-	return sk;
-}
-
-static int open_unixsk_standalone(struct unix_sk_info *ui)
-{
-	int sk;
-
-	pr_info("Opening standalone socket (id 0x%x peer 0x%x)\n",
-			ui->ue.id, ui->ue.peer);
-
-	sk = socket(PF_UNIX, ui->ue.type, 0);
-	if (sk < 0) {
-		pr_perror("Can't make unix socket");
-		return -1;
-	}
-
-	if (restore_fown(sk, &ui->ue.fown))
-		return -1;
-
-	if (bind_unix_sk(sk, ui))
-		return -1;
-
-	if (ui->ue.state == TCP_LISTEN) {
-		pr_info("\tPutting 0x%x into listen state\n", ui->ue.id);
-		if (listen(sk, ui->ue.backlog) < 0) {
-			pr_perror("Can't make usk listen");
-			return -1;
-		}
-	} else if (ui->peer) {
-		pr_info("\tWill connect 0x%x to 0x%x later\n", ui->ue.id, ui->ue.peer);
-		if (schedule_conn_job(ui))
-			return -1;
-	}
-
-	return sk;
-}
-
-static int open_unix_sk(struct file_desc *d)
+int resolve_sockets_peers(void)
 {
-	struct unix_sk_info *ui;
+	int i, err;
 
-	ui = container_of(d, struct unix_sk_info, d);
-	if (ui->flags & USK_PAIR_MASTER)
-		return open_unixsk_pair_master(ui);
-	else if (ui->flags & USK_PAIR_SLAVE)
-		return open_unixsk_pair_slave(ui);
-	else
-		return open_unixsk_standalone(ui);
-}
-
-static struct file_desc_ops unix_desc_ops = {
-	.open = open_unix_sk,
-	.want_transport = unixsk_should_open_transport,
-};
+	pr_debug("Resolving sockets peers\n");
 
-int collect_unix_sockets(void)
-{
-	int fd, ret;
-
-	pr_info("Reading unix sockets in\n");
-
-	fd = open_image_ro(CR_FD_UNIXSK);
-	if (fd < 0) {
-		if (errno == ENOENT)
-			return 0;
-		else
-			return -1;
-	}
-
-	while (1) {
-		struct unix_sk_info *ui;
-
-		ui = xmalloc(sizeof(*ui));
-		ret = -1;
-		if (ui == NULL)
-			break;
-
-		ret = read_img_eof(fd, &ui->ue);
-		if (ret <= 0) {
-			xfree(ui);
-			break;
-		}
-
-		if (ui->ue.namelen) {
-			ret = -1;
-
-			if (!ui->ue.namelen || ui->ue.namelen >= UNIX_PATH_MAX) {
-				pr_err("Bad unix name len %d\n", ui->ue.namelen);
-				break;
-			}
-
-			ui->name = xmalloc(ui->ue.namelen);
-			if (ui->name == NULL)
-				break;
-
-			ret = read_img_buf(fd, ui->name, ui->ue.namelen);
-			if (ret < 0)
-				break;
-
-			/*
-			 * Make FS clean from sockets we're about to
-			 * restore. See for how we bind them for details
-			 */
-			if (ui->name[0] != '\0' &&
-			    !(ui->ue.uflags & USK_EXTERN))
-				unlink(ui->name);
-		} else
-			ui->name = NULL;
-
-		ui->peer = NULL;
-		ui->flags = 0;
-		pr_info(" `- Got %u peer %u\n", ui->ue.id, ui->ue.peer);
-		file_desc_add(&ui->d, FDINFO_UNIXSK, ui->ue.id,
-				&unix_desc_ops);
-		list_add_tail(&ui->list, &unix_sockets);
+	for_each_sk_op(i, sk_ops) {
+		if (!sk_ops[i]->resolve_peers)
+			continue;
+		err = sk_ops[i]->resolve_peers();
+		if (err)
+			return err;
 	}
 
-	close(fd);
-
-	return read_sk_queues();
+	return 0;
 }
 
-int resolve_unix_peers(void)
+int connect_delayed_sockets(void)
 {
-	struct unix_sk_info *ui, *peer;
-	struct fdinfo_list_entry *fle, *fle_peer;
+	int i, err;
 
-	list_for_each_entry(ui, &unix_sockets, list) {
-		if (ui->peer)
-			continue;
-		if (!ui->ue.peer)
-			continue;
+	pr_debug("Connecting delayed sockets\n");
 
-		peer = find_unix_sk(ui->ue.peer);
-
-		/*
-		 * Connect to external sockets requires
-		 * special option to be passed.
-		 */
-		if (peer &&
-		    (peer->ue.uflags & USK_EXTERN) &&
-		    !(opts.ext_unix_sk))
-			peer = NULL;
-
-		if (!peer) {
-			pr_err("FATAL: Peer 0x%x unresolved for 0x%x\n",
-					ui->ue.peer, ui->ue.id);
-			return -1;
-		}
-
-		ui->peer = peer;
-		if (ui == peer)
-			/* socket connected to self %) */
+	for_each_sk_op(i, sk_ops) {
+		if (!sk_ops[i]->connect_delayed)
 			continue;
-		if (peer->ue.peer != ui->ue.id)
-			continue;
-
-		/* socketpair or interconnected sockets */
-		peer->peer = ui;
-
-		/*
-		 * Select who will restore the pair. Check is identical to
-		 * the one in pipes.c and makes sure tasks wait for each other
-		 * in pids sorting order (ascending).
-		 */
-
-		fle = file_master(&ui->d);
-		fle_peer = file_master(&peer->d);
-
-		if ((fle->pid < fle_peer->pid) ||
-				(fle->pid == fle_peer->pid &&
-				 fle->fe.fd < fle_peer->fe.fd)) {
-			ui->flags |= USK_PAIR_MASTER;
-			peer->flags |= USK_PAIR_SLAVE;
-		} else {
-			peer->flags |= USK_PAIR_MASTER;
-			ui->flags |= USK_PAIR_SLAVE;
-		}
-	}
-
-	pr_info("Unix sockets:\n");
-	list_for_each_entry(ui, &unix_sockets, list) {
-		struct fdinfo_list_entry *fle;
-
-		pr_info("\t0x%x -> 0x%x (0x%x) flags 0x%x\n", ui->ue.id, ui->ue.peer,
-				ui->peer ? ui->peer->ue.id : 0, ui->flags);
-		list_for_each_entry(fle, &ui->d.fd_info_head, desc_list)
-			pr_info("\t\tfd %d in pid %d\n",
-					fle->fe.fd, fle->pid);
-
+		err = sk_ops[i]->connect_delayed();
+		if (err)
+			return err;
 	}
 
 	return 0;
-- 
1.7.7.6



More information about the CRIU mailing list