[Devel] [PATCH 1/3] C/R: Support for IPv6 addresses on network devices (v2)
Dan Smith
danms at us.ibm.com
Tue Apr 6 07:13:01 PDT 2010
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
Signed-off-by: Dan Smith <danms at us.ibm.com>
---
include/linux/checkpoint.h | 2 +-
include/linux/checkpoint_hdr.h | 8 ++
net/checkpoint_dev.c | 272 ++++++++++++++++++++++++++++++++--------
3 files changed, 228 insertions(+), 54 deletions(-)
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index 96693e2..2caa38c 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -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);
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index 36386ad..90e4934 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -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)));
diff --git a/net/checkpoint_dev.c b/net/checkpoint_dev.c
index 5a4a95b..776931b 100644
--- a/net/checkpoint_dev.c
+++ b/net/checkpoint_dev.c
@@ -18,8 +18,11 @@
#include <linux/checkpoint_hdr.h>
#include <linux/deferqueue.h>
+#include <net/if_inet6.h>
+#include <net/ipv6.h>
#include <net/net_namespace.h>
#include <net/sch_generic.h>
+#include <net/addrconf.h>
struct veth_newlink {
char *peer;
@@ -47,6 +50,24 @@ static int __kern_devinet_ioctl(struct net *net, unsigned int cmd, void *arg)
return ret;
}
+#ifdef CONFIG_IPV6
+static int __kern_addrconf(struct net *net, unsigned int cmd, void *arg)
+{
+ mm_segment_t fs;
+ int ret;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ if (cmd == SIOCSIFADDR)
+ ret = addrconf_add_ifaddr(net, arg);
+ else
+ ret = -EINVAL;
+ set_fs(fs);
+
+ return ret;
+}
+#endif
+
static int __kern_dev_ioctl(struct net *net, unsigned int cmd, void *arg)
{
mm_segment_t fs;
@@ -151,11 +172,109 @@ int ckpt_netdev_hwaddr(struct net_device *dev, struct ckpt_hdr_netdev *h)
return 0;
}
-int ckpt_netdev_inet_addrs(struct in_device *indev,
+static int ckpt_netdev_inet4_addrs(struct in_device *indev,
+ int index, int max,
+ struct ckpt_netdev_addr *abuf)
+{
+ struct in_ifaddr *addr = indev->ifa_list;
+
+ while (addr) {
+ abuf[index].type = CKPT_NETDEV_ADDR_IPV4;
+ abuf[index].inet4_local = htonl(addr->ifa_local);
+ abuf[index].inet4_address = htonl(addr->ifa_address);
+ abuf[index].inet4_mask = htonl(addr->ifa_mask);
+ abuf[index].inet4_broadcast = htonl(addr->ifa_broadcast);
+
+ addr = addr->ifa_next;
+ if (++index >= max)
+ return -E2BIG;
+ }
+
+ return index;
+}
+
+#ifdef CONFIG_IPV6
+
+#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) {
+ 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;
+ }
+ unlock:
read_unlock(&dev_base_lock);
out:
if (addrs < 0) {
@@ -210,7 +329,7 @@ struct ckpt_hdr_netdev *ckpt_netdev_base(struct ckpt_ctx *ctx,
goto out;
*addrs = NULL;
- ret = h->inet_addrs = ckpt_netdev_inet_addrs(dev->ip_ptr, addrs);
+ ret = h->inet_addrs = ckpt_netdev_inet_addrs(dev, addrs);
if (ret < 0)
goto out;
@@ -291,6 +410,85 @@ int checkpoint_netns(struct ckpt_ctx *ctx, void *ptr)
return ret;
}
+static int restore_inet4_addr(struct ckpt_ctx *ctx,
+ struct net_device *dev,
+ struct net *net,
+ struct ckpt_netdev_addr *addr)
+{
+ struct ifreq req;
+ struct sockaddr_in *inaddr;
+ int ret;
+
+ ckpt_debug("restoring %s: %x/%x/%x\n",
+ dev->name,
+ addr->inet4_address,
+ addr->inet4_mask,
+ addr->inet4_broadcast);
+
+ memcpy(req.ifr_name, dev->name, IFNAMSIZ);
+
+ inaddr = (struct sockaddr_in *)&req.ifr_addr;
+ inaddr->sin_addr.s_addr = ntohl(addr->inet4_address);
+ inaddr->sin_family = AF_INET;
+ ret = __kern_devinet_ioctl(net, SIOCSIFADDR, &req);
+ if (ret < 0) {
+ ckpt_err(ctx, ret, "Failed to set address\n");
+ return ret;
+ }
+
+ inaddr = (struct sockaddr_in *)&req.ifr_addr;
+ inaddr->sin_addr.s_addr = ntohl(addr->inet4_mask);
+ inaddr->sin_family = AF_INET;
+ ret = __kern_devinet_ioctl(net, SIOCSIFNETMASK, &req);
+ if (ret < 0) {
+ ckpt_err(ctx, ret, "Failed to set netmask\n");
+ return ret;
+ }
+
+ inaddr = (struct sockaddr_in *)&req.ifr_addr;
+ inaddr->sin_addr.s_addr = ntohl(addr->inet4_broadcast);
+ inaddr->sin_family = AF_INET;
+ ret = __kern_devinet_ioctl(net, SIOCSIFBRDADDR, &req);
+ if (ret < 0) {
+ ckpt_err(ctx, ret, "Failed to set broadcast\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_IPV6
+static int restore_inet6_addr(struct ckpt_ctx *ctx,
+ struct net_device *dev,
+ struct net *net,
+ struct ckpt_netdev_addr *addr)
+{
+ struct in6_ifreq req;
+ int ret;
+
+ req.ifr6_ifindex = dev->ifindex;
+ NTOH_IPV6(&req.ifr6_addr, &addr->inet6_addr);
+ req.ifr6_prefixlen = addr->inet6_prefix_len;
+
+ ckpt_debug("Restored %s: %pI6\n", dev->name, &req.ifr6_addr);
+
+ ret = __kern_addrconf(net, SIOCSIFADDR, &req);
+ if (ret < 0)
+ ckpt_err(ctx, ret, "Failed to set address");
+
+ return ret;
+}
+#else
+static int restore_inet6_addr(struct ckpt_ctx *ctx,
+ struct net_device *dev,
+ struct net *net,
+ struct ckpt_netdev_addr *addr)
+{
+ ckpt_err(ctx, -ENOSYS, "IPv6 not supported");
+ return -ENOSYS;
+}
+#endif
+
static int restore_in_addrs(struct ckpt_ctx *ctx,
__u32 naddrs,
struct net *net,
@@ -307,49 +505,17 @@ static int restore_in_addrs(struct ckpt_ctx *ctx,
for (i = 0; i < naddrs; i++) {
struct ckpt_netdev_addr *addr = &addrs[i];
- struct ifreq req;
- struct sockaddr_in *inaddr;
- if (addr->type != CKPT_NETDEV_ADDR_IPV4) {
+ if (addr->type == CKPT_NETDEV_ADDR_IPV4)
+ ret = restore_inet4_addr(ctx, dev, net, addr);
+ else if (addr->type == CKPT_NETDEV_ADDR_IPV6)
+ ret = restore_inet6_addr(ctx, dev, net, addr);
+ else {
ret = -EINVAL;
ckpt_err(ctx, ret, "Unsupported netdev addr type %i\n",
addr->type);
break;
}
-
- ckpt_debug("restoring %s: %x/%x/%x\n", dev->name,
- addr->inet4_address,
- addr->inet4_mask,
- addr->inet4_broadcast);
-
- memcpy(req.ifr_name, dev->name, IFNAMSIZ);
-
- inaddr = (struct sockaddr_in *)&req.ifr_addr;
- inaddr->sin_addr.s_addr = ntohl(addr->inet4_address);
- inaddr->sin_family = AF_INET;
- ret = __kern_devinet_ioctl(net, SIOCSIFADDR, &req);
- if (ret < 0) {
- ckpt_err(ctx, ret, "Failed to set address\n");
- break;
- }
-
- inaddr = (struct sockaddr_in *)&req.ifr_addr;
- inaddr->sin_addr.s_addr = ntohl(addr->inet4_mask);
- inaddr->sin_family = AF_INET;
- ret = __kern_devinet_ioctl(net, SIOCSIFNETMASK, &req);
- if (ret < 0) {
- ckpt_err(ctx, ret, "Failed to set netmask\n");
- break;
- }
-
- inaddr = (struct sockaddr_in *)&req.ifr_addr;
- inaddr->sin_addr.s_addr = ntohl(addr->inet4_broadcast);
- inaddr->sin_family = AF_INET;
- ret = __kern_devinet_ioctl(net, SIOCSIFBRDADDR, &req);
- if (ret < 0) {
- ckpt_err(ctx, ret, "Failed to set broadcast\n");
- break;
- }
}
out:
--
1.6.2.5
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list