Skip to contentSkip to conversation

c/r: IPv6 support

PATCH In review Dan Smith 5 participants 11 messages Apr 6, 2010 – Apr 24, 2010 7 messages not in the archive Networking

lore
Share & tools
lore

Dan Smith

author
#0 lore raw

This version of the patch set incorporates feedback from Brian, as well as a new patch at the end to check for IPv4 multicast addresses when doing the checkpoint.

A message that is not in the archive (sent off-list or not yet received): 1270563183-9351-1-git-send-email-danms-r/[email protected]

author
#2 lore raw

PATCH 1/3 C/R: Support for IPv6 addresses on network devices (v2)

Changes in v2:
- Store an in6_addr structure in the checkpoint stream
- Fix network byte order conversion
- Don't checkpoint and restore non-global scope addresses
- Fail checkpoint if we find global scope multicast or anycast addresses

---
 include/linux/checkpoint.h     |    2 +-
 include/linux/checkpoint_hdr.h |    8 ++
 net/checkpoint_dev.c           |  272 ++++++++++++++++++++++++++++++++--------
 3 files changed, 228 insertions(+), 54 deletions(-)
include/linux/checkpoint.h +1 -1
index 96693e2..2caa38c 100644
@@ -132,7 +132,7 @@ extern void *restore_netdev(struct ckpt_ctx *ctx);
 
 extern int ckpt_netdev_in_init_netns(struct ckpt_ctx *ctx,
 				     struct net_device *dev);
