[CRIU] [PATCH v2 2/3] inet: remember ipv6 connections' ifindex for restore

Tycho Andersen tycho.andersen at canonical.com
Tue Nov 24 13:28:17 PST 2015


For some ipv6 scope types (link local, amongo others), we need to tell the
kernel the ifindex of the interface whose address we want to bind to as
well as the address itself.

v2: use SO_BINDTODEVICE to get the ifname, and pass that through instead of
    using netlink to grab all the ipv6 sockets via rtnetlink.

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 protobuf/sk-inet.proto |  4 ++++
 sk-inet.c              | 42 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/protobuf/sk-inet.proto b/protobuf/sk-inet.proto
index ad49928..a2770df 100644
--- a/protobuf/sk-inet.proto
+++ b/protobuf/sk-inet.proto
@@ -33,4 +33,8 @@ message inet_sk_entry {
 	required sk_opts_entry		opts		= 14;
 	optional bool			v6only		= 15;
 	optional ip_opts_entry		ip_opts		= 16;
+
+	/* for ipv6, we need to send the ifindex to bind(); we keep the ifname
+	 * here and convert it on restore */
+	optional string			ifname		= 17;
 }
diff --git a/sk-inet.c b/sk-inet.c
index e08b2be..53e568c 100644
--- a/sk-inet.c
+++ b/sk-inet.c
@@ -2,6 +2,7 @@
 #include <sys/socket.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#include <net/if.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <netinet/tcp.h>
@@ -288,6 +289,8 @@ static int do_dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p, int fa
 	ie.n_dst_addr = PB_ALEN_INET;
 	if (ie.family == AF_INET6) {
 		int val;
+		char device[IFNAMSIZ];
+		socklen_t len = sizeof(device);
 
 		ie.n_src_addr = PB_ALEN_INET6;
 		ie.n_dst_addr = PB_ALEN_INET6;
@@ -298,6 +301,21 @@ static int do_dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p, int fa
 
 		ie.v6only = val ? true : false;
 		ie.has_v6only = true;
+
+		/* ifindex only matters on source ports for bind, so let's
+		 * find only that ifindex. */
+		if (getsockopt(lfd, SOL_SOCKET, SO_BINDTODEVICE, device, &len) < 0) {
+			pr_perror("can't get ifname");
+			goto err;
+		}
+
+		if (len > 0) {
+			ie.ifname = xstrdup(device);
+			if (!ie.ifname)
+				goto err;
+		} else {
+			pr_warn("couldn't find ifname for %d, can't bind\n", id);
+		}
 	}
 
 	ie.src_addr = xmalloc(pb_repeated_size(&ie, src_addr));
@@ -607,7 +625,7 @@ union sockaddr_inet {
 };
 
 static int restore_sockaddr(union sockaddr_inet *sa,
-		int family, u32 pb_port, u32 *pb_addr)
+		int family, u32 pb_port, u32 *pb_addr, u32 ifindex)
 {
 	BUILD_BUG_ON(sizeof(sa->v4.sin_addr.s_addr) > PB_ALEN_INET * sizeof(u32));
 	BUILD_BUG_ON(sizeof(sa->v6.sin6_addr.s6_addr) > PB_ALEN_INET6 * sizeof(u32));
@@ -625,6 +643,12 @@ static int restore_sockaddr(union sockaddr_inet *sa,
 		sa->v6.sin6_family = AF_INET6;
 		sa->v6.sin6_port = htons(pb_port);
 		memcpy(sa->v6.sin6_addr.s6_addr, pb_addr, sizeof(sa->v6.sin6_addr.s6_addr));
+
+		/* Here although the struct member is called scope_id, the
+		 * kernel really wants ifindex. See
+		 * /net/ipv6/af_inet6.c:inet6_bind for details.
+		 */
+		sa->v6.sin6_scope_id = ifindex;
 		return sizeof(sa->v6);
 	}
 
@@ -636,10 +660,18 @@ int inet_bind(int sk, struct inet_sk_info *ii)
 {
 	bool rst_freebind = false;
 	union sockaddr_inet addr;
-	int addr_size;
+	int addr_size, ifindex = 0;
+
+	if (ii->ie->ifname) {
+		ifindex = if_nametoindex(ii->ie->ifname);
+		if (!ifindex) {
+			pr_err("couldn't find ifindex for %s\n", ii->ie->ifname);
+			return -1;
+		}
+	}
 
 	addr_size = restore_sockaddr(&addr, ii->ie->family,
-			ii->ie->src_port, ii->ie->src_addr);
+			ii->ie->src_port, ii->ie->src_addr, ifindex);
 
 	/*
 	 * ipv6 addresses go through a “tentative” phase and
@@ -663,7 +695,7 @@ int inet_bind(int sk, struct inet_sk_info *ii)
 	}
 
 	if (bind(sk, (struct sockaddr *)&addr, addr_size) == -1) {
-		pr_perror("Can't bind inet socket");
+		pr_perror("Can't bind inet socket (id %d)", ii->ie->id);
 		return -1;
 	}
 
@@ -687,7 +719,7 @@ int inet_connect(int sk, struct inet_sk_info *ii)
 	int addr_size;
 
 	addr_size = restore_sockaddr(&addr, ii->ie->family,
-			ii->ie->dst_port, ii->ie->dst_addr);
+			ii->ie->dst_port, ii->ie->dst_addr, 0);
 
 	if (connect(sk, (struct sockaddr *)&addr, addr_size) == -1) {
 		pr_perror("Can't connect inet socket back");
-- 
2.6.2



More information about the CRIU mailing list