On Mon, May 22, 2023 at 01:42:22AM +0200, Stefano Brivio wrote: > Similarly to what we've just done with routes, support NL_DUP for > addresses (currently not exposed): nl_addr() can optionally copy > mulitple addresses to the target namespace, by fixing up data from > the dump with appropriate flags and interface index, and repeating > it back to the kernel on the socket opened in the target namespace. > > Link-local addresses are not copied: the family is set to AF_UNSPEC, > which means the kernel will ignore them. Same for addresses from a > mismatching address (pre-4.19 kernels without support for > NETLINK_GET_STRICT_CHK). > > Ignore IFA_LABEL attributes by changing their type to IFA_UNSPEC, > because in general they will report mismatching names, and we don't > really need to use labels as we already know the interface index. > > Signed-off-by: Stefano Brivio Reviewed-by: David Gibson > --- > conf.c | 8 ++++--- > netlink.c | 62 +++++++++++++++++++++++++++++++++++++++++-------------- > netlink.h | 4 ++-- > pasta.c | 8 +++---- > 4 files changed, 58 insertions(+), 24 deletions(-) > > diff --git a/conf.c b/conf.c > index c07b697..1ffd05c 100644 > --- a/conf.c > +++ b/conf.c > @@ -645,8 +645,10 @@ static unsigned int conf_ip4(unsigned int ifi, > if (IN4_IS_ADDR_UNSPECIFIED(&ip4->gw)) > nl_route(NL_GET, ifi, 0, AF_INET, &ip4->gw); > > - if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) > - nl_addr(0, ifi, AF_INET, &ip4->addr, &ip4->prefix_len, NULL); > + if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) { > + nl_addr(NL_GET, ifi, 0, AF_INET, > + &ip4->addr, &ip4->prefix_len, NULL); > + } > > if (!ip4->prefix_len) { > in_addr_t addr = ntohl(ip4->addr.s_addr); > @@ -696,7 +698,7 @@ static unsigned int conf_ip6(unsigned int ifi, > if (IN6_IS_ADDR_UNSPECIFIED(&ip6->gw)) > nl_route(NL_GET, ifi, 0, AF_INET6, &ip6->gw); > > - nl_addr(0, ifi, AF_INET6, > + nl_addr(NL_GET, ifi, 0, AF_INET6, > IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) ? &ip6->addr : NULL, > &prefix_len, &ip6->addr_ll); > > diff --git a/netlink.c b/netlink.c > index d93ecda..bc5b2bf 100644 > --- a/netlink.c > +++ b/netlink.c > @@ -334,19 +334,18 @@ next: > } > > /** > - * nl_addr() - Get/set IP addresses > - * @ns: Use netlink socket in namespace > - * @ifi: Interface index > + * nl_addr() - Get/set/copy IP addresses for given interface and address family > + * @op: Requested operation > + * @ifi: Interface index in outer network namespace > + * @ifi_ns: Interface index in target namespace for NL_SET, NL_DUP > * @af: Address family > - * @addr: Global address to fill if zero, to set if not, ignored if NULL > + * @addr: Global address to fill on NL_GET, to set on NL_SET > * @prefix_len: Mask or prefix length, set or fetched (for IPv4) > - * @addr_l: Link-scoped address to fill, NULL if not requested > + * @addr_l: Link-scoped address to fill on NL_GET > */ > -void nl_addr(int ns, unsigned int ifi, sa_family_t af, > - void *addr, int *prefix_len, void *addr_l) > +void nl_addr(enum nl_op op, unsigned int ifi, unsigned int ifi_ns, > + sa_family_t af, void *addr, int *prefix_len, void *addr_l) > { > - int set = addr && ((af == AF_INET6 && !IN6_IS_ADDR_UNSPECIFIED(addr)) || > - (af == AF_INET && *(uint32_t *)addr)); > struct req_t { > struct nlmsghdr nlh; > struct ifaddrmsg ifa; > @@ -365,23 +364,23 @@ void nl_addr(int ns, unsigned int ifi, sa_family_t af, > } a6; > } set; > } req = { > - .nlh.nlmsg_type = set ? RTM_NEWADDR : RTM_GETADDR, > + .nlh.nlmsg_type = op == NL_SET ? RTM_NEWADDR : RTM_GETADDR, > .nlh.nlmsg_flags = NLM_F_REQUEST, > .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), > .nlh.nlmsg_seq = nl_seq++, > > .ifa.ifa_family = af, > .ifa.ifa_index = ifi, > - .ifa.ifa_prefixlen = *prefix_len, > + .ifa.ifa_prefixlen = op == NL_SET ? *prefix_len : 0, > }; > + ssize_t n, nlmsgs_size; > struct ifaddrmsg *ifa; > struct nlmsghdr *nh; > struct rtattr *rta; > char buf[NLBUFSIZ]; > - ssize_t n; > size_t na; > > - if (set) { > + if (op == NL_SET) { > if (af == AF_INET6) { > size_t rta_len = RTA_LENGTH(sizeof(req.set.a6.l)); > > @@ -416,21 +415,47 @@ void nl_addr(int ns, unsigned int ifi, sa_family_t af, > req.nlh.nlmsg_flags |= NLM_F_DUMP; > } > > - if ((n = nl_req(ns, buf, &req, req.nlh.nlmsg_len)) < 0 || set) > + if ((n = nl_req(op == NL_SET, buf, &req, req.nlh.nlmsg_len)) < 0) > + return; > + > + if (op == NL_SET) > return; > > nh = (struct nlmsghdr *)buf; > + nlmsgs_size = n; > + > for ( ; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) { > if (nh->nlmsg_type != RTM_NEWADDR) > goto next; > > + if (op == NL_DUP) { > + nh->nlmsg_seq = nl_seq++; > + nh->nlmsg_pid = 0; > + nh->nlmsg_flags &= ~NLM_F_DUMP_FILTERED; > + nh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK | > + NLM_F_CREATE; > + } > + > ifa = (struct ifaddrmsg *)NLMSG_DATA(nh); > + > + if (op == NL_DUP && (ifa->ifa_scope == RT_SCOPE_LINK || > + ifa->ifa_index != ifi)) { > + ifa->ifa_family = AF_UNSPEC; > + goto next; > + } > + > if (ifa->ifa_index != ifi) > goto next; > > + if (op == NL_DUP) > + ifa->ifa_index = ifi_ns; > + > for (rta = IFA_RTA(ifa), na = RTM_PAYLOAD(nh); RTA_OK(rta, na); > rta = RTA_NEXT(rta, na)) { > - if (rta->rta_type != IFA_ADDRESS) > + if (op == NL_DUP && rta->rta_type == IFA_LABEL) > + rta->rta_type = IFA_UNSPEC; > + > + if (op == NL_DUP || rta->rta_type != IFA_ADDRESS) > continue; > > if (af == AF_INET && addr && !*(uint32_t *)addr) { > @@ -451,6 +476,13 @@ next: > if (nh->nlmsg_type == NLMSG_DONE) > break; > } > + > + if (op == NL_DUP) { > + char resp[NLBUFSIZ]; > + > + nh = (struct nlmsghdr *)buf; > + nl_req(1, resp, nh, nlmsgs_size); > + } > } > > /** > diff --git a/netlink.h b/netlink.h > index 217cf1e..cd0e666 100644 > --- a/netlink.h > +++ b/netlink.h > @@ -16,8 +16,8 @@ void nl_sock_init(const struct ctx *c, bool ns); > unsigned int nl_get_ext_if(sa_family_t af); > void nl_route(enum nl_op op, unsigned int ifi, unsigned int ifi_ns, > sa_family_t af, void *gw); > -void nl_addr(int ns, unsigned int ifi, sa_family_t af, > - void *addr, int *prefix_len, void *addr_l); > +void nl_addr(enum nl_op op, unsigned int ifi, unsigned int ifi_ns, > + sa_family_t af, void *addr, int *prefix_len, void *addr_l); > void nl_link(int ns, unsigned int ifi, void *mac, int up, int mtu); > > #endif /* NETLINK_H */ > diff --git a/pasta.c b/pasta.c > index b546c93..99ef3fc 100644 > --- a/pasta.c > +++ b/pasta.c > @@ -278,16 +278,16 @@ void pasta_ns_conf(struct ctx *c) > nl_link(1, c->pasta_ifi, c->mac_guest, 1, c->mtu); > > if (c->ifi4) { > - nl_addr(1, c->pasta_ifi, AF_INET, &c->ip4.addr, > - &c->ip4.prefix_len, NULL); > + nl_addr(NL_SET, c->ifi4, c->pasta_ifi, AF_INET, > + &c->ip4.addr, &c->ip4.prefix_len, NULL); > nl_route(op_routes, c->ifi4, c->pasta_ifi, AF_INET, > &c->ip4.gw); > } > > if (c->ifi6) { > int prefix_len = 64; > - nl_addr(1, c->pasta_ifi, AF_INET6, &c->ip6.addr, > - &prefix_len, NULL); > + nl_addr(NL_SET, c->ifi6, c->pasta_ifi, AF_INET6, > + &c->ip6.addr, &prefix_len, NULL); > nl_route(op_routes, c->ifi6, c->pasta_ifi, AF_INET6, > &c->ip6.gw); > } -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson