]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
iplink: Fix link-netns id and link ifindex
authorXiao Liang <shaw.leon@gmail.com>
Fri, 11 Oct 2024 08:01:09 +0000 (16:01 +0800)
committerStephen Hemminger <stephen@networkplumber.org>
Thu, 17 Oct 2024 19:09:44 +0000 (12:09 -0700)
When link-netns or link-netnsid is supplied, lookup link in that netns.
And if both netns and link-netns are given, IFLA_LINK_NETNSID should be
the nsid of link-netns from the view of target netns, not from current
one.

For example, when handling:

    # ip -n ns1 link add netns ns2 link-netns ns3 link eth1 eth1.100 type vlan id 100

should lookup eth1 in ns3 and IFLA_LINK_NETNSID is the id of ns3 from
ns2.

Signed-off-by: Xiao Liang <shaw.leon@gmail.com>
Acked-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
ip/iplink.c

index c916898562614b312b22111b9edece640d2bf356..e650a5c22d687c13c42b75328e2ec3f47971b26e 100644 (file)
@@ -240,6 +240,38 @@ static int nl_get_ll_addr_len(const char *ifname)
        return len;
 }
 
+static int get_ifindex_in_netns(struct rtnl_handle *rtnl, int netnsid,
+                               const char *ifname)
+{
+       struct {
+               struct nlmsghdr         n;
+               struct ifinfomsg        ifm;
+               char                    buf[1024];
+       } req = {
+               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+               .n.nlmsg_flags = NLM_F_REQUEST,
+               .n.nlmsg_type = RTM_GETLINK,
+       };
+       struct nlmsghdr *answer;
+       int ifindex;
+
+       addattr32(&req.n, sizeof(req), IFLA_TARGET_NETNSID, netnsid);
+       addattr_l(&req.n, sizeof(req),
+                 !check_ifname(ifname) ? IFLA_IFNAME : IFLA_ALT_IFNAME,
+                 ifname, strlen(ifname) + 1);
+
+       if (rtnl_talk(rtnl, &req.n, &answer) < 0)
+               return 0;
+
+       if (answer->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
+               free(answer);
+               return 0;
+       }
+       ifindex = ((struct ifinfomsg *)NLMSG_DATA(answer))->ifi_index;
+       free(answer);
+       return ifindex;
+}
+
 static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
                                      struct ifla_vf_vlan_info *ivvip)
 {
@@ -536,7 +568,10 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
        int vf = -1;
        int numtxqueues = -1;
        int numrxqueues = -1;
+       char *link_netns = NULL;
        int link_netnsid = -1;
+       struct rtnl_handle netns_rtnl;
+       struct rtnl_handle *rtnl = &rth;
        int index = 0;
        int group = -1;
        int addr_len = 0;
@@ -618,20 +653,25 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
                        if (offload && name == dev)
                                dev = NULL;
                } else if (strcmp(*argv, "netns") == 0) {
+                       int pid;
+
                        NEXT_ARG();
                        if (netns != -1)
                                duparg("netns", *argv);
                        netns = netns_get_fd(*argv);
-                       if (netns >= 0) {
-                               open_fds_add(netns);
-                               addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD,
-                                         &netns, 4);
+                       if (netns < 0 && get_integer(&pid, *argv, 0) == 0) {
+                               char path[PATH_MAX];
+
+                               snprintf(path, sizeof(path), "/proc/%d/ns/net",
+                                        pid);
+                               netns = open(path, O_RDONLY);
                        }
-                       else if (get_integer(&netns, *argv, 0) == 0)
-                               addattr_l(&req->n, sizeof(*req),
-                                         IFLA_NET_NS_PID, &netns, 4);
-                       else
+                       if (netns < 0)
                                invarg("Invalid \"netns\" value\n", *argv);
+
+                       open_fds_add(netns);
+                       addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD,
+                                 &netns, 4);
                        move_netns = true;
                } else if (strcmp(*argv, "multicast") == 0) {
                        NEXT_ARG();
@@ -817,21 +857,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
                        addattr_nest_end(&req->n, afs);
                } else if (matches(*argv, "link-netns") == 0) {
                        NEXT_ARG();
-                       if (link_netnsid != -1)
+                       if (link_netnsid != -1 || link_netns)
                                duparg("link-netns/link-netnsid", *argv);
-                       link_netnsid = netns_id_from_name(&rth, *argv);
-                       /* No nsid? Try to assign one. */
-                       if (link_netnsid < 0)
-                               set_netns_id_from_name(&rth, *argv, -1);
-                       link_netnsid = netns_id_from_name(&rth, *argv);
-                       if (link_netnsid < 0)
-                               invarg("Invalid \"link-netns\" value\n",
-                                      *argv);
-                       addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
-                                 link_netnsid);
+                       link_netns = *argv;
                } else if (matches(*argv, "link-netnsid") == 0) {
                        NEXT_ARG();
-                       if (link_netnsid != -1)
+                       if (link_netnsid != -1 || link_netns)
                                duparg("link-netns/link-netnsid", *argv);
                        if (get_integer(&link_netnsid, *argv, 0))
                                invarg("Invalid \"link-netnsid\" value\n",
@@ -983,6 +1014,53 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
                }
        }
 
