Linux virtualization list
 help / color / mirror / Atom feed
* [PATCH RFC iproute2-next 0/2] ip: add "ip vsock" to move the guest vsock device to a netns
@ 2026-11-05 19:30 Bobby Eshleman
  2026-11-05 19:30 ` [PATCH RFC iproute2-next 1/2] uapi: add vsock_netlink.h Bobby Eshleman
  2026-11-05 19:30 ` [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
  0 siblings, 2 replies; 5+ messages in thread
From: Bobby Eshleman @ 2026-11-05 19:30 UTC (permalink / raw)
  To: netdev
  Cc: David Ahern, Stephen Hemminger, Stefano Garzarella,
	virtualization, Bobby Eshleman, Bobby Eshleman

This adds iproute2 support for the "vsock" generic netlink family from
the kernel series "vsock: assign the guest vsock device to a network
namespace":

  https://lore.kernel.org/all/20261103-vsock-guest-ns-v5-0-48179b9b66b8@meta.com/

The guest's vsock device is not a netdev, so it cannot be moved with
"ip link set DEV netns". The kernel series adds VSOCK_CMD_DEV_NETNS_SET,
which assigns the device to the namespace the request was sent from, and
VSOCK_CMD_DEV_NETNS_GET, which reports where it is. This series exposes
both through a new "ip vsock" object:

  # ip netns add guest
  # ip vsock set netns guest
  # ip vsock show
  vsock netns guest
  # ip -n guest vsock show
  vsock

I considered using "ip link", but I opted away from that option to avoid
the scenario (however unlikely) of masking out / colliding with a netdev
with the name "vsock".

Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Bobby Eshleman (2):
      uapi: add vsock_netlink.h
      ip: add "ip vsock" to move the guest vsock device to a netns

 include/uapi/linux/vsock_netlink.h |  28 +++++
 ip/Makefile                        |   2 +-
 ip/ip.c                            |   3 +-
 ip/ip_common.h                     |   1 +
 ip/ipvsock.c                       | 205 +++++++++++++++++++++++++++++++++++++
 man/man8/ip-vsock.8                | 121 ++++++++++++++++++++++
 man/man8/ip.8                      |   7 +-
 7 files changed, 364 insertions(+), 3 deletions(-)
---
base-commit: 204a721033556114c81c47d00375f2d52e98e463
change-id: 20261105-vsock-guest-ns-4bf1ce74742e

Best regards,
-- 
Bobby Eshleman <bobbyeshleman@meta.com>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH RFC iproute2-next 1/2] uapi: add vsock_netlink.h
  2026-11-05 19:30 [PATCH RFC iproute2-next 0/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
@ 2026-11-05 19:30 ` Bobby Eshleman
  2026-11-05 19:30 ` [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
  1 sibling, 0 replies; 5+ messages in thread
From: Bobby Eshleman @ 2026-11-05 19:30 UTC (permalink / raw)
  To: netdev
  Cc: David Ahern, Stephen Hemminger, Stefano Garzarella,
	virtualization, Bobby Eshleman, Bobby Eshleman

Add the uapi header for the "vsock" generic netlink family, from the
pending kernel series that adds it. This is only here so that the
following patch builds, and is meant to be replaced by the usual kernel
header sync once the series is merged.

Link: https://lore.kernel.org/all/20261103-vsock-guest-ns-v5-0-48179b9b66b8@meta.com/
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
 include/uapi/linux/vsock_netlink.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/include/uapi/linux/vsock_netlink.h b/include/uapi/linux/vsock_netlink.h
new file mode 100644
index 000000000000..adf0fbc50ad5
--- /dev/null
+++ b/include/uapi/linux/vsock_netlink.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/vsock.yaml */
+/* YNL-GEN uapi header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _UAPI_LINUX_VSOCK_NETLINK_H
+#define _UAPI_LINUX_VSOCK_NETLINK_H
+
+#define VSOCK_FAMILY_NAME	"vsock"
+#define VSOCK_FAMILY_VERSION	1
+
+enum {
+	VSOCK_A_NETNS_ID = 1,
+
+	__VSOCK_A_MAX,
+	VSOCK_A_MAX = (__VSOCK_A_MAX - 1)
+};
+
+enum {
+	VSOCK_CMD_DEV_NETNS_SET = 1,
+	VSOCK_CMD_DEV_NETNS_GET,
+
+	__VSOCK_CMD_MAX,
+	VSOCK_CMD_MAX = (__VSOCK_CMD_MAX - 1)
+};
+
+#endif /* _UAPI_LINUX_VSOCK_NETLINK_H */

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns
  2026-11-05 19:30 [PATCH RFC iproute2-next 0/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
  2026-11-05 19:30 ` [PATCH RFC iproute2-next 1/2] uapi: add vsock_netlink.h Bobby Eshleman
@ 2026-11-05 19:30 ` Bobby Eshleman
  2026-11-06  9:26   ` Stephen Hemminger
  2026-11-06  9:36   ` Stephen Hemminger
  1 sibling, 2 replies; 5+ messages in thread
From: Bobby Eshleman @ 2026-11-05 19:30 UTC (permalink / raw)
  To: netdev
  Cc: David Ahern, Stephen Hemminger, Stefano Garzarella,
	virtualization, Bobby Eshleman, Bobby Eshleman

The guest's vsock device is not a netdev, so rtnetlink cannot move it
between network namespaces. Add an "ip vsock" object that goes through
the "vsock" generic netlink family instead:

  ip vsock set netns { PID | NETNSNAME | NETNSFILE }
  ip vsock show

"set" issues VSOCK_CMD_DEV_NETNS_SET from inside the target namespace,
since the kernel moves the device to the namespace the request was sent
from. "show" issues VSOCK_CMD_DEV_NETNS_GET and prints where the device
is, relative to the current namespace.

Link: https://lore.kernel.org/all/20261103-vsock-guest-ns-v5-0-48179b9b66b8@meta.com/
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
 ip/Makefile         |   2 +-
 ip/ip.c             |   3 +-
 ip/ip_common.h      |   1 +
 ip/ipvsock.c        | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 man/man8/ip-vsock.8 | 121 +++++++++++++++++++++++++++++++
 man/man8/ip.8       |   7 +-
 6 files changed, 336 insertions(+), 3 deletions(-)

diff --git a/ip/Makefile b/ip/Makefile
index 978f68413025..681140ef8ec4 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -13,7 +13,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
     ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o ipioam6.o \
     iplink_amt.o iplink_batadv.o iplink_gtp.o iplink_virt_wifi.o \
-    iplink_netkit.o ipstats.o iplink_ovpn.o
+    iplink_netkit.o ipstats.o iplink_ovpn.o ipvsock.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip.c b/ip/ip.c
index e4b71bde94b4..68deac93a538 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -63,7 +63,7 @@ static void usage(void)
 		"                   macsec | maddress | monitor | mptcp | mroute | mrule |\n"
 		"                   neighbor | neighbour | netconf | netns | nexthop | ntable |\n"
 		"                   ntbl | route | rule | sr | stats | tap | tcpmetrics |\n"
-		"                   token | tunnel | tuntap | vrf | xfrm }\n"
+		"                   token | tunnel | tuntap | vrf | vsock | xfrm }\n"
 		"       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
 		"                    -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
 		"                    -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
@@ -113,6 +113,7 @@ static const struct cmd {
 	{ "netns",	do_netns },
 	{ "netconf",	do_ipnetconf },
 	{ "vrf",	do_ipvrf},
+	{ "vsock",	do_ipvsock },
 	{ "sr",		do_seg6 },
 	{ "nexthop",	do_ipnh },
 	{ "mptcp",	do_mptcp },
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 3f55ea336b37..c82b1132e43c 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -93,6 +93,7 @@ int do_ipnh(int argc, char **argv);
 int do_mptcp(int argc, char **argv);
 int do_ioam6(int argc, char **argv);
 int do_ipstats(int argc, char **argv);
+int do_ipvsock(int argc, char **argv);
 
 int iplink_get(char *name, __u32 filt_mask);
 int iplink_ifla_xstats(int argc, char **argv);
diff --git a/ip/ipvsock.c b/ip/ipvsock.c
new file mode 100644
index 000000000000..ed4347dd0084
--- /dev/null
+++ b/ip/ipvsock.c
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ipvsock.c		"ip vsock"
+ *
+ * The guest's vsock device is not a netdev, so rtnetlink cannot move it.
+ * The "vsock" generic netlink family moves it with VSOCK_CMD_DEV_NETNS_SET
+ * and reports where it is with VSOCK_CMD_DEV_NETNS_GET. There is nothing to
+ * name in a request: the kernel serves it in the namespace of the socket it
+ * arrived on, so the destination is chosen by sending it from there, and the
+ * reported namespace is an nsid relative to the sender.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/genetlink.h>
+#include <linux/net_namespace.h>
+#include <linux/vsock_netlink.h>
+
+#include "libgenl.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "json_print.h"
+#include "namespace.h"
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"Usage:	ip vsock set netns { PID | NETNSNAME | NETNSFILE }\n"
+		"	ip vsock show\n");
+	exit(-1);
+}
+
+static int vsock_talk(__u8 cmd, struct nlmsghdr **answer)
+{
+	GENL_REQUEST(req, 128, 0, 0, VSOCK_FAMILY_VERSION, cmd, NLM_F_REQUEST);
+	struct rtnl_handle grth = { .fd = -1 };
+	int family = -1;
+	int ret;
+
+	if (genl_init_handle(&grth, VSOCK_FAMILY_NAME, &family)) {
+		fprintf(stderr,
+			"Cannot resolve the \"%s\" netlink family, the kernel may not support moving the vsock device.\n",
+			VSOCK_FAMILY_NAME);
+		return -1;
+	}
+
+	req.n.nlmsg_type = family;
+
+	ret = rtnl_talk(&grth, &req.n, answer);
+	rtnl_close(&grth);
+
+	return ret < 0 ? -2 : 0;
+}
+
+static int vsock_dev_netns_set(const char *netns)
+{
+	int netns_fd, orig_fd, ret;
+
+	netns_fd = netns_get_fd(netns);
+	if (netns_fd < 0) {
+		fprintf(stderr, "Invalid \"netns\" value \"%s\"\n", netns);
+		return -1;
+	}
+
+	orig_fd = open("/proc/self/ns/net", O_RDONLY);
+	if (orig_fd < 0) {
+		fprintf(stderr, "Cannot open namespace: %s\n", strerror(errno));
+		close(netns_fd);
+		return -1;
+	}
+
+	if (setns(netns_fd, CLONE_NEWNET) < 0) {
+		fprintf(stderr, "Cannot set namespace: %s\n", strerror(errno));
+		ret = -1;
+		goto out;
+	}
+
+	/* VSOCK_CMD_DEV_NETNS_SET has no reply, so wait for the ACK. */
+	ret = vsock_talk(VSOCK_CMD_DEV_NETNS_SET, NULL);
+
+	if (setns(orig_fd, CLONE_NEWNET) < 0) {
+		fprintf(stderr, "Cannot restore namespace: %s\n",
+			strerror(errno));
+		ret = -1;
+	}
+
+out:
+	close(orig_fd);
+	close(netns_fd);
+
+	return ret;
+}
+
+static void vsock_print_netnsid(int id)
+{
+	char *name;
+
+	if (is_json_context()) {
+		print_int(PRINT_JSON, "netnsid", NULL, id);
+		return;
+	}
+
+	if (id == NETNSA_NSID_NOT_ASSIGNED) {
+		print_string(PRINT_FP, NULL, " netnsid %s", "unknown");
+		return;
+	}
+
+	name = get_name_from_nsid(id);
+	if (name)
+		print_string(PRINT_FP, NULL, " netns %s", name);
+	else
+		print_int(PRINT_FP, NULL, " netnsid %d", id);
+}
+
+static int vsock_dev_netns_show(void)
+{
+	struct rtattr *tb[VSOCK_A_MAX + 1];
+	struct nlmsghdr *answer;
+	struct genlmsghdr *ghdr;
+	int len, ret;
+
+	ret = vsock_talk(VSOCK_CMD_DEV_NETNS_GET, &answer);
+	if (ret)
+		return ret;
+
+	ghdr = NLMSG_DATA(answer);
+	len = answer->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+	if (len < 0) {
+		fprintf(stderr, "Wrong vsock reply length %d\n", len);
+		free(answer);
+		return -1;
+	}
+
+	parse_rtattr(tb, VSOCK_A_MAX, (void *)ghdr + GENL_HDRLEN, len);
+
+	new_json_obj(json);
+	open_json_object(NULL);
+	print_string(PRINT_ANY, "dev", "%s", "vsock");
+	if (tb[VSOCK_A_NETNS_ID])
+		vsock_print_netnsid(rta_getattr_s32(tb[VSOCK_A_NETNS_ID]));
+	print_nl();
+	close_json_object();
+	delete_json_obj();
+
+	free(answer);
+
+	return 0;
+}
+
+static int vsock_set(int argc, char **argv)
+{
+	const char *netns = NULL;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "netns") == 0) {
+			NEXT_ARG();
+			if (netns)
+				duparg("netns", *argv);
+			netns = *argv;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			invarg("unknown argument", *argv);
+		}
+		argc--;
+		argv++;
+	}
+
+	if (!netns) {
+		fprintf(stderr, "Not enough information: \"netns\" argument is required.\n");
+		exit(-1);
+	}
+
+	return vsock_dev_netns_set(netns);
+}
+
+int do_ipvsock(int argc, char **argv)
+{
+	if (argc < 1)
+		return vsock_dev_netns_show();
+
+	if (matches(*argv, "show") == 0 ||
+	    matches(*argv, "list") == 0) {
+		if (argc > 1)
+			invarg("unknown argument", argv[1]);
+		return vsock_dev_netns_show();
+	}
+
+	if (matches(*argv, "set") == 0)
+		return vsock_set(argc - 1, argv + 1);
+
+	if (matches(*argv, "help") == 0)
+		usage();
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip vsock help\".\n",
+		*argv);
+	exit(-1);
+}
diff --git a/man/man8/ip-vsock.8 b/man/man8/ip-vsock.8
new file mode 100644
index 000000000000..18e618241523
--- /dev/null
+++ b/man/man8/ip-vsock.8
@@ -0,0 +1,121 @@
+.TH IP\-VSOCK 8 "5 Nov 2026" "iproute2" "Linux"
+.SH "NAME"
+ip-vsock \- guest vsock device network namespace management
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B vsock
+.RI "{ " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.B ip vsock set netns
+.RI "{ " PID " | " NETNSNAME " | " NETNSFILE " }"
+
+.ti -8
+.B ip vsock show
+
+.SH DESCRIPTION
+The
+.B ip vsock
+command manages the network namespace of the guest's vsock device, the one
+owned by the guest-to-host transport. That device is not a network device, so
+it cannot be moved with
+.BR "ip link set" " ... " netns .
+Instead,
+.B ip vsock
+uses the
+.B vsock
+generic netlink family.
+
+.SS ip vsock set netns - move the guest vsock device to a network namespace
+
+Move the device to the network namespace associated with process
+.IR "PID " or
+the name
+.IR "NETNSNAME " or
+the file
+.IR "NETNSFILE" .
+This is carried out with the
+.B VSOCK_CMD_DEV_NETNS_SET
+command, which the kernel serves in the namespace the request is sent from, so
+.B ip vsock
+sends it from inside the destination namespace.
+
+The vsock mode of the destination namespace, reported by
+.IR /proc/sys/net/vsock/ns_mode " and inherited from the creating namespace's"
+.IR /proc/sys/net/vsock/child_ns_mode ,
+then decides who may use the device. A
+.B global
+mode namespace shares it with every other global mode namespace, while a
+.B local
+mode namespace reserves the connection to the host for itself.
+
+The device starts out in the initial namespace, so moving it back there is how
+an assignment is undone. An assignment keeps the namespace alive, so deleting
+the namespace does not move the device. Connections made before the move, from
+a namespace that can no longer reach the device, are reset.
+
+Support is transport dependent: a guest-to-host transport that does not
+implement the move refuses the request with
+.BR EOPNOTSUPP ,
+as does a loaded host-to-guest transport that cannot check the device's CID.
+The move is refused with
+.B EADDRINUSE
+if a guest in the destination namespace already uses the device's CID. The
+command requires
+.B CAP_NET_ADMIN
+in the initial user namespace.
+
+.SS ip vsock show - show the network namespace of the guest vsock device
+
+Show the network namespace the device is in, relative to the current one,
+using the
+.B VSOCK_CMD_DEV_NETNS_GET
+command. This is also what
+.B ip vsock
+does when given no command. Nothing follows
+.B vsock
+when the device is in the current namespace. Otherwise the namespace is printed
+as
+.BI "netns " NETNSNAME
+if it has a name, or as
+.BI "netnsid " ID
+if it does not. When the current namespace cannot reach the device, or no nsid
+can be allocated for the device's namespace, the kernel does not say where the
+device is, and
+.B netnsid unknown
+is printed instead.
+
+.SH EXAMPLES
+.PP
+ip netns add guest
+.br
+ip vsock set netns guest
+.RS 4
+Move the guest vsock device to the network namespace "guest".
+.RE
+.PP
+ip vsock show
+.RS 4
+Show which network namespace the guest vsock device is in.
+.RE
+.PP
+ip vsock set netns 1
+.RS 4
+Move the device back to the initial network namespace, undoing the assignment.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8),
+.BR ip-netns (8),
+.BR vsock (7)
+
+.SH AUTHOR
+Bobby Eshleman <bobbyeshleman@meta.com>
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index fdae57c5f651..0f0a1560e2c3 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -24,7 +24,7 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels
  mroute " | " mrule " | " neighbor " | " neighbour " | " netconf " | "\
  netns " | " nexthop " | " ntable " | " ntbl " | " route " | " rule " | "\
  sr " | " tap " | " tcpmetrics " | " token " | " tunnel " | " tuntap " | "\
