Signed-off-by: Dan Smith <danms@xxxxxxxxxx> --- include/linux/checkpoint.h | 2 +- include/linux/checkpoint_hdr.h | 8 ++ net/checkpoint_dev.c | 252 +++++++++++++++++++++++++++++++--------- 3 files changed, 208 insertions(+), 54 deletions(-) diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h index efbc049..a8131bd 100644 --- a/include/linux/checkpoint.h +++ b/include/linux/checkpoint.h @@ -129,7 +129,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 01553b4..913d76d 100644 --- a/include/linux/checkpoint_hdr.h +++ b/include/linux/checkpoint_hdr.h @@ -802,6 +802,7 @@ struct ckpt_hdr_netdev { enum ckpt_netdev_addr_types { CKPT_NETDEV_ADDR_IPV4, + CKPT_NETDEV_ADDR_IPV6, }; struct ckpt_netdev_addr { @@ -813,6 +814,13 @@ struct ckpt_netdev_addr { __be32 inet4_mask; __be32 inet4_broadcast; }; + struct { + __be32 inet6_addr[4]; + __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 2bb3d4d..173c2ea 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; @@ -149,11 +170,81 @@ 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 < 4; i++) \ + dst[i] = op(src[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 = indev->addr_list; + + while (addr) { + abuf[index].type = CKPT_NETDEV_ADDR_IPV6; + + HTON_IPV6(abuf[index].inet6_addr, addr->addr.in6_u.u6_addr32); + + ckpt_debug("Checkpointed inet6: %x:%x:%x:%x\n", + abuf[index].inet6_addr[0], + abuf[index].inet6_addr[1], + abuf[index].inet6_addr[2], + abuf[index].inet6_addr[3]); + + 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; + + addr = addr->if_next; + if (++index >= max) + return -E2BIG; + } + + 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; @@ -167,21 +258,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) { @@ -208,7 +299,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; @@ -278,6 +369,93 @@ 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; + + ckpt_debug("restoring %s: %x:%x:%x:%x/%i\n", + dev->name, + addr->inet6_addr[0], + addr->inet6_addr[1], + addr->inet6_addr[2], + addr->inet6_addr[3], + addr->inet6_prefix_len); + + req.ifr6_ifindex = dev->ifindex; + NTOH_IPV6(req.ifr6_addr.in6_u.u6_addr32, &addr->inet6_addr); + req.ifr6_prefixlen = addr->inet6_prefix_len; + + ret = __kern_addrconf(net, SIOCSIFADDR, &req); + if (ret == -EEXIST) + ret = 0; + else 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, @@ -294,49 +472,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@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/containers