+       if (netns != -1 && (link_netnsid != -1 || link_netns)) {
+               int orig_netns;
+
+               /*
+                * When both link-netns and netns are set, open an RTNL in
+                * target netns, to
+                *   1) get link-netns id from the view of target netns, and
+                *   2) get link ifindex from link-netns.
+                */
+               orig_netns = open("/proc/self/ns/net", O_RDONLY);
+               if (orig_netns == -1) {
+                       fprintf(stderr, "Cannot open namespace: %s\n",
+                               strerror(errno));
+                       exit(-1);
+               }
+               if (setns(netns, CLONE_NEWNET) < 0) {
+                       fprintf(stderr, "Cannot set namespace: %s\n",
+                               strerror(errno));
+                       exit(-1);
+               }
+               if (rtnl_open(&netns_rtnl, 0) < 0) {
+                       fprintf(stderr, "Cannot open rtnetlink\n");
+                       exit(-1);
+               }
+               if (setns(orig_netns, CLONE_NEWNET) < 0) {
+                       fprintf(stderr, "Cannot set namespace: %s\n",
+                               strerror(errno));
+                       exit(-1);
+               }
+               close(orig_netns);
+               rtnl = &netns_rtnl;
+       }
+
+       if (link_netns) {
+               link_netnsid = netns_id_from_name(rtnl, link_netns);
+               /* No nsid? Try to assign one. */
+               if (link_netnsid < 0) {
+                       set_netns_id_from_name(rtnl, link_netns, -1);
+                       link_netnsid = netns_id_from_name(rtnl, link_netns);
+               }
+               if (link_netnsid < 0)
+                       invarg("Invalid \"link-netns\" value\n",
+                              *argv);
+               addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
+                         link_netnsid);
+       }
+
        if (!(req->n.nlmsg_flags & NLM_F_CREATE)) {
                if (!dev) {
                        fprintf(stderr,
@@ -991,8 +1069,10 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
                }
 
                req->i.ifi_index = ll_name_to_index(dev);
-               if (!req->i.ifi_index)
-                       return nodev(dev);
+               if (!req->i.ifi_index) {
+                       ret = nodev(dev);
+                       goto out;
+               }
 
                /* Not renaming to the same name */
                if (name == dev)
@@ -1010,9 +1090,17 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
                if (link) {
                        int ifindex;
 
-                       ifindex = ll_name_to_index(link);
-                       if (!ifindex)
-                               return nodev(link);
+                       if (link_netnsid == -1)
+                               ifindex = ll_name_to_index(link);
+                       else
+                               ifindex = get_ifindex_in_netns(rtnl,
+                                                              link_netnsid,
+                                                              link);
+
+                       if (!ifindex) {
+                               ret = nodev(link);
+                               goto out;
+                       }
                        addattr32(&req->n, sizeof(*req), IFLA_LINK, ifindex);
                }
 
@@ -1024,6 +1112,10 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
                          IFLA_IFNAME, name, strlen(name) + 1);
        }
 
+out:
+       if (rtnl == &netns_rtnl)
+               rtnl_close(rtnl);
+
        return ret;
 }