diff --git a/ip/ip_common.h b/ip/ip_common.h
index 625311c25bce..e643b89ffd88 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -125,7 +125,7 @@ struct link_util {
 	const char		*id;
 	int			maxattr;
 	int			(*parse_opt)(struct link_util *, int, char **,
-					     struct nlmsghdr *);
+					     struct nlmsghdr *, int *);
 	void			(*print_opt)(struct link_util *, FILE *,
 					     struct rtattr *[]);
 	void			(*print_xstats)(struct link_util *, FILE *,
@@ -139,7 +139,7 @@ struct link_util {
 
 struct link_util *get_link_kind(const char *kind);
 
-int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type);
+int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type, int *netns_fd);
 
 /* iplink_bridge.c */
 void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len);
diff --git a/ip/iplink.c b/ip/iplink.c
index 3bc75d243719..c2b63999daee 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -522,7 +522,7 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
 	return 0;
 }
 
-int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
+int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type, int *netns_fd)
 {
 	bool move_netns = false;
 	char *name = NULL;
@@ -622,9 +622,11 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
 			if (netns != -1)
 				duparg("netns", *argv);
 			netns = netns_get_fd(*argv);
-			if (netns >= 0)
+			if (netns >= 0) {
+				*netns_fd = netns;
 				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD,
 					  &netns, 4);
+			}
 			else if (get_integer(&netns, *argv, 0) == 0)
 				addattr_l(&req->n, sizeof(*req),
 					  IFLA_NET_NS_PID, &netns, 4);
@@ -1034,9 +1036,10 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
 		.n.nlmsg_type = cmd,
 		.i.ifi_family = preferred_family,
 	};
+	int netns_fd = -1, netns_fd2 = -1;
 	int ret;
 
-	ret = iplink_parse(argc, argv, &req, &type);
+	ret = iplink_parse(argc, argv, &req, &type, &netns_fd);
 	if (ret < 0)
 		return ret;
 
@@ -1064,7 +1067,7 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
 
 			data = addattr_nest(&req.n, sizeof(req), iflatype);
 
-			if (lu->parse_opt(lu, argc, argv, &req.n))
+			if (lu->parse_opt(lu, argc, argv, &req.n, &netns_fd2))
 				return -1;
 
 			addattr_nest_end(&req.n, data);
@@ -1088,6 +1091,11 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
 	else
 		ret = rtnl_talk(&rth, &req.n, NULL);
 
+	if (netns_fd >= 0)
+		close(netns_fd);
+	if (netns_fd2 >= 0)
+		close(netns_fd2);
+
 	if (ret)
 		return -2;
 
diff --git a/ip/iplink_amt.c b/ip/iplink_amt.c
index 3a35bd9df9d1..9d7f991d12e3 100644
--- a/ip/iplink_amt.c
+++ b/ip/iplink_amt.c
@@ -55,7 +55,7 @@ static void check_duparg(__u64 *attrs, int type, const char *key,
 }
 
 static int amt_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	unsigned int mode, max_tunnels;
 	inet_prefix saddr, daddr;
diff --git a/ip/iplink_bareudp.c b/ip/iplink_bareudp.c
index aa3111068788..b21f962b1ba1 100644
--- a/ip/iplink_bareudp.c
+++ b/ip/iplink_bareudp.c
@@ -46,7 +46,7 @@ static void check_duparg(__u64 *attrs, int type, const char *key,
 }
 
 static int bareudp_parse_opt(struct link_util *lu, int argc, char **argv,
-			     struct nlmsghdr *n)
+			     struct nlmsghdr *n, int *netns_fd)
 {
 	bool multiproto = false;
 	__u16 srcportmin = 0;
diff --git a/ip/iplink_batadv.c b/ip/iplink_batadv.c
index 27d316762e70..481bfb4e5920 100644
--- a/ip/iplink_batadv.c
+++ b/ip/iplink_batadv.c
@@ -29,7 +29,7 @@ static void explain(void)
 }
 
 static int batadv_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
+			    struct nlmsghdr *n, int *netns_fd)
 {
 	while (argc > 0) {
 		if (matches(*argv, "ra") == 0) {
diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c
index 19af67d001f0..0e1303834219 100644
--- a/ip/iplink_bond.c
+++ b/ip/iplink_bond.c
@@ -174,7 +174,7 @@ static void explain(void)
 }
 
 static int bond_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *n, int *netns_fd)
 {
 	__u8 mode, use_carrier, primary_reselect, fail_over_mac;
 	__u8 xmit_hash_policy, num_peer_notif, all_slaves_active;
diff --git a/ip/iplink_bond_slave.c b/ip/iplink_bond_slave.c
index ad6875006950..52a8ba076013 100644
--- a/ip/iplink_bond_slave.c
+++ b/ip/iplink_bond_slave.c
@@ -148,7 +148,7 @@ static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *t
 }
 
 static int bond_slave_parse_opt(struct link_util *lu, int argc, char **argv,
-				struct nlmsghdr *n)
+				struct nlmsghdr *n, int *netns_fd)
 {
 	__u16 queue_id;
 	int prio;
diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c
index f01ffe15d7e8..debc804986ac 100644
--- a/ip/iplink_bridge.c
+++ b/ip/iplink_bridge.c
@@ -81,7 +81,7 @@ void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len)
 }
 
 static int bridge_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
+			    struct nlmsghdr *n, int *netns_fd)
 {
 	struct br_boolopt_multi bm = {};
 	__u32 val;
diff --git a/ip/iplink_bridge_slave.c b/ip/iplink_bridge_slave.c
index 3821923b5da5..5ce9d5d2cbdc 100644
--- a/ip/iplink_bridge_slave.c
+++ b/ip/iplink_bridge_slave.c
@@ -327,7 +327,7 @@ static void bridge_slave_parse_on_off(char *arg_name, char *arg_val,
 }
 
 static int bridge_slave_parse_opt(struct link_util *lu, int argc, char **argv,
-				  struct nlmsghdr *n)
+				  struct nlmsghdr *n, int *netns_fd)
 {
 	__u8 state;
 	__u16 priority;
diff --git a/ip/iplink_can.c b/ip/iplink_can.c
index f2967db5d2b6..957356f5959b 100644
--- a/ip/iplink_can.c
+++ b/ip/iplink_can.c
@@ -124,7 +124,7 @@ static void print_ctrlmode(enum output_type t, __u32 flags, const char* key)
 }
 
 static int can_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	struct can_bittiming bt = {}, dbt = {};
 	struct can_ctrlmode cm = { 0 };
diff --git a/ip/iplink_dsa.c b/ip/iplink_dsa.c
index e3f3f8aca13c..cabccd909d53 100644
--- a/ip/iplink_dsa.c
+++ b/ip/iplink_dsa.c
@@ -12,7 +12,7 @@ static void print_usage(FILE *f)
 }
 
 static int dsa_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	while (argc > 0) {
 		if (strcmp(*argv, "conduit") == 0 ||
diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c
index 62c61bce138b..2c693bb25f01 100644
--- a/ip/iplink_geneve.c
+++ b/ip/iplink_geneve.c
@@ -54,7 +54,7 @@ static void check_duparg(__u64 *attrs, int type, const char *key,
 }
 
 static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			    struct nlmsghdr *n, int *netns_fd)
 {
 	inet_prefix daddr;
 	__u32 vni = 0;
diff --git a/ip/iplink_gtp.c b/ip/iplink_gtp.c
index 086946b62ede..7774acce6a64 100644
--- a/ip/iplink_gtp.c
+++ b/ip/iplink_gtp.c
@@ -32,7 +32,7 @@ static void check_duparg(__u32 *attrs, int type, const char *key,
 }
 
 static int gtp_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	__u32 attrs = 0;
 
diff --git a/ip/iplink_hsr.c b/ip/iplink_hsr.c
index 42adb43009b7..9fb8ac93af29 100644
--- a/ip/iplink_hsr.c
+++ b/ip/iplink_hsr.c
@@ -45,7 +45,7 @@ static void usage(void)
 }
 
 static int hsr_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	int ifindex;
 	unsigned char multicast_spec;
diff --git a/ip/iplink_ipoib.c b/ip/iplink_ipoib.c
index 7bf4e3215dd2..37e5a57e6d5e 100644
--- a/ip/iplink_ipoib.c
+++ b/ip/iplink_ipoib.c
@@ -38,7 +38,7 @@ static int mode_arg(void)
 }
 
 static int ipoib_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			   struct nlmsghdr *n, int *netns_fd)
 {
 	__u16 pkey, mode, umcast;
 
diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c
index f29fa4f9e3eb..ba2b81ee94e0 100644
--- a/ip/iplink_ipvlan.c
+++ b/ip/iplink_ipvlan.c
@@ -26,7 +26,7 @@ static void print_explain(struct link_util *lu, FILE *f)
 }
 
 static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
+			    struct nlmsghdr *n, int *netns_fd)
 {
 	__u16 flags = 0;
 	bool mflag_given = false;
diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c
index 6bdc76d1a700..2d4419c04053 100644
--- a/ip/iplink_macvlan.c
+++ b/ip/iplink_macvlan.c
@@ -75,7 +75,7 @@ static int bclim_arg(const char *arg)
 }
 
 static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			     struct nlmsghdr *n, int *netns_fd)
 {
 	__u32 mode = 0;
 	__u16 flags = 0;
diff --git a/ip/iplink_netkit.c b/ip/iplink_netkit.c
index a838a41078f9..e2e6cd2941d1 100644
--- a/ip/iplink_netkit.c
+++ b/ip/iplink_netkit.c
@@ -38,7 +38,7 @@ static void explain(struct link_util *lu, FILE *f)
 }
 
 static int netkit_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
+			    struct nlmsghdr *n, int *netns_fd)
 {
 	__u32 ifi_flags, ifi_change, ifi_index;
 	struct ifinfomsg *ifm, *peer_ifm;
@@ -97,7 +97,8 @@ static int netkit_parse_opt(struct link_util *lu, int argc, char **argv,
 			if (seen_peer) {
 				data = addattr_nest(n, 1024, IFLA_NETKIT_PEER_INFO);
 				n->nlmsg_len += sizeof(struct ifinfomsg);
-				err = iplink_parse(argc, argv, (struct iplink_req *)n, &type);
+				err = iplink_parse(argc, argv, (struct iplink_req *)n, &type,
+						   netns_fd);
 				if (err < 0)
 					return err;
 				if (type)
diff --git a/ip/iplink_rmnet.c b/ip/iplink_rmnet.c
index 1d16440c6900..ca5afe0579ce 100644
--- a/ip/iplink_rmnet.c
+++ b/ip/iplink_rmnet.c
@@ -27,7 +27,7 @@ static void explain(void)
 }
 
 static int rmnet_parse_opt(struct link_util *lu, int argc, char **argv,
-			   struct nlmsghdr *n)
+			   struct nlmsghdr *n, int *netns_fd)
 {
 	__u16 mux_id;
 
diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c
index 4ac5bc03f2b3..6d029297da7a 100644
--- a/ip/iplink_vlan.c
+++ b/ip/iplink_vlan.c
@@ -73,7 +73,7 @@ static int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n,
 }
 
 static int vlan_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifla_vlan_flags flags = { 0 };
 	__u16 id, proto;
diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c
index 9474a2b78c5f..ef6a93561abc 100644
--- a/ip/iplink_vrf.c
+++ b/ip/iplink_vrf.c
@@ -26,7 +26,7 @@ static void explain(void)
 }
 
 static int vrf_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	while (argc > 0) {
 		if (matches(*argv, "table") == 0) {
diff --git a/ip/iplink_vxcan.c b/ip/iplink_vxcan.c
index e0f9bacbd3db..1154fea9fdb1 100644
--- a/ip/iplink_vxcan.c
+++ b/ip/iplink_vxcan.c
@@ -27,7 +27,7 @@ static void usage(void)
 }
 
 static int vxcan_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			   struct nlmsghdr *n, int *netns_fd)
 {
 	char *type = NULL;
 	int err;
@@ -52,7 +52,8 @@ static int vxcan_parse_opt(struct link_util *lu, int argc, char **argv,
 
 	n->nlmsg_len += sizeof(struct ifinfomsg);
 
-	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type);
+	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type,
+			   netns_fd);
 	if (err < 0)
 		return err;
 
diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c
index 7781d60bbb52..e9e19ef27b43 100644
--- a/ip/iplink_vxlan.c
+++ b/ip/iplink_vxlan.c
@@ -92,7 +92,7 @@ static void check_duparg(__u64 *attrs, int type, const char *key,
 }
 
 static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			   struct nlmsghdr *n, int *netns_fd)
 {
 	inet_prefix saddr, daddr;
 	__u32 vni = 0;
diff --git a/ip/iplink_wwan.c b/ip/iplink_wwan.c
index 3510477a16ca..26ad47a3fe87 100644
--- a/ip/iplink_wwan.c
+++ b/ip/iplink_wwan.c
@@ -22,7 +22,7 @@ static void explain(void)
 }
 
 static int wwan_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *n, int *netns_fd)
 {
 	while (argc > 0) {
 		if (matches(*argv, "linkid") == 0) {
diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c
index fc4c86314fab..f074f7deff87 100644
--- a/ip/ipmacsec.c
+++ b/ip/ipmacsec.c
@@ -1344,7 +1344,7 @@ static void usage(FILE *f)
 }
 
 static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
+			    struct nlmsghdr *n, int *netns_fd)
 {
 	int ret;
 	__u8 encoding_sa = 0xff;
diff --git a/ip/link_gre.c b/ip/link_gre.c
index 010b4824b809..d2a45b740cb1 100644
--- a/ip/link_gre.c
+++ b/ip/link_gre.c
@@ -63,7 +63,7 @@ static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
 }
 
 static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
diff --git a/ip/link_gre6.c b/ip/link_gre6.c
index fc3e35f7e83f..1b6f00c47160 100644
--- a/ip/link_gre6.c
+++ b/ip/link_gre6.c
@@ -66,7 +66,7 @@ static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
 }
 
 static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c
index c2a05cee35bb..5b94855be586 100644
--- a/ip/link_ip6tnl.c
+++ b/ip/link_ip6tnl.c
@@ -59,7 +59,7 @@ static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv,
 }
 
 static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv,
-			       struct nlmsghdr *n)
+			       struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
index 49c3ae2612ca..97922f85a626 100644
--- a/ip/link_iptnl.c
+++ b/ip/link_iptnl.c
@@ -60,7 +60,7 @@ static void iptunnel_print_help(struct link_util *lu, int argc, char **argv,
 }
 
 static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
-			      struct nlmsghdr *n)
+			      struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
diff --git a/ip/link_veth.c b/ip/link_veth.c
index 6da5b64f73ce..4c60f7cecb7b 100644
--- a/ip/link_veth.c
+++ b/ip/link_veth.c
@@ -24,7 +24,7 @@ static void usage(void)
 }
 
 static int veth_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *n, int *netns_fd)
 {
 	char *type = NULL;
 	int err;
@@ -49,7 +49,8 @@ static int veth_parse_opt(struct link_util *lu, int argc, char **argv,
 
 	n->nlmsg_len += sizeof(struct ifinfomsg);
 
-	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type);
+	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type,
+			   netns_fd);
 	if (err < 0)
 		return err;
 
diff --git a/ip/link_vti.c b/ip/link_vti.c
index 2106a9d2cee3..03823528ea38 100644
--- a/ip/link_vti.c
+++ b/ip/link_vti.c
@@ -35,7 +35,7 @@ static void vti_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
 }
 
 static int vti_parse_opt(struct link_util *lu, int argc, char **argv,
-			 struct nlmsghdr *n)
+			 struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
diff --git a/ip/link_vti6.c b/ip/link_vti6.c
index 7362f336a895..26dad8c11294 100644
--- a/ip/link_vti6.c
+++ b/ip/link_vti6.c
@@ -37,7 +37,7 @@ static void vti6_print_help(struct link_util *lu, int argc, char **argv,
 }
 
 static int vti6_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *n, int *netns_fd)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
diff --git a/ip/link_xfrm.c b/ip/link_xfrm.c
index d76398cd3283..dd96f0504784 100644
--- a/ip/link_xfrm.c
+++ b/ip/link_xfrm.c
@@ -25,7 +25,7 @@ static void xfrm_print_help(struct link_util *lu, int argc, char **argv,
 }
 
 static int xfrm_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *n, int *netns_fd)
 {
 	unsigned int link = 0;
 	bool metadata = false;
