diff mbox series

[v2,iproute2,2/2] iplink: Fix link-netns id and link ifindex

Message ID 20241011080111.387028-3-shaw.leon@gmail.com (mailing list archive)
State Accepted
Delegated to: Stephen Hemminger
Headers show
Series iplink: Fix link-netns handling | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Xiao Liang Oct. 11, 2024, 8:01 a.m. UTC
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>
---
 ip/iplink.c | 143 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 118 insertions(+), 25 deletions(-)

Comments

Nicolas Dichtel Oct. 15, 2024, 7:45 a.m. UTC | #1
Le 11/10/2024 à 10:01, Xiao Liang a écrit :
> 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.
Indeed.

> 
> Signed-off-by: Xiao Liang <shaw.leon@gmail.com>
> ---
>  ip/iplink.c | 143 +++++++++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 118 insertions(+), 25 deletions(-)
> 

[snip]

> @@ -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);
>  			}
This chunk is added to allow the user to give a pid instead of a netns name.
It's not directly related to the patch topic. Could you put in a separate patch?

> -			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();
Xiao Liang Oct. 15, 2024, 9:06 a.m. UTC | #2
On Tue, Oct 15, 2024 at 3:45 PM Nicolas Dichtel
<nicolas.dichtel@6wind.com> wrote:

> > @@ -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);
> >                       }
> This chunk is added to allow the user to give a pid instead of a netns name.
> It's not directly related to the patch topic. Could you put in a separate patch?

Currently ip-link already accepts pid as netns argument, and passes to
kernel as IFLA_NET_NS_PID. This patch converts it to fd for
simplicity, so that it can be reused in later setns() call (before
opening RTNL in target netns).
Nicolas Dichtel Oct. 15, 2024, 2:27 p.m. UTC | #3
Le 15/10/2024 à 11:06, Xiao Liang a écrit :
> On Tue, Oct 15, 2024 at 3:45 PM Nicolas Dichtel
> <nicolas.dichtel@6wind.com> wrote:
> 
>>> @@ -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);
>>>                       }
>> This chunk is added to allow the user to give a pid instead of a netns name.
>> It's not directly related to the patch topic. Could you put in a separate patch?
> 
> Currently ip-link already accepts pid as netns argument, and passes to
> kernel as IFLA_NET_NS_PID. This patch converts it to fd for
> simplicity, so that it can be reused in later setns() call (before
> opening RTNL in target netns).
Right, I've misread the diff.

Acked-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
diff mbox series

Patch

diff --git a/ip/iplink.c b/ip/iplink.c
index c9168985..c2c0e371 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -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,11 @@  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;
 }