-extern int ckpt_netdev_inet_addrs(struct in_device *indev,
+extern int ckpt_netdev_inet_addrs(struct net_device *dev,
 				  struct ckpt_netdev_addr *list[]);
 extern int ckpt_netdev_hwaddr(struct net_device *dev,
 			      struct ckpt_hdr_netdev *h);
include/linux/checkpoint_hdr.h +8 -0
index 36386ad..90e4934 100644
@@ -804,6 +804,7 @@ struct ckpt_hdr_netdev {
 
 enum ckpt_netdev_addr_types {
 	CKPT_NETDEV_ADDR_IPV4,
+	CKPT_NETDEV_ADDR_IPV6,
 };
 
 struct ckpt_netdev_addr {
@@ -815,6 +816,13 @@ struct ckpt_netdev_addr {
 			__be32 inet4_mask;
 			__be32 inet4_broadcast;
 		};
+		struct {
+			struct in6_addr inet6_addr;
+			__u32  inet6_prefix_len;
+			__u32  inet6_valid_lft;
+			__u32  inet6_prefered_lft;
+			__u16  inet6_scope;
+		};
 	} __attribute__((aligned(8)));
 } __attribute__((aligned(8)));
 
net/checkpoint_dev.c +219 -53

327 lines not on this page · raw message

Signature · 1 line
1.6.2.5

A message that is not in the archive (sent off-list or not yet received): 1270563183-9351-2-git-send-email-danms-r/[email protected]

#4 lore raw

Re: [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)

net/checkpoint_dev.c int ckpt_netdev_hwaddr(struct net_device *dev, struct ckpt_hdr_netdev *h) · line 220

Quoted 11 lines · Dan Smith
net/checkpoint_dev.c +11 -0
+static int ckpt_netdev_inet6_addrs(struct inet6_dev *indev,
+				   int index, int max,
+				   struct ckpt_netdev_addr *abuf)
+{
+	struct inet6_ifaddr *addr;
+	struct ifmcaddr6 *mcaddr;
+	struct ifacaddr6 *acaddr;
+
+	for (addr = indev->addr_list; addr; addr = addr->if_next) {
+		if (ipv6_addr_scope(&addr->addr))
+			continue; /* Ignore non-global scope addresses */

This will prevent the loopback address (::1) from getting checkpointed since it's host-local. The ipv6 module will add it anyways so it's probably not a big deal.

-Brian

A message that is not in the archive (sent off-list or not yet received): [email protected]

author
#6 lore raw

Re: [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)

BH> This will prevent the loopback address (::1) from getting BH> checkpointed since it's host-local. The ipv6 module will add it BH> anyways so it's probably not a big deal.

Yep, and it does.

Thanks!

Signature · 3 lines
Dan Smith
IBM Linux Technology Center
email: danms-r/[email protected]

A message that is not in the archive (sent off-list or not yet received): 87eiisblkn.fsf-FLMGYpZoEPULwtHQx/[email protected]

#8 lore raw

Re: [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)

Quoted 5 lines · Dan Smith

BH> This will prevent the loopback address (::1) from getting BH> checkpointed since it's host-local. The ipv6 module will add it BH> anyways so it's probably not a big deal.

Yep, and it does.

Probably worth a comment inline ?

Oren.

#9 lore raw

Re: [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)

In reply to a message not in the archive

Quoting Dan Smith (danms-r/[email protected]):

Two related problems here:

Quoted 1 line

+#ifdef CONFIG_IPV6

1. this will only trigger if CONFIG_IPV6=y, not if it =m. So you might have meant

	#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)

or something. But I'm not sure that if it's a module but not yet loaded that that suffices?

Quoted 106 lines
net/checkpoint_dev.c +81 -14
+
+#define __BYTE_ORDER_COPY(op, dst, src)					\
+	do {								\
+		int i;							\
+		for (i = 0; i < 16; i++) {				\
+			(dst)->in6_u.u6_addr8[i] =			\
+				(src)->in6_u.u6_addr8[16-i];		\
+		}							\
+	} while (0);
+
+#define HTON_IPV6(dst, src) __BYTE_ORDER_COPY(htonl, dst, src)
+#define NTOH_IPV6(dst, src) __BYTE_ORDER_COPY(ntohl, dst, src)
+
+static int ckpt_netdev_inet6_addrs(struct inet6_dev *indev,
+				   int index, int max,
+				   struct ckpt_netdev_addr *abuf)
+{
+	struct inet6_ifaddr *addr;
+	struct ifmcaddr6 *mcaddr;
+	struct ifacaddr6 *acaddr;
+
+	for (addr = indev->addr_list; addr; addr = addr->if_next) {
+		if (ipv6_addr_scope(&addr->addr))
+			continue; /* Ignore non-global scope addresses */
+
+		abuf[index].type = CKPT_NETDEV_ADDR_IPV6;
+
+		HTON_IPV6(&abuf[index].inet6_addr, &addr->addr);
+
+		ckpt_debug("Checkpointed inet6: %pI6\n", &addr->addr);
+
+		abuf[index].inet6_prefix_len = addr->prefix_len;
+		abuf[index].inet6_valid_lft = addr->valid_lft;
+		abuf[index].inet6_prefered_lft = addr->prefered_lft;
+		abuf[index].inet6_scope = addr->scope;
+
+		if (++index >= max)
+			return -E2BIG;
+	}
+
+	for (mcaddr = indev->mc_list; mcaddr; mcaddr = mcaddr->next) {
+		if (ipv6_addr_scope(&mcaddr->mca_addr))
+			continue; /* Ignore non-global scope addresses */
+
+		/* TODO */
+
+		/* Multicast addresses are not supported, so do not
+		 * allow checkpoint to continue if one is assigned
+		 */
+		ckpt_debug("ipv6 multicast addresses are not supported\n");
+		return -EINVAL;
+	}
+
+	for (acaddr = indev->ac_list; acaddr; acaddr = acaddr->aca_next) {
+		if (ipv6_addr_scope(&acaddr->aca_addr))
+			continue; /* Ignore non-global scope addresses */
+
+		/* TODO */
+
+		/* Anycast addresses are not supported, so do not
+		 * allow checkpoint to continue if one is assigned
+		 */
+		ckpt_debug("ipv6 anycast addresses are not supported\n");
+		return -EINVAL;
+	}
+
+	return index;
+}
+#else
+static int ckpt_netdev_inet6_addrs(struct inet6_dev *indev,
+				   int index, int max,
+				   struct ckpt_netdev_addr *abuf)
+{
+	return -ENOSYS;
+}
+#endif
+
+int ckpt_netdev_inet_addrs(struct net_device *dev,
 			   struct ckpt_netdev_addr *_abuf[])
 {
 	struct ckpt_netdev_addr *abuf = NULL;
-	struct in_ifaddr *addr = indev->ifa_list;
 	int addrs = 0;
 	int max = 32;
@@ -169,21 +288,21 @@ int ckpt_netdev_inet_addrs(struct in_device *indev,
 
 	read_lock(&dev_base_lock);
 
-	while (addr) {
-		abuf[addrs].type = CKPT_NETDEV_ADDR_IPV4; /* Only IPv4 now */
-		abuf[addrs].inet4_local = htonl(addr->ifa_local);
-		abuf[addrs].inet4_address = htonl(addr->ifa_address);
-		abuf[addrs].inet4_mask = htonl(addr->ifa_mask);
-		abuf[addrs].inet4_broadcast = htonl(addr->ifa_broadcast);
+	addrs = 0;
 
-		addr = addr->ifa_next;
-		if (++addrs >= max) {
-			read_unlock(&dev_base_lock);
-			max *= 2;
-			goto retry;
-		}
-	}
+	addrs = ckpt_netdev_inet4_addrs(dev->ip_ptr, addrs, max, abuf);
+	if (addrs == -E2BIG) {

net/checkpoint_dev.c int ckpt_netdev_inet_addrs(struct in_device *indev, · line 304

Quoted 10 lines
net/checkpoint_dev.c +9 -0
+		read_unlock(&dev_base_lock);
+		goto retry;
+	} else if (addrs < 0)
+		goto unlock;
 
+	addrs = ckpt_netdev_inet6_addrs(dev->ip6_ptr, addrs, max, abuf);
+	if (addrs == -E2BIG) {
+		read_unlock(&dev_base_lock);
+		goto retry;
+	}

This is the second problem. If CONFIG_IPV6=n or CONFIG_IPV6=m, then ckpt_netdev_inet6_addrs() will return -ENOSYS, and you'll fail here. So in those cases there is now no way to do a checkpoint with CHECKPOINT_NETNS (or without CHECKPOINT_NONETNS :).

The fix of course can't be quite as simple as ignoring return value of -ENOSYS since you'll have lost the passed-in addrs from ckpt_netdev_inet4_addrs.

net/checkpoint_dev.c int ckpt_netdev_inet_addrs(struct in_device *indev, · line 308

Quoted 4 lines
net/checkpoint_dev.c +1 -0
+ unlock:
 	read_unlock(&dev_base_lock);
  out:
 	if (addrs < 0) {

-serge

A message that is not in the archive (sent off-list or not yet received): 20100407041858.GA12287-r/[email protected]

author
#11 lore raw

Re: [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)

SH> 1. this will only trigger if CONFIG_IPV6=y, not if it =m. So you SH> might have meant
SH> #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)

Ah, okay, right.

Quoted 5 lines

net/checkpoint_dev.c int ckpt_netdev_inet_addrs(struct in_device *indev, · line 304

Quoted 5 lines
net/checkpoint_dev.c +5 -0
+	addrs = ckpt_netdev_inet6_addrs(dev->ip6_ptr, addrs, max, abuf);
+	if (addrs == -E2BIG) {
+		read_unlock(&dev_base_lock);
+		goto retry;
+	}

SH> This is the second problem. If CONFIG_IPV6=n or CONFIG_IPV6=m, then SH> ckpt_netdev_inet6_addrs() will return -ENOSYS, and you'll fail here. SH> So in those cases there is now no way to do a checkpoint with SH> CHECKPOINT_NETNS (or without CHECKPOINT_NONETNS :).

SH> The fix of course can't be quite as simple as ignoring return SH> value of -ENOSYS since you'll have lost the passed-in addrs from SH> ckpt_netdev_inet4_addrs.

If I change it to compile in with CONFIG_IPV6_MODULE as you stated above, then it won't return -ENOSYS, right? The code will still be there, and will not traverse the ipv6 address list (if I make it check for an empty dev->ip6_ptr) until the module is loaded and the devices gain some IPv6 information.

I'll test this and resend the patch appropriately.

Thanks!

Signature · 3 lines
Dan Smith
IBM Linux Technology Center
email: danms-r/[email protected]

A message that is not in the archive (sent off-list or not yet received): 87aatfbbtk.fsf-FLMGYpZoEPULwtHQx/[email protected]

#13 lore raw

Re: [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)

Quoted 17 lines · Dan Smith

SH> 1. this will only trigger if CONFIG_IPV6=y, not if it =m. So you SH> might have meant
SH> #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)

Ah, okay, right.

Quoted 5 lines

net/checkpoint_dev.c int ckpt_netdev_inet_addrs(struct in_device *indev, · line 304

Quoted 5 lines
net/checkpoint_dev.c +5 -0
+	addrs = ckpt_netdev_inet6_addrs(dev->ip6_ptr, addrs, max, abuf);
+	if (addrs == -E2BIG) {
+		read_unlock(&dev_base_lock);
+		goto retry;
+	}

SH> This is the second problem. If CONFIG_IPV6=n or CONFIG_IPV6=m, then SH> ckpt_netdev_inet6_addrs() will return -ENOSYS, and you'll fail here. SH> So in those cases there is now no way to do a checkpoint with SH> CHECKPOINT_NETNS (or without CHECKPOINT_NONETNS :).

Quoted 6 lines

SH> The fix of course can't be quite as simple as ignoring return SH> value of -ENOSYS since you'll have lost the passed-in addrs from SH> ckpt_netdev_inet4_addrs.

If I change it to compile in with CONFIG_IPV6_MODULE as you stated above, then it won't return -ENOSYS, right? The code will still be

But it'll return -ENOSYS if CONFIG_IPV6=n.

Quoted 16 lines

there, and will not traverse the ipv6 address list (if I make it check for an empty dev->ip6_ptr) until the module is loaded and the devices gain some IPv6 information.

I'll test this and resend the patch appropriately.

Thanks!

Signature · 7 lines
Dan Smith
IBM Linux Technology Center
email: danms-r/[email protected]
_______________________________________________
Containers mailing list
Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
https://lists.linux-foundation.org/mailman/listinfo/containers
author
#14 lore raw

PATCH 2/3 C/R: Fix storing IPv6 addresses and handle the "ipv6only" socket flag

In reply to a message not in the archive

The first item is a result of sockaddr_in6 being larger than the base sockaddr structure, thus not being long enough to reserve enough space in the checkpoint header.

The second comes into play when things (like sshd) bind to INADDR6_ANY, set the "ipv6only" socket flag and then bind an IPv4 socket to the same port.

---
 include/linux/checkpoint_hdr.h |   13 +++++++++++--
 net/ipv4/checkpoint.c          |   34 ++++++++++++++++++++--------------
 net/ipv6/af_inet6.c            |    2 ++
 3 files changed, 33 insertions(+), 16 deletions(-)
include/linux/checkpoint_hdr.h +11 -2
index 90e4934..e771758 100644
@@ -758,12 +758,21 @@ struct ckpt_hdr_socket_inet {
 		struct in6_addr saddr;
 		struct in6_addr rcv_saddr;
 		struct in6_addr daddr;
+		__u8 ipv6only;
 	} inet6 __attribute__ ((aligned(8)));
 
 	__u32 laddr_len;
 	__u32 raddr_len;
-	struct sockaddr_in laddr;
-	struct sockaddr_in raddr;
+	union {
+		struct sockaddr laddr;
+		struct sockaddr_in laddr4;
+		struct sockaddr_in6 laddr6;
+	};
+	union {
+		struct sockaddr raddr;
+		struct sockaddr_in raddr4;
+		struct sockaddr_in6 raddr6;
+	};
 } __attribute__((aligned(8)));
 
 struct ckpt_hdr_file_socket {
net/ipv4/checkpoint.c +20 -14
index b4024e7..57b185d 100644
@@ -190,12 +190,14 @@ static int sock_inet_restore_connection(struct sock *sk,
 	struct inet_sock *inet = inet_sk(sk);
 	int tcp_gso = sk->sk_family == AF_INET ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
 
-	inet->inet_daddr = hh->raddr.sin_addr.s_addr;
-	inet->inet_saddr = hh->laddr.sin_addr.s_addr;
-	inet->inet_rcv_saddr = inet->inet_saddr;
+	if (sk->sk_family == AF_INET) {
+		inet->inet_daddr = hh->raddr4.sin_addr.s_addr;
+		inet->inet_saddr = hh->laddr4.sin_addr.s_addr;
+		inet->inet_rcv_saddr = inet->inet_saddr;
 
-	inet->inet_dport = hh->raddr.sin_port;
-	inet->inet_sport = hh->laddr.sin_port;
+		inet->inet_dport = hh->raddr4.sin_port;
+		inet->inet_sport = hh->laddr4.sin_port;
+	}
 
 	if (sk->sk_protocol == IPPROTO_TCP)
 		sk->sk_gso_type = tcp_gso;
@@ -266,6 +268,7 @@ static int sock_inet_cptrst(struct ckpt_ctx *ctx,
 			ipv6_addr_copy(&inet6->rcv_saddr, &hh->inet6.rcv_saddr);
 			ipv6_addr_copy(&inet6->daddr, &hh->inet6.daddr);
 		}
+		CKPT_COPY(op, hh->inet6.ipv6only, inet6->ipv6only);
 	}
 
 	return ret;
@@ -281,8 +284,8 @@ int inet_checkpoint(struct ckpt_ctx *ctx, struct socket *sock)
 		return -EINVAL;
 
 	ret = ckpt_sock_getnames(ctx, sock,
-				(struct sockaddr *)&in->laddr, &in->laddr_len,
-				(struct sockaddr *)&in->raddr, &in->raddr_len);
+				&in->laddr, &in->laddr_len,
+				&in->raddr, &in->raddr_len);
 	if (ret)
 		goto out;
 
@@ -387,19 +390,19 @@ static int inet_precheck(struct socket *sock, struct ckpt_hdr_socket_inet *in)
 	__u8 nonagle_mask = TCP_NAGLE_OFF | TCP_NAGLE_CORK | TCP_NAGLE_PUSH;
 	__u8 ecn_mask = TCP_ECN_OK | TCP_ECN_QUEUE_CWR | TCP_ECN_DEMAND_CWR;
 
-	if ((htons(in->laddr.sin_port) < PROT_SOCK) &&
+	if ((htons(in->laddr4.sin_port) < PROT_SOCK) &&
 	    !capable(CAP_NET_BIND_SERVICE)) {
 		ckpt_debug("unable to bind to port %hu\n",
-			   htons(in->laddr.sin_port));
+			   htons(in->laddr4.sin_port));
 		return -EINVAL;
 	}
 
-	if (in->laddr_len > sizeof(struct sockaddr_in)) {
+	if (in->laddr_len > sizeof(in->laddr6)) {
 		ckpt_debug("laddr_len is too big\n");
 		return -EINVAL;
 	}
 
-	if (in->raddr_len > sizeof(struct sockaddr_in)) {
+	if (in->raddr_len > sizeof(in->laddr6)) {
 		ckpt_debug("raddr_len is too big\n");
 		return -EINVAL;
 	}
@@ -498,13 +501,16 @@ int inet_restore(struct ckpt_ctx *ctx,
 	    ((h->sock.state == TCP_CLOSE) && (in->laddr_len > 0))) {
 		sock->sk->sk_reuse = 2;
 		inet_sk(sock->sk)->freebind = 1;
-		ret = sock->ops->bind(sock,
-				      (struct sockaddr *)&in->laddr,
-				      in->laddr_len);
+		ret = sock->ops->bind(sock, &in->laddr, in->laddr_len);
 		ckpt_debug("inet bind: %i\n", ret);
 		if (ret < 0)
 			goto out;
 
+		if (in->inet6.ipv6only) {
+			struct ipv6_pinfo *np = inet6_sk(sock->sk);
+			np->ipv6only = 1;
+		}
+
 		if (h->sock.state == TCP_LISTEN) {
 			ret = sock->ops->listen(sock, h->sock.backlog);
 			ckpt_debug("inet listen: %i\n", ret);
net/ipv6/af_inet6.c +2 -0
index 12e69d3..9acb55a 100644
@@ -523,6 +523,8 @@ const struct proto_ops inet6_stream_ops = {
 	.mmap		   = sock_no_mmap,
 	.sendpage	   = tcp_sendpage,
 	.splice_read	   = tcp_splice_read,
+	.checkpoint	   = inet_checkpoint,
+	.restore	   = inet_restore,
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_sock_common_setsockopt,
 	.compat_getsockopt = compat_sock_common_getsockopt,
Signature · 1 line
1.6.2.5

A message that is not in the archive (sent off-list or not yet received): 1270563183-9351-3-git-send-email-danms-r/[email protected]

#16 lore raw

Re: [PATCH 2/3] C/R: Fix storing IPv6 addresses and handle the "ipv6only" socket flag

Quoted 9 lines · Dan Smith

The first item is a result of sockaddr_in6 being larger than the base sockaddr structure, thus not being long enough to reserve enough space in the checkpoint header.

The second comes into play when things (like sshd) bind to INADDR6_ANY, set the "ipv6only" socket flag and then bind an IPv4 socket to the same port.

since ipv6 can be a module, this requires:

From 7132840517ecc258a2d972dae7011a6666e91c56 Mon Sep 17 00:00:00 2001
From: Serge E. Hallyn <serue-r/[email protected]>
Date: Tue, 6 Apr 2010 12:35:54 -0500
Subject: [PATCH 1/1] export needed symbols needed by ipv6

---
 net/ipv4/checkpoint.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
net/ipv4/checkpoint.c +2 -0
index 57b185d..7479ffd 100644
@@ -299,6 +299,7 @@ int inet_checkpoint(struct ckpt_ctx *ctx, struct socket *sock)
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(inet_checkpoint);
 
 int inet_collect(struct ckpt_ctx *ctx, struct socket *sock)
 {
@@ -553,3 +554,4 @@ int inet_restore(struct ckpt_ctx *ctx,
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(inet_restore);
Signature · 1 line
1.6.0.6
author
#17 lore raw

PATCH 3/3 Fail checkpoint if IPv4 multicast addresses are configured

In reply to a message not in the archive

This causes checkpoint to fail if an interface actually has a multicast membership.

---
 net/checkpoint_dev.c |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)
net/checkpoint_dev.c +15 -0
index 776931b..11c6544 100644
@@ -17,6 +17,7 @@
 #include <linux/checkpoint.h>
 #include <linux/checkpoint_hdr.h>
 #include <linux/deferqueue.h>
+#include <linux/igmp.h>
 
 #include <net/if_inet6.h>
 #include <net/ipv6.h>
@@ -177,6 +178,7 @@ static int ckpt_netdev_inet4_addrs(struct in_device *indev,
 				   struct ckpt_netdev_addr *abuf)
 {
 	struct in_ifaddr *addr = indev->ifa_list;
+	struct ip_mc_list *mcaddr;
 
 	while (addr) {
 		abuf[index].type = CKPT_NETDEV_ADDR_IPV4;
@@ -190,6 +192,19 @@ static int ckpt_netdev_inet4_addrs(struct in_device *indev,
 			return -E2BIG;
 	}
 
+	for (mcaddr = indev->mc_list; mcaddr; mcaddr = mcaddr->next) {
+		if ((mcaddr->multiaddr & IGMP_LOCAL_GROUP_MASK) == IGMP_LOCAL_GROUP)
+			continue;
+
+		/* TODO */
+
+		/* Multicast addresses are not supported, so do not
+		 * allow checkpoint to continue if one is assigned
+		 */
+		ckpt_debug("ipv4 multicast addresses are not supported\n");
+		return -EINVAL;
+	}
+
 	return index;
 }
 
Signature · 1 line
1.6.2.5