- vrf " | " xfrm " }"
+ vrf " | " vsock " | " xfrm " }"
 .sp
 
 .ti -8
@@ -356,6 +356,10 @@ Request the kernel to send the applied configuration back.
 .B vrf
 - manage virtual routing and forwarding devices.
 
+.TP
+.B vsock
+- manage the network namespace of the guest vsock device.
+
 .TP
 .B xfrm
 - manage IPSec policies.
@@ -463,6 +467,7 @@ was written by Alexey N. Kuznetsov and added in Linux 2.2.
 .BR ip-token (8),
 .BR ip-tunnel (8),
 .BR ip-vrf (8),
+.BR ip-vsock (8),
 .BR ip-xfrm (8)
 .br
 .RB "IP Command reference " ip-cref.ps

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns
  2026-11-05 19:30 ` [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
@ 2026-11-06  9:26   ` Stephen Hemminger
  2026-11-06  9:36   ` Stephen Hemminger
  1 sibling, 0 replies; 5+ messages in thread
From: Stephen Hemminger @ 2026-11-06  9:26 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: netdev, David Ahern, Stefano Garzarella, virtualization,
	Bobby Eshleman

On Thu, 5 Nov 2026 11:30:36 -0800
Bobby Eshleman <bobbyeshleman@meta.com> wrote:

> + * The guest's vsock device is not a netdev, so rtnetlink cannot move it.
> + * The "vsock" generic netlink family moves it with VSOCK_CMD_DEV_NETNS_SET
> + * and reports where it is with VSOCK_CMD_DEV_NETNS_GET. There is nothing to
> + * name in a request: the kernel serves it in the namespace of the socket it
> + * arrived on, so the destination is chosen by sending it from there, and the
> + * reported namespace is an nsid relative to the sender.
> + */

Please be more concise in comments, AI seems to think it gets paid by word.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns
  2026-11-05 19:30 ` [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
  2026-11-06  9:26   ` Stephen Hemminger
@ 2026-11-06  9:36   ` Stephen Hemminger
  1 sibling, 0 replies; 5+ messages in thread
From: Stephen Hemminger @ 2026-11-06  9:36 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: netdev, David Ahern, Stefano Garzarella, virtualization,
	Bobby Eshleman

On Thu, 5 Nov 2026 11:30:36 -0800
Bobby Eshleman <bobbyeshleman@meta.com> wrote:

> The guest's vsock device is not a netdev, so rtnetlink cannot move it
> between network namespaces. Add an "ip vsock" object that goes through
> the "vsock" generic netlink family instead:
> 
>   ip vsock set netns { PID | NETNSNAME | NETNSFILE }
>   ip vsock show
> 
> "set" issues VSOCK_CMD_DEV_NETNS_SET from inside the target namespace,
> since the kernel moves the device to the namespace the request was sent
> from. "show" issues VSOCK_CMD_DEV_NETNS_GET and prints where the device
> is, relative to the current namespace.
> 
> Link: https://lore.kernel.org/all/20261103-vsock-guest-ns-v5-0-48179b9b66b8@meta.com/
> Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
> ---

Semi-automated review using review-prompts for iproute2


On Thu, 5 Nov 2026 11:30:36 -0800, Bobby Eshleman wrote:

Thanks, this is mostly in good shape. A few things to fix before it
goes beyond RFC.

> +#include <linux/vsock_netlink.h>

This depends on a uapi header (and kernel series) that is not yet
merged. iproute2-next can only take this once the kernel side is in
net-next; the header update should then come from the normal uapi
sync rather than a hand-added copy in patch 1/2.

> +static int vsock_talk(__u8 cmd, struct nlmsghdr **answer)
> +{
> +	GENL_REQUEST(req, 128, 0, 0, VSOCK_FAMILY_VERSION, cmd, NLM_F_REQUEST);
> +	struct rtnl_handle grth = { .fd = -1 };
> +	int family = -1;
> +	int ret;
> +
> +	if (genl_init_handle(&grth, VSOCK_FAMILY_NAME, &family)) {
> +		fprintf(stderr,
> +			"Cannot resolve the \"%s\" netlink family, the kernel may not support moving the vsock device.\n",
> +			VSOCK_FAMILY_NAME);
> +		return -1;
> +	}

genl_init_handle() leaves the socket open when genl_resolve_family()
fails, so this leaks grth.fd. Harmless today since ip exits, but not
under "ip -batch". Add rtnl_close(&grth) on this path.

Also genl_resolve_family() already prints "Error talking to the kernel"
on failure, so the user gets two messages. Not a big deal, just be aware.

> +static void vsock_print_netnsid(int id)
> +{
> +	char *name;
> +
> +	if (is_json_context()) {
> +		print_int(PRINT_JSON, "netnsid", NULL, id);
> +		return;
> +	}

This copies the IFLA_LINK_NETNSID pattern from ipaddress.c, which is
fine for consistency, but note that JSON users never get the netns
name and get -1 for "unknown". Consider also emitting
print_string(PRINT_JSON, "netns", NULL, name) when a name exists.

> +int do_ipvsock(int argc, char **argv)
> +{
> +	if (argc < 1)
> +		return vsock_dev_netns_show();
> +
> +	if (matches(*argv, "show") == 0 ||
> +	    matches(*argv, "list") == 0) {
...
> +	if (matches(*argv, "set") == 0)
...
> +	if (matches(*argv, "help") == 0)
> +		usage();

and in vsock_set():

> +		} else if (matches(*argv, "help") == 0) {

New code must use strcmp(), not matches(). Abbreviation matching has
caused repeated breakage when keywords are added later (e.g. "s" here
already matches both "show" and "set"; order decides which wins).
Please convert all of these to strcmp().

> +	ret = rtnl_talk(&grth, &req.n, answer);
> +	rtnl_close(&grth);
> +
> +	return ret < 0 ? -2 : 0;

The -2 propagates out as the exit status of ip (254). Other ip
subcommands return -1/1 on failure; either is fine but please be
consistent with vsock_dev_netns_set() which returns -1 for its own
errors and -2 for a kernel error.

> +		"Usage:	ip vsock set netns { PID | NETNSNAME | NETNSFILE }\n"
> +		"	ip vsock show\n");

Literal tabs in the usage string; the rest of ip uses spaces for
alignment here ("Usage: ip vsock ...\n       ip vsock show\n").
Also "ip vsock help" should be listed.

> +static int vsock_dev_netns_set(const char *netns)
> +{
...
> +	if (setns(netns_fd, CLONE_NEWNET) < 0) {

Design note, more for the kernel side than this patch: because
VSOCK_CMD_DEV_NETNS_SET has no target attribute, userspace has to do a
setns() dance, and "ip -n foo vsock set netns bar" ends up depending on
which namespace the genl socket was opened in. rtnetlink and devlink
both take an explicit NETNS_FD / NETNS_PID / NETNS_ID attribute for
this; doing the same for vsock would make the tool trivial and avoid
needing setns() (and the privileges to enter the target ns) at all.
Since this is RFC on both sides it seems worth reconsidering now.

If the kernel API stays as is, the setns/restore logic looks correct.

> +ip vsock set netns 1
> +.RS 4
> +Move the device back to the initial network namespace, undoing the assignment.

That only holds if ip is run in the initial pid namespace; "1" is a
PID, not "the initial netns". Maybe say so, or use /proc/1/ns/net.

Minor: the man page says "Nothing follows vsock when the device is in
the current namespace" -- worth also showing an example of the
"netnsid unknown" output so users know what it means.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-11-06  9:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 19:30 [PATCH RFC iproute2-next 0/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
2026-11-05 19:30 ` [PATCH RFC iproute2-next 1/2] uapi: add vsock_netlink.h Bobby Eshleman
2026-11-05 19:30 ` [PATCH RFC iproute2-next 2/2] ip: add "ip vsock" to move the guest vsock device to a netns Bobby Eshleman
2026-11-06  9:26   ` Stephen Hemminger
2026-11-06  9:36   ` Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox