[CRIU] [PATCH 3/5] net: c/r ipv6 addresses for links

Andrew Vagin avagin at virtuozzo.com
Tue Nov 24 01:22:59 PST 2015


On Mon, Nov 23, 2015 at 01:55:26PM -0700, Tycho Andersen wrote:
> Here's a start at checkpoint/restore of links with ipv6 addresses. Each
> link can have more than one ipv6 address:
> 
> eth0      Link encap:Ethernet  HWaddr 1c:6f:65:d5:56:98
>           inet addr:192.168.0.69  Bcast:192.168.0.255  Mask:255.255.255.0
>           inet6 addr: fe80::1e6f:65ff:fed5:5698/64 Scope:Link
>           inet6 addr: fd5d:e5bb:c5f9::c0c/128 Scope:Global
>           inet6 addr: fd5d:e5bb:c5f9:0:1e6f:65ff:fed5:5698/64 Scope:Global
>           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>           RX packets:63439992 errors:0 dropped:0 overruns:0 frame:0
>           TX packets:44446651 errors:0 dropped:0 overruns:0 carrier:0
>           collisions:0 txqueuelen:1000
>           RX bytes:56986160322 (56.9 GB)  TX bytes:20927902606 (20.9 GB)
> 
> This patch doesn't dump any flags right now (and only restores with NODAD), but
> I don't know enough about ipv6 to say how to c/r flags, or even if anything is
> needed.

We use "ip addr save" to dump addresses and it works for ipv4. Why this
doesn't work for ipv6?

[root at fc22-vm criu]# ip addr save | ip addr showdump
if1:
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
if2:
    inet 192.168.122.141/24 brd 192.168.122.255 scope global dynamic
eth0
       valid_lft 2603sec preferred_lft 2603sec
if1:
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
if2:
    inet6 2001:4860:4801:d::49/128 scope global 
       valid_lft forever preferred_lft forever
if2:
    inet6 fe80::5054:ff:fe49:915/64 scope link 
       valid_lft forever preferred_lft forever

> 
> Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
> ---
>  include/net.h                  |   3 +
>  net.c                          | 170 ++++++++++++++++++++++++++++++++++++++++-
>  protobuf/netdev.proto          |   9 +++
>  sk-inet.c                      |   4 +-
>  test/zdtm/live/static/bridge.c |  11 ++-
>  5 files changed, 190 insertions(+), 7 deletions(-)
> 
> diff --git a/include/net.h b/include/net.h
> index 900b136..bcf0d4a 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -30,4 +30,7 @@ extern int restore_link_parms(NetDeviceEntry *nde, int nlsk);
>  extern int veth_pair_add(char *in, char *out);
>  extern int move_veth_to_bridge(void);
>  
> +#define PB_ALEN_INET	1
> +#define PB_ALEN_INET6	4
> +
>  #endif /* __CR_NET_H__ */
> diff --git a/net.c b/net.c
> index d719471..1b9691b 100644
> --- a/net.c
> +++ b/net.c
> @@ -148,6 +148,103 @@ int write_netdev_img(NetDeviceEntry *nde, struct cr_imgset *fds)
>  	return pb_write_one(img_from_set(fds, CR_FD_NETDEV), nde, PB_NETDEV);
>  }
>  
> +#define IPV6_ADDRLEN 16
> +static int n_v6_addresses = 0;
> +static V6Address *v6_addresses = NULL;
> +
> +static int collect_v6_addresses_cb(struct nlmsghdr *hdr, void *arg)
> +{
> +	struct ifaddrmsg *ifa;
> +	struct rtattr *tb[IFA_MAX + 1];
> +	int len = hdr->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
> +	void *m;
> +	V6Address *cur;
> +	char buf[INET6_ADDRSTRLEN];
> +
> +	ifa = NLMSG_DATA(hdr);
> +	parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len);
> +
> +	if (!inet_ntop(AF_INET6, RTA_DATA(tb[IFA_ADDRESS]), buf, sizeof(buf)))
> +		pr_perror("problem printing address for idx %d\n", ifa->ifa_index);
> +	else
> +		pr_info("found ipv6 address for %d: %s\n", ifa->ifa_index, buf);
> +
> +	if (RTA_PAYLOAD(tb[IFA_ADDRESS]) != IPV6_ADDRLEN) {
> +		pr_err("wrong address size %lu\n", RTA_PAYLOAD(tb[IFA_ADDRESS]));
> +		return -1;
> +	}
> +
> +	m = realloc(v6_addresses, sizeof(*v6_addresses) * (n_v6_addresses + 1));
> +	if (!m)
> +		return -1;
> +
> +	v6_addresses = m;
> +	cur = &v6_addresses[n_v6_addresses++];
> +
> +	v6_address__init(cur);
> +
> +	cur->addr = xmalloc(sizeof(*cur->addr) * PB_ALEN_INET6);
> +	if (!cur->addr)
> +		return -1;
> +
> +	cur->n_addr = PB_ALEN_INET6;
> +	memcpy(cur->addr, RTA_DATA(tb[IFA_ADDRESS]), IPV6_ADDRLEN);
> +	cur->ifindex = ifa->ifa_index;
> +	cur->scope = ifa->ifa_scope;
> +	cur->prefixlen = ifa->ifa_prefixlen;
> +	/* TODO: what flags should we save and restore, if any? */
> +
> +	return 0;
> +}
> +
> +static int collect_v6_addresses(int nlsk)
> +{
> +	int ret;
> +	struct {
> +		struct nlmsghdr nlh;
> +		struct ifaddrmsg r;
> +		char buf[1024];
> +	} req;
> +
> +	memset(&req, 0, sizeof(req));
> +	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
> +	req.nlh.nlmsg_type = RTM_GETADDR;
> +	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
> +	req.nlh.nlmsg_pid = 0;
> +	req.nlh.nlmsg_seq = CR_NLMSG_SEQ;
> +
> +	req.r.ifa_family = AF_INET6;
> +
> +	ret = do_rtnl_req(nlsk, &req, sizeof(req), collect_v6_addresses_cb, NULL, NULL);
> +	close(nlsk);
> +	if (ret < 0)
> +		pr_err("collecting v6 addrs failed (%d)\n", ret);
> +
> +	return ret;
> +}
> +
> +static int attach_v6_addresses(NetDeviceEntry *nde)
> +{
> +	int i;
> +
> +	for (i = 0; i < n_v6_addresses; i++) {
> +		V6Address *cur = &v6_addresses[i];
> +		void *m;
> +
> +		if (cur->ifindex != nde->ifindex)
> +			continue;
> +
> +		m = realloc(nde->v6addrs, sizeof(*nde->v6addrs) * (nde->n_v6addrs + 1));
> +		if (!m)
> +			return -1;
> +
> +		nde->v6addrs = m;
> +		nde->v6addrs[nde->n_v6addrs++] = cur;
> +	}
> +
> +	return 0;
> +}
> +
>  static int dump_one_netdev(int type, struct ifinfomsg *ifi,
>  		struct rtattr **tb, struct cr_imgset *fds,
>  		int (*dump)(NetDeviceEntry *, struct cr_imgset *))
> @@ -175,6 +272,11 @@ static int dump_one_netdev(int type, struct ifinfomsg *ifi,
>  				(int)netdev.address.len, netdev.name);
>  	}
>  
> +	if (attach_v6_addresses(&netdev) < 0) {
> +		pr_err("attaching v6 addresses failed\n");
> +		return -1;
> +	}
> +
>  	netdev.n_conf = ARRAY_SIZE(devconfs);
>  	netdev.conf = xmalloc(sizeof(int) * netdev.n_conf);
>  	if (!netdev.conf)
> @@ -360,6 +462,11 @@ static int dump_links(struct cr_imgset *fds)
>  		goto out;
>  	}
>  
> +	if (collect_v6_addresses(sk) < 0) {
> +		pr_err("getting v6 addresses failed\n");
> +		return -1;
> +	}
> +
>  	memset(&req, 0, sizeof(req));
>  	req.nlh.nlmsg_len = sizeof(req);
>  	req.nlh.nlmsg_type = RTM_GETLINK;
> @@ -439,11 +546,70 @@ int restore_link_parms(NetDeviceEntry *nde, int nlsk)
>  	return do_rtm_link_req(RTM_SETLINK, nde, nlsk, NULL);
>  }
>  
> +static int restore_addr_cb(struct nlmsghdr *hdr, void *arg)
> +{
> +	pr_info("Got response on NEWADDR =)\n");
> +	return 0;
> +}
> +
> +static int restore_v6_addr(int nlsk, V6Address *addr)
> +{
> +	struct {
> +		struct nlmsghdr nlh;
> +		struct ifaddrmsg r;
> +		char buf[1024];
> +	} req;
> +
> +	memset(&req, 0, sizeof(req));
> +	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
> +	req.nlh.nlmsg_type = RTM_NEWADDR;
> +	req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE;
> +	req.nlh.nlmsg_pid = 0;
> +	req.nlh.nlmsg_seq = CR_NLMSG_SEQ;
> +
> +	req.r.ifa_family = AF_INET6;
> +	req.r.ifa_scope = addr->scope;
> +	req.r.ifa_index = addr->ifindex;
> +	req.r.ifa_prefixlen = addr->prefixlen;
> +
> +	/* We disable DAD here because otherwise we'd have to wait for
> +	 * IFA_F_TENTATIVE to clear before we can bind() to the address, which
> +	 * slows down the restore process to an unknown amount of time (based
> +	 * on the whims of the kernel).
> +	 */
> +	req.r.ifa_flags = IFA_F_NODAD;
> +
> +	if (addr->n_addr != PB_ALEN_INET6) {
> +		pr_err("bad addr len %lu\n", addr->n_addr);
> +		return -1;
> +	}
> +
> +	addattr_l(&req.nlh, sizeof(req), IFA_ADDRESS, addr->addr, IPV6_ADDRLEN);
> +
> +	return do_rtnl_req(nlsk, &req, req.nlh.nlmsg_len, restore_addr_cb, NULL, NULL);
> +}
> +
>  static int restore_one_link(NetDeviceEntry *nde, int nlsk,
>  		int (*link_info)(NetDeviceEntry *, struct newlink_req *))
>  {
> +	int i;
> +
>  	pr_info("Restoring netdev %s idx %d\n", nde->name, nde->ifindex);
> -	return do_rtm_link_req(RTM_NEWLINK, nde, nlsk, link_info);
> +
> +	if (do_rtm_link_req(RTM_NEWLINK, nde, nlsk, link_info) < 0)
> +		return -1;
> +
> +	/* now, restore any ipv6 addrs it had */
> +	for (i = 0; i < nde->n_v6addrs; i++) {
> +		pr_info("restoring v6 addr for %s\n", nde->name);
> +
> +		if (restore_v6_addr(nlsk, nde->v6addrs[i]) < 0) {
> +			pr_err("failed to restore v6 addr for %s\n", nde->name);
> +			return -1;
> +		}
> +	}
> +
> +	return 0;
>  }
>  
>  #ifndef VETH_INFO_MAX
> @@ -518,7 +684,7 @@ static int bridge_link_info(NetDeviceEntry *nde, struct newlink_req *req)
>  
>  static int restore_link(NetDeviceEntry *nde, int nlsk)
>  {
> -	pr_info("Restoring link %s type %d\n", nde->name, nde->type);
> +	pr_info("Restoring link %s type %d (idx %d)\n", nde->name, nde->type, nde->ifindex);
>  
>  	switch (nde->type) {
>  	case ND_TYPE__LOOPBACK: /* fallthrough */
> diff --git a/protobuf/netdev.proto b/protobuf/netdev.proto
> index dafa2bd..ea567d5 100644
> --- a/protobuf/netdev.proto
> +++ b/protobuf/netdev.proto
> @@ -19,6 +19,13 @@ enum nd_type {
>  	BRIDGE		= 6;
>  }
>  
> +message v6_address {
> +	repeated uint32		addr		= 1 [(criu).ipadd = true];
> +	required uint32		scope		= 2;
> +	required uint32		ifindex		= 3;
> +	required uint32		prefixlen	= 4;
> +}
> +
>  message net_device_entry {
>  	required nd_type type		= 1;
>  	required uint32  ifindex	= 2;
> @@ -31,6 +38,8 @@ message net_device_entry {
>  	optional bytes address		= 7;
>  
>  	repeated int32 conf		= 8;
> +
> +	repeated v6_address v6addrs	= 9;
>  }
>  
>  message netns_entry {
> diff --git a/sk-inet.c b/sk-inet.c
> index e08b2be..9194dbc 100644
> --- a/sk-inet.c
> +++ b/sk-inet.c
> @@ -19,11 +19,9 @@
>  #include "log.h"
>  #include "util.h"
>  #include "sockets.h"
> +#include "net.h"
>  #include "sk-inet.h"
>  
> -#define PB_ALEN_INET	1
> -#define PB_ALEN_INET6	4
> -
>  static LIST_HEAD(inet_ports);
>  
>  struct inet_port {
> diff --git a/test/zdtm/live/static/bridge.c b/test/zdtm/live/static/bridge.c
> index 0aca514..6e9d5a0 100644
> --- a/test/zdtm/live/static/bridge.c
> +++ b/test/zdtm/live/static/bridge.c
> @@ -23,6 +23,9 @@ int add_bridge(void)
>  	if (system("ip addr add 10.0.55.55/32 dev " BRIDGE_NAME))
>  		return -1;
>  
> +	if (system("ip addr add 1234:4567::1/64 nodad dev " BRIDGE_NAME))
> +		return -1;
> +
>  	if (system("ip link set " BRIDGE_NAME " up"))
>  		return -1;
>  
> @@ -54,8 +57,12 @@ int main(int argc, char **argv)
>  	 *
>  	 * (I got this race with zdtm.py, but not with zdtm.sh; not quite sure
>  	 * what the environment difference is/was.)
> +	 *
> +	 * Also, grep for global addresses only since we get a "free" link
> +	 * local address, which isn't created with nodad but is restored with
> +	 * nodad.
>  	 */
> -	if (system("ip addr list dev " BRIDGE_NAME " | grep inet > bridge.dump.test")) {
> +	if (system("ip addr list dev " BRIDGE_NAME " | grep inet | grep global > bridge.dump.test")) {
>  		pr_perror("can't save net config");
>  		fail("Can't save net config");
>  		goto out;
> @@ -64,7 +71,7 @@ int main(int argc, char **argv)
>  	test_daemon();
>  	test_waitsig();
>  
> -	if (system("ip addr list dev " BRIDGE_NAME " | grep inet > bridge.rst.test")) {
> +	if (system("ip addr list dev " BRIDGE_NAME " | grep inet | grep global > bridge.rst.test")) {
>  		fail("Can't get net config");
>  		goto out;
>  	}
> -- 
> 2.6.2
> 
> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu


More information about the CRIU mailing list