From nobody Sun Feb 8 19:39:56 2026 Received: from mail-qv1-f42.google.com (mail-qv1-f42.google.com [209.85.219.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CF6C2DCF7B for ; Fri, 16 Jan 2026 23:27:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768606061; cv=none; b=LDL38Uy6z3rJ/fZAiLQgUhTFc9hA6BscJ/cro6ny7vh5xXXRXRNLdejWEmsnvx1NO5Qswa138AcpG45GAXRciuqyCFpC/MkGGIsJSdVE7S+nNF8aDc9l+pwr6r7B8tz3A3lkkw1So46cv6KXraXGLi+fv2RK6jqPItY63eNXNnM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768606061; c=relaxed/simple; bh=iWhx534wqtQNl1nxF2sFcEqz8ZHOjdK0bgb8yXgcfu8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oWpcvAKXulNl+14O+mXx9C7qJrbVzvlkaha+TW+WTQgL/zrEHU/ZD1V5pDuPRvu8R3qHctiLWA384QKwUiotDZERrgPbqdIhCXQnNdgmcHusBapLXwfO3H6AVAfBfODFLJmrjCuJ5NnwRPm4Q9dmFDOAZ9ChWiQJuDxjsuyGlMk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DEE6fW4N; arc=none smtp.client-ip=209.85.219.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DEE6fW4N" Received: by mail-qv1-f42.google.com with SMTP id 6a1803df08f44-8888546d570so32646256d6.2 for ; Fri, 16 Jan 2026 15:27:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1768606058; x=1769210858; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=Bz5SQh4ZUAGs5FOQH9D0Ss9v/z0FeDQKGW44xhNAZ6Q=; b=DEE6fW4NaSM3X8HD3iYqMMMFgAKgGH8C0BdFvydD69mdJIiRA576hpZvf1dEQ0iDmY gS+ZcISAWnAcd3fD9vE9c6fGAZWGH16WogiAmE9jQKR09dVtsojwbPcCsrDPfHeyMcXm pwmLSrSoDcuw5I2v+oU30NWEz3VaXXe+mHNiLykeHgDdPysf1ru+6dbpDn2bzULB32V8 9BaCRuHJqvL904nkY4Nthllr+/cL6iEMciw/Nkx/KR+shlWB3kpBQ5HHG9UAxQz19QJH BsvUoTKop8T6d9gpGGPS85F97+M53ELpa0vu/98fYY/TPJTpUbx33sp5fqtv4mZ03pdD XA7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768606058; x=1769210858; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=Bz5SQh4ZUAGs5FOQH9D0Ss9v/z0FeDQKGW44xhNAZ6Q=; b=JNi7ToMxaHPMJScsKF9hV35mT5lu3Wb7t5aZuChGCh+QzVHFwgwQsYm6aXjrhxZxIf B+UdjCZdL4sLI98/UFeS6uzbOHHloOkhMxz3L4vI6nqvVbHj2yTPL0RSs7M3W8Oi1kwL Abu9zEJQw/ZClB8x6+TcWxrbiji5dt+Vag1f5toj8Xrh01Um5E2c16v71igvnn4qkDQe qLqgNq8TtWVD700vmu8pLZjKczEAS4WkEL1hZOpxPg4XrFucA+mu4ZQsFBkTSFhJIChq Ac9Eis4hukFspKHYZwJEvf4zq5/ubO7KW8Z12hhicEeqP6llm4HOpAGTbunmRS+CHIY4 zsGQ== X-Gm-Message-State: AOJu0Yzj7APtCh50PzcV3AwoJu8YbY0txNCKtV88KKM3Mhq7DmUIex9J Rr/Z2FsLfCXjmFijZl56cGQ42Ouy5cdyWjH+xXT4sNhzNrXrLnnqAeok X-Gm-Gg: AY/fxX7z8ljMrdmVKHDVPc4LH0ovmyGJmkKoz/XTQvwMgRYAp2c2HZK5Cty+eCK2wNl fnwHO54ZY0KqRxJ8IsHxxccdKc9qNJYY5W0rfMxPTa9uHlvk2d2P0csf9VxPxlzc/FHb9ejywD9 hnsCK7BV5A29lwrecy3oM6Ngc8/T8xKWmYIoOjw2+pp99mhlya/SPfUJ5t5B0wwHxrmPrn2kUHd eh+5P2QZTntlRJxaedv+702JIyO6uG6Titil0cK+v2NeL+W9GbZ+fCBeA36Z3Xgz7xFIyIoIKZR LiKRTLBKoJf+lP4AXgPRfBVsYiqvvEJY1do4W9ITsIGBz5kvG/I5dJr3P+foJh5S8gdv38RK7j2 +y4N7uFP5DXJ+hI5X0y43IfU1JyUAXEEkl6ACGkdPMtU/8MOAjKJghiRKjD+CZqx+gE+xa0dorR TYUtyACAjH X-Received: by 2002:a05:690c:660d:b0:78f:f362:ec4b with SMTP id 00721157ae682-793c67e8f8emr30304037b3.36.1768598943673; Fri, 16 Jan 2026 13:29:03 -0800 (PST) Received: from localhost ([2a03:2880:25ff:9::]) by smtp.gmail.com with ESMTPSA id 00721157ae682-793c66c708fsm13466837b3.3.2026.01.16.13.29.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Jan 2026 13:29:03 -0800 (PST) From: Bobby Eshleman Date: Fri, 16 Jan 2026 13:28:41 -0800 Subject: [PATCH net-next v15 01/12] vsock: add netns to vsock core Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260116-vsock-vmtest-v15-1-bbfd1a668548@meta.com> References: <20260116-vsock-vmtest-v15-0-bbfd1a668548@meta.com> In-Reply-To: <20260116-vsock-vmtest-v15-0-bbfd1a668548@meta.com> To: Stefano Garzarella , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Stefan Hajnoczi , "Michael S. Tsirkin" , Jason Wang , =?utf-8?q?Eugenio_P=C3=A9rez?= , Xuan Zhuo , "K. Y. Srinivasan" , Haiyang Zhang , Wei Liu , Dexuan Cui , Bryan Tan , Vishnu Dasa , Broadcom internal kernel review list , Shuah Khan , Long Li , Jonathan Corbet Cc: linux-kernel@vger.kernel.org, virtualization@lists.linux.dev, netdev@vger.kernel.org, kvm@vger.kernel.org, linux-hyperv@vger.kernel.org, linux-kselftest@vger.kernel.org, berrange@redhat.com, Sargun Dhillon , linux-doc@vger.kernel.org, Bobby Eshleman , Bobby Eshleman X-Mailer: b4 0.14.3 From: Bobby Eshleman Add netns logic to vsock core. Additionally, modify transport hook prototypes to be used by later transport-specific patches (e.g., *_seqpacket_allow()). Namespaces are supported primarily by changing socket lookup functions (e.g., vsock_find_connected_socket()) to take into account the socket namespace and the namespace mode before considering a candidate socket a "match". This patch also introduces the sysctl /proc/sys/net/vsock/ns_mode to report the mode and /proc/sys/net/vsock/child_ns_mode to set the mode for new namespaces. Add netns functionality (initialization, passing to transports, procfs, etc...) to the af_vsock socket layer. Later patches that add netns support to transports depend on this patch. dgram_allow(), stream_allow(), and seqpacket_allow() callbacks are modified to take a vsk in order to perform logic on namespace modes. In future patches, the net will also be used for socket lookups in these functions. Signed-off-by: Bobby Eshleman Reviewed-by: Stefano Garzarella --- Changes in v15: - make static port in __vsock_bind_connectible per-netns - remove __net_initdata because we want the ops beyond just boot - add vsock_init_ns_mode kernel cmdline parameter to set init ns mode - use if (ret || !write) in __vsock_net_mode_string() (Stefano) - add vsock_net_mode_global() (Stefano) - hide !net =3D=3D VSOCK_NET_MODE_GLOBAL inside vsock_net_mode() (Stefano) - clarify af_vsock.c comments on ns_mode/child_ns_mode (Stefano) Changes in v14: - include linux/sysctl.h in af_vsock.c - squash patch 'vsock: add per-net vsock NS mode state' into this patch (prior version can be found here): https://lore.kernel.org/all/20251223-vsock-vmtest-v13-1-9d6db8e7c80b@meta= .com/) Changes in v13: - remove net_mode and replace with direct accesses to net->vsock.mode, since this is now immutable. - update comments about mode behavior and mutability, and sysctl API - only pass NULL for net when wanting global, instead of net_mode =3D=3D VSOCK_NET_MODE_GLOBAL. This reflects the new logic of vsock_net_check_mode() that only requires net pointers (not net_mode). - refactor sysctl string code into a re-usable function, because child_ns_mode and ns_mode both handle the same strings. - remove redundant vsock_net_init(&init_net) call in module init because pernet registration calls the callback on the init_net too Changes in v12: - return true in dgram_allow(), stream_allow(), and seqpacket_allow() only if net_mode =3D=3D VSOCK_NET_MODE_GLOBAL (Stefano) - document bind(VMADDR_CID_ANY) case in af_vsock.c (Stefano) - change order of stream_allow() call in vmci so we can pass vsk to it Changes in v10: - add file-level comment about what happens to sockets/devices when the namespace mode changes (Stefano) - change the 'if (write)' boolean in vsock_net_mode_string() to if (!write), this simplifies a later patch which adds "goto" for mutex unlocking on function exit. Changes in v9: - remove virtio_vsock_alloc_rx_skb() (Stefano) - remove vsock_global_dummy_net, not needed as net=3DNULL + net_mode=3DVSOCK_NET_MODE_GLOBAL achieves identical result Changes in v7: - hv_sock: fix hyperv build error - explain why vhost does not use the dummy - explain usage of __vsock_global_dummy_net - explain why VSOCK_NET_MODE_STR_MAX is 8 characters - use switch-case in vsock_net_mode_string() - avoid changing transports as much as possible - add vsock_find_{bound,connected}_socket_net() - rename `vsock_hdr` to `sysctl_hdr` - add virtio_vsock_alloc_linear_skb() wrapper for setting dummy net and global mode for virtio-vsock, move skb->cb zero-ing into wrapper - explain seqpacket_allow() change - move net setting to __vsock_create() instead of vsock_create() so that child sockets also have their net assigned upon accept() Changes in v6: - unregister sysctl ops in vsock_exit() - af_vsock: clarify description of CID behavior - af_vsock: fix buf vs buffer naming, and length checking - af_vsock: fix length checking w/ correct ctl_table->maxlen Changes in v5: - vsock_global_net() -> vsock_global_dummy_net() - update comments for new uAPI - use /proc/sys/net/vsock/ns_mode instead of /proc/net/vsock_ns_mode - add prototype changes so patch remains compilable --- Documentation/admin-guide/kernel-parameters.txt | 14 + MAINTAINERS | 1 + drivers/vhost/vsock.c | 6 +- include/linux/virtio_vsock.h | 4 +- include/net/af_vsock.h | 61 ++++- include/net/net_namespace.h | 4 + include/net/netns/vsock.h | 21 ++ net/vmw_vsock/af_vsock.c | 328 ++++++++++++++++++++= ++-- net/vmw_vsock/hyperv_transport.c | 7 +- net/vmw_vsock/virtio_transport.c | 9 +- net/vmw_vsock/virtio_transport_common.c | 6 +- net/vmw_vsock/vmci_transport.c | 26 +- net/vmw_vsock/vsock_loopback.c | 8 +- 13 files changed, 444 insertions(+), 51 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentatio= n/admin-guide/kernel-parameters.txt index a8d0afde7f85..b6e3bfe365a1 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -8253,6 +8253,20 @@ Kernel parameters them quite hard to use for exploits but might break your system. =20 + vsock_init_ns_mode=3D + [KNL,NET] Set the vsock namespace mode for the init + (root) network namespace. + + global [default] The init namespace operates in + global mode where CIDs are system-wide and + sockets can communicate across global + namespaces. + + local The init namespace operates in local mode + where CIDs are private to the namespace and + sockets can only communicate within the same + namespace. + vt.color=3D [VT] Default text color. Format: 0xYX, X =3D foreground, Y =3D background. Default: 0x07 =3D light gray on black. diff --git a/MAINTAINERS b/MAINTAINERS index afc71089ba09..c48a2e047686 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27557,6 +27557,7 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/vhost/vsock.c F: include/linux/virtio_vsock.h +F: include/net/netns/vsock.h F: include/uapi/linux/virtio_vsock.h F: net/vmw_vsock/virtio_transport.c F: net/vmw_vsock/virtio_transport_common.c diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index 552cfb53498a..647ded6f6ea5 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -407,7 +407,8 @@ static bool vhost_transport_msgzerocopy_allow(void) return true; } =20 -static bool vhost_transport_seqpacket_allow(u32 remote_cid); +static bool vhost_transport_seqpacket_allow(struct vsock_sock *vsk, + u32 remote_cid); =20 static struct virtio_transport vhost_transport =3D { .transport =3D { @@ -463,7 +464,8 @@ static struct virtio_transport vhost_transport =3D { .send_pkt =3D vhost_transport_send_pkt, }; =20 -static bool vhost_transport_seqpacket_allow(u32 remote_cid) +static bool vhost_transport_seqpacket_allow(struct vsock_sock *vsk, + u32 remote_cid) { struct vhost_vsock *vsock; bool seqpacket_allow =3D false; diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h index 0c67543a45c8..1845e8d4f78d 100644 --- a/include/linux/virtio_vsock.h +++ b/include/linux/virtio_vsock.h @@ -256,10 +256,10 @@ void virtio_transport_notify_buffer_size(struct vsock= _sock *vsk, u64 *val); =20 u64 virtio_transport_stream_rcvhiwat(struct vsock_sock *vsk); bool virtio_transport_stream_is_active(struct vsock_sock *vsk); -bool virtio_transport_stream_allow(u32 cid, u32 port); +bool virtio_transport_stream_allow(struct vsock_sock *vsk, u32 cid, u32 po= rt); int virtio_transport_dgram_bind(struct vsock_sock *vsk, struct sockaddr_vm *addr); -bool virtio_transport_dgram_allow(u32 cid, u32 port); +bool virtio_transport_dgram_allow(struct vsock_sock *vsk, u32 cid, u32 por= t); =20 int virtio_transport_connect(struct vsock_sock *vsk); =20 diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index d40e978126e3..d3ff48a2fbe0 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -10,6 +10,7 @@ =20 #include #include +#include #include #include =20 @@ -124,7 +125,7 @@ struct vsock_transport { size_t len, int flags); int (*dgram_enqueue)(struct vsock_sock *, struct sockaddr_vm *, struct msghdr *, size_t len); - bool (*dgram_allow)(u32 cid, u32 port); + bool (*dgram_allow)(struct vsock_sock *vsk, u32 cid, u32 port); =20 /* STREAM. */ /* TODO: stream_bind() */ @@ -136,14 +137,14 @@ struct vsock_transport { s64 (*stream_has_space)(struct vsock_sock *); u64 (*stream_rcvhiwat)(struct vsock_sock *); bool (*stream_is_active)(struct vsock_sock *); - bool (*stream_allow)(u32 cid, u32 port); + bool (*stream_allow)(struct vsock_sock *vsk, u32 cid, u32 port); =20 /* SEQ_PACKET. */ ssize_t (*seqpacket_dequeue)(struct vsock_sock *vsk, struct msghdr *msg, int flags); int (*seqpacket_enqueue)(struct vsock_sock *vsk, struct msghdr *msg, size_t len); - bool (*seqpacket_allow)(u32 remote_cid); + bool (*seqpacket_allow)(struct vsock_sock *vsk, u32 remote_cid); u32 (*seqpacket_has_data)(struct vsock_sock *vsk); =20 /* Notification. */ @@ -216,6 +217,11 @@ void vsock_remove_connected(struct vsock_sock *vsk); struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr); struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, struct sockaddr_vm *dst); +struct sock *vsock_find_bound_socket_net(struct sockaddr_vm *addr, + struct net *net); +struct sock *vsock_find_connected_socket_net(struct sockaddr_vm *src, + struct sockaddr_vm *dst, + struct net *net); void vsock_remove_sock(struct vsock_sock *vsk); void vsock_for_each_connected_socket(struct vsock_transport *transport, void (*fn)(struct sock *sk)); @@ -256,4 +262,53 @@ static inline bool vsock_msgzerocopy_allow(const struc= t vsock_transport *t) { return t->msgzerocopy_allow && t->msgzerocopy_allow(); } + +static inline enum vsock_net_mode vsock_net_mode(struct net *net) +{ + if (!net) + return VSOCK_NET_MODE_GLOBAL; + + return READ_ONCE(net->vsock.mode); +} + +static inline bool vsock_net_mode_global(struct vsock_sock *vsk) +{ + return vsock_net_mode(sock_net(sk_vsock(vsk))) =3D=3D VSOCK_NET_MODE_GLOB= AL; +} + +static inline void vsock_net_set_child_mode(struct net *net, + enum vsock_net_mode mode) +{ + WRITE_ONCE(net->vsock.child_ns_mode, mode); +} + +static inline enum vsock_net_mode vsock_net_child_mode(struct net *net) +{ + return READ_ONCE(net->vsock.child_ns_mode); +} + +/* Return true if two namespaces pass the mode rules. Otherwise, return fa= lse. + * + * A NULL namespace is treated as VSOCK_NET_MODE_GLOBAL. + * + * Read more about modes in the comment header of net/vmw_vsock/af_vsock.c. + */ +static inline bool vsock_net_check_mode(struct net *ns0, struct net *ns1) +{ + enum vsock_net_mode mode0, mode1; + + /* Any vsocks within the same network namespace are always reachable, + * regardless of the mode. + */ + if (net_eq(ns0, ns1)) + return true; + + mode0 =3D vsock_net_mode(ns0); + mode1 =3D vsock_net_mode(ns1); + + /* Different namespaces are only reachable if they are both + * global mode. + */ + return mode0 =3D=3D VSOCK_NET_MODE_GLOBAL && mode0 =3D=3D mode1; +} #endif /* __AF_VSOCK_H__ */ diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index cb664f6e3558..66d3de1d935f 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,9 @@ struct net { /* Move to a better place when the config guard is removed. */ struct mutex rtnl_mutex; #endif +#if IS_ENABLED(CONFIG_VSOCKETS) + struct netns_vsock vsock; +#endif } __randomize_layout; =20 #include diff --git a/include/net/netns/vsock.h b/include/net/netns/vsock.h new file mode 100644 index 000000000000..b34d69a22fa8 --- /dev/null +++ b/include/net/netns/vsock.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __NET_NET_NAMESPACE_VSOCK_H +#define __NET_NET_NAMESPACE_VSOCK_H + +#include + +enum vsock_net_mode { + VSOCK_NET_MODE_GLOBAL, + VSOCK_NET_MODE_LOCAL, +}; + +struct netns_vsock { + struct ctl_table_header *sysctl_hdr; + + /* protected by the vsock_table_lock in af_vsock.c */ + u32 port; + + enum vsock_net_mode mode; + enum vsock_net_mode child_ns_mode; +}; +#endif /* __NET_NET_NAMESPACE_VSOCK_H */ diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index a3505a4dcee0..3fc8160d51df 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -83,6 +83,48 @@ * TCP_ESTABLISHED - connected * TCP_CLOSING - disconnecting * TCP_LISTEN - listening + * + * - Namespaces in vsock support two different modes: "local" and "global". + * Each mode defines how the namespace interacts with CIDs. + * Each namespace exposes two sysctl files: + * + * - /proc/sys/net/vsock/ns_mode (read-only) reports the current namespa= ce's + * mode, which is set at namespace creation and immutable thereafter. + * - /proc/sys/net/vsock/child_ns_mode (writable) controls what mode fut= ure + * child namespaces will inherit when created. The default is "global". + * + * Changing child_ns_mode only affects newly created namespaces, not the + * current namespace or existing children. At namespace creation, ns_mode + * is inherited from the parent's child_ns_mode. + * + * The modes affect the allocation and accessibility of CIDs as follows: + * + * - global - access and allocation are all system-wide + * - all CID allocation from global namespaces draw from the same + * system-wide pool. + * - if one global namespace has already allocated some CID, another + * global namespace will not be able to allocate the same CID. + * - global mode AF_VSOCK sockets can reach any VM or socket in any g= lobal + * namespace, they are not contained to only their own namespace. + * - AF_VSOCK sockets in a global mode namespace cannot reach VMs or + * sockets in any local mode namespace. + * - local - access and allocation are contained within the namespace + * - CID allocation draws only from a private pool local only to the + * namespace, and does not affect the CIDs available for allocation = in any + * other namespace (global or local). + * - VMs in a local namespace do not collide with CIDs in any other lo= cal + * namespace or any global namespace. For example, if a VM in a loca= l mode + * namespace is given CID 10, then CID 10 is still available for + * allocation in any other namespace, but not in the same namespace. + * - AF_VSOCK sockets in a local mode namespace can connect only to VM= s or + * other sockets within their own namespace. + * - sockets bound to VMADDR_CID_ANY in local namespaces will never re= solve + * to any transport that is not compatible with local mode. There is= no + * error that propagates to the user (as there is for connection att= empts) + * because it is possible for some packet to reach this socket from + * a different transport that *does* support local mode. For + * example, virtio-vsock may not support local mode, but the socket + * may still accept a connection from vhost-vsock which does. */ =20 #include @@ -100,20 +142,31 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include =20 +#define VSOCK_NET_MODE_STR_GLOBAL "global" +#define VSOCK_NET_MODE_STR_LOCAL "local" + +/* 6 chars for "global", 1 for null-terminator, and 1 more for '\n'. + * The newline is added by proc_dostring() for read operations. + */ +#define VSOCK_NET_MODE_STR_MAX 8 + static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr); static void vsock_sk_destruct(struct sock *sk); static int vsock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); @@ -149,6 +202,21 @@ static const struct vsock_transport *transport_dgram; static const struct vsock_transport *transport_local; static DEFINE_MUTEX(vsock_register_mutex); =20 +static enum vsock_net_mode vsock_init_ns_mode =3D VSOCK_NET_MODE_GLOBAL; + +#ifndef MODULE +static int __init vsock_init_ns_mode_setup(char *str) +{ + if (!strcmp(str, VSOCK_NET_MODE_STR_LOCAL)) + vsock_init_ns_mode =3D VSOCK_NET_MODE_LOCAL; + else if (!strcmp(str, VSOCK_NET_MODE_STR_GLOBAL)) + vsock_init_ns_mode =3D VSOCK_NET_MODE_GLOBAL; + + return 1; +} +__setup("vsock_init_ns_mode=3D", vsock_init_ns_mode_setup); +#endif + /**** UTILS ****/ =20 /* Each bound VSocket is stored in the bind hash table and each connected @@ -235,33 +303,42 @@ static void __vsock_remove_connected(struct vsock_soc= k *vsk) sock_put(&vsk->sk); } =20 -static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr) +static struct sock *__vsock_find_bound_socket_net(struct sockaddr_vm *addr, + struct net *net) { struct vsock_sock *vsk; =20 list_for_each_entry(vsk, vsock_bound_sockets(addr), bound_table) { - if (vsock_addr_equals_addr(addr, &vsk->local_addr)) - return sk_vsock(vsk); + struct sock *sk =3D sk_vsock(vsk); + + if (vsock_addr_equals_addr(addr, &vsk->local_addr) && + vsock_net_check_mode(sock_net(sk), net)) + return sk; =20 if (addr->svm_port =3D=3D vsk->local_addr.svm_port && (vsk->local_addr.svm_cid =3D=3D VMADDR_CID_ANY || - addr->svm_cid =3D=3D VMADDR_CID_ANY)) - return sk_vsock(vsk); + addr->svm_cid =3D=3D VMADDR_CID_ANY) && + vsock_net_check_mode(sock_net(sk), net)) + return sk; } =20 return NULL; } =20 -static struct sock *__vsock_find_connected_socket(struct sockaddr_vm *src, - struct sockaddr_vm *dst) +static struct sock * +__vsock_find_connected_socket_net(struct sockaddr_vm *src, + struct sockaddr_vm *dst, struct net *net) { struct vsock_sock *vsk; =20 list_for_each_entry(vsk, vsock_connected_sockets(src, dst), connected_table) { + struct sock *sk =3D sk_vsock(vsk); + if (vsock_addr_equals_addr(src, &vsk->remote_addr) && - dst->svm_port =3D=3D vsk->local_addr.svm_port) { - return sk_vsock(vsk); + dst->svm_port =3D=3D vsk->local_addr.svm_port && + vsock_net_check_mode(sock_net(sk), net)) { + return sk; } } =20 @@ -304,12 +381,13 @@ void vsock_remove_connected(struct vsock_sock *vsk) } EXPORT_SYMBOL_GPL(vsock_remove_connected); =20 -struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr) +struct sock *vsock_find_bound_socket_net(struct sockaddr_vm *addr, + struct net *net) { struct sock *sk; =20 spin_lock_bh(&vsock_table_lock); - sk =3D __vsock_find_bound_socket(addr); + sk =3D __vsock_find_bound_socket_net(addr, net); if (sk) sock_hold(sk); =20 @@ -317,15 +395,22 @@ struct sock *vsock_find_bound_socket(struct sockaddr_= vm *addr) =20 return sk; } +EXPORT_SYMBOL_GPL(vsock_find_bound_socket_net); + +struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr) +{ + return vsock_find_bound_socket_net(addr, NULL); +} EXPORT_SYMBOL_GPL(vsock_find_bound_socket); =20 -struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, - struct sockaddr_vm *dst) +struct sock *vsock_find_connected_socket_net(struct sockaddr_vm *src, + struct sockaddr_vm *dst, + struct net *net) { struct sock *sk; =20 spin_lock_bh(&vsock_table_lock); - sk =3D __vsock_find_connected_socket(src, dst); + sk =3D __vsock_find_connected_socket_net(src, dst, net); if (sk) sock_hold(sk); =20 @@ -333,6 +418,13 @@ struct sock *vsock_find_connected_socket(struct sockad= dr_vm *src, =20 return sk; } +EXPORT_SYMBOL_GPL(vsock_find_connected_socket_net); + +struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, + struct sockaddr_vm *dst) +{ + return vsock_find_connected_socket_net(src, dst, NULL); +} EXPORT_SYMBOL_GPL(vsock_find_connected_socket); =20 void vsock_remove_sock(struct vsock_sock *vsk) @@ -528,7 +620,7 @@ int vsock_assign_transport(struct vsock_sock *vsk, stru= ct vsock_sock *psk) =20 if (sk->sk_type =3D=3D SOCK_SEQPACKET) { if (!new_transport->seqpacket_allow || - !new_transport->seqpacket_allow(remote_cid)) { + !new_transport->seqpacket_allow(vsk, remote_cid)) { module_put(new_transport->module); return -ESOCKTNOSUPPORT; } @@ -676,11 +768,11 @@ static void vsock_pending_work(struct work_struct *wo= rk) static int __vsock_bind_connectible(struct vsock_sock *vsk, struct sockaddr_vm *addr) { - static u32 port; + struct net *net =3D sock_net(sk_vsock(vsk)); struct sockaddr_vm new_addr; =20 - if (!port) - port =3D get_random_u32_above(LAST_RESERVED_PORT); + if (!net->vsock.port) + net->vsock.port =3D get_random_u32_above(LAST_RESERVED_PORT); =20 vsock_addr_init(&new_addr, addr->svm_cid, addr->svm_port); =20 @@ -689,13 +781,13 @@ static int __vsock_bind_connectible(struct vsock_sock= *vsk, unsigned int i; =20 for (i =3D 0; i < MAX_PORT_RETRIES; i++) { - if (port =3D=3D VMADDR_PORT_ANY || - port <=3D LAST_RESERVED_PORT) - port =3D LAST_RESERVED_PORT + 1; + if (net->vsock.port =3D=3D VMADDR_PORT_ANY || + net->vsock.port <=3D LAST_RESERVED_PORT) + net->vsock.port =3D LAST_RESERVED_PORT + 1; =20 - new_addr.svm_port =3D port++; + new_addr.svm_port =3D net->vsock.port++; =20 - if (!__vsock_find_bound_socket(&new_addr)) { + if (!__vsock_find_bound_socket_net(&new_addr, net)) { found =3D true; break; } @@ -712,7 +804,7 @@ static int __vsock_bind_connectible(struct vsock_sock *= vsk, return -EACCES; } =20 - if (__vsock_find_bound_socket(&new_addr)) + if (__vsock_find_bound_socket_net(&new_addr, net)) return -EADDRINUSE; } =20 @@ -1314,7 +1406,7 @@ static int vsock_dgram_sendmsg(struct socket *sock, s= truct msghdr *msg, goto out; } =20 - if (!transport->dgram_allow(remote_addr->svm_cid, + if (!transport->dgram_allow(vsk, remote_addr->svm_cid, remote_addr->svm_port)) { err =3D -EINVAL; goto out; @@ -1355,7 +1447,7 @@ static int vsock_dgram_connect(struct socket *sock, if (err) goto out; =20 - if (!vsk->transport->dgram_allow(remote_addr->svm_cid, + if (!vsk->transport->dgram_allow(vsk, remote_addr->svm_cid, remote_addr->svm_port)) { err =3D -EINVAL; goto out; @@ -1585,7 +1677,7 @@ static int vsock_connect(struct socket *sock, struct = sockaddr_unsized *addr, * endpoints. */ if (!transport || - !transport->stream_allow(remote_addr->svm_cid, + !transport->stream_allow(vsk, remote_addr->svm_cid, remote_addr->svm_port)) { err =3D -ENETUNREACH; goto out; @@ -2662,6 +2754,180 @@ static struct miscdevice vsock_device =3D { .fops =3D &vsock_device_ops, }; =20 +static int __vsock_net_mode_string(const struct ctl_table *table, int writ= e, + void *buffer, size_t *lenp, loff_t *ppos, + enum vsock_net_mode mode, + enum vsock_net_mode *new_mode) +{ + char data[VSOCK_NET_MODE_STR_MAX] =3D {0}; + struct ctl_table tmp; + int ret; + + if (!table->data || !table->maxlen || !*lenp) { + *lenp =3D 0; + return 0; + } + + tmp =3D *table; + tmp.data =3D data; + + if (!write) { + const char *p; + + switch (mode) { + case VSOCK_NET_MODE_GLOBAL: + p =3D VSOCK_NET_MODE_STR_GLOBAL; + break; + case VSOCK_NET_MODE_LOCAL: + p =3D VSOCK_NET_MODE_STR_LOCAL; + break; + default: + WARN_ONCE(true, "netns has invalid vsock mode"); + *lenp =3D 0; + return 0; + } + + strscpy(data, p, sizeof(data)); + tmp.maxlen =3D strlen(p); + } + + ret =3D proc_dostring(&tmp, write, buffer, lenp, ppos); + if (ret || !write) + return ret; + + if (*lenp >=3D sizeof(data)) + return -EINVAL; + + if (!strncmp(data, VSOCK_NET_MODE_STR_GLOBAL, sizeof(data))) + *new_mode =3D VSOCK_NET_MODE_GLOBAL; + else if (!strncmp(data, VSOCK_NET_MODE_STR_LOCAL, sizeof(data))) + *new_mode =3D VSOCK_NET_MODE_LOCAL; + else + return -EINVAL; + + return 0; +} + +static int vsock_net_mode_string(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + struct net *net; + + if (write) + return -EPERM; + + net =3D current->nsproxy->net_ns; + + return __vsock_net_mode_string(table, write, buffer, lenp, ppos, + vsock_net_mode(net), NULL); +} + +static int vsock_net_child_mode_string(const struct ctl_table *table, int = write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + enum vsock_net_mode new_mode; + struct net *net; + int ret; + + net =3D current->nsproxy->net_ns; + + ret =3D __vsock_net_mode_string(table, write, buffer, lenp, ppos, + vsock_net_child_mode(net), &new_mode); + if (ret) + return ret; + + if (write) + vsock_net_set_child_mode(net, new_mode); + + return 0; +} + +static struct ctl_table vsock_table[] =3D { + { + .procname =3D "ns_mode", + .data =3D &init_net.vsock.mode, + .maxlen =3D VSOCK_NET_MODE_STR_MAX, + .mode =3D 0444, + .proc_handler =3D vsock_net_mode_string + }, + { + .procname =3D "child_ns_mode", + .data =3D &init_net.vsock.child_ns_mode, + .maxlen =3D VSOCK_NET_MODE_STR_MAX, + .mode =3D 0644, + .proc_handler =3D vsock_net_child_mode_string + }, +}; + +static int __net_init vsock_sysctl_register(struct net *net) +{ + struct ctl_table *table; + + if (net_eq(net, &init_net)) { + table =3D vsock_table; + } else { + table =3D kmemdup(vsock_table, sizeof(vsock_table), GFP_KERNEL); + if (!table) + goto err_alloc; + + table[0].data =3D &net->vsock.mode; + table[1].data =3D &net->vsock.child_ns_mode; + } + + net->vsock.sysctl_hdr =3D register_net_sysctl_sz(net, "net/vsock", table, + ARRAY_SIZE(vsock_table)); + if (!net->vsock.sysctl_hdr) + goto err_reg; + + return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(table); +err_alloc: + return -ENOMEM; +} + +static void vsock_sysctl_unregister(struct net *net) +{ + const struct ctl_table *table; + + table =3D net->vsock.sysctl_hdr->ctl_table_arg; + unregister_net_sysctl_table(net->vsock.sysctl_hdr); + if (!net_eq(net, &init_net)) + kfree(table); +} + +static void vsock_net_init(struct net *net) +{ + if (net_eq(net, &init_net)) + net->vsock.mode =3D vsock_init_ns_mode; + else + net->vsock.mode =3D vsock_net_child_mode(current->nsproxy->net_ns); + + net->vsock.child_ns_mode =3D VSOCK_NET_MODE_GLOBAL; +} + +static __net_init int vsock_sysctl_init_net(struct net *net) +{ + vsock_net_init(net); + + if (vsock_sysctl_register(net)) + return -ENOMEM; + + return 0; +} + +static __net_exit void vsock_sysctl_exit_net(struct net *net) +{ + vsock_sysctl_unregister(net); +} + +static struct pernet_operations vsock_sysctl_ops =3D { + .init =3D vsock_sysctl_init_net, + .exit =3D vsock_sysctl_exit_net, +}; + static int __init vsock_init(void) { int err =3D 0; @@ -2689,10 +2955,17 @@ static int __init vsock_init(void) goto err_unregister_proto; } =20 + if (register_pernet_subsys(&vsock_sysctl_ops)) { + err =3D -ENOMEM; + goto err_unregister_sock; + } + vsock_bpf_build_proto(); =20 return 0; =20 +err_unregister_sock: + sock_unregister(AF_VSOCK); err_unregister_proto: proto_unregister(&vsock_proto); err_deregister_misc: @@ -2706,6 +2979,7 @@ static void __exit vsock_exit(void) misc_deregister(&vsock_device); sock_unregister(AF_VSOCK); proto_unregister(&vsock_proto); + unregister_pernet_subsys(&vsock_sysctl_ops); } =20 const struct vsock_transport *vsock_core_get_transport(struct vsock_sock *= vsk) diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transp= ort.c index 432fcbbd14d4..c3010c874308 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -570,7 +570,7 @@ static int hvs_dgram_enqueue(struct vsock_sock *vsk, return -EOPNOTSUPP; } =20 -static bool hvs_dgram_allow(u32 cid, u32 port) +static bool hvs_dgram_allow(struct vsock_sock *vsk, u32 cid, u32 port) { return false; } @@ -745,8 +745,11 @@ static bool hvs_stream_is_active(struct vsock_sock *vs= k) return hvs->chan !=3D NULL; } =20 -static bool hvs_stream_allow(u32 cid, u32 port) +static bool hvs_stream_allow(struct vsock_sock *vsk, u32 cid, u32 port) { + if (!vsock_net_mode_global(vsk)) + return false; + if (cid =3D=3D VMADDR_CID_HOST) return true; =20 diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transp= ort.c index 8c867023a2e5..f0a9e51118f3 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -536,7 +536,8 @@ static bool virtio_transport_msgzerocopy_allow(void) return true; } =20 -static bool virtio_transport_seqpacket_allow(u32 remote_cid); +static bool virtio_transport_seqpacket_allow(struct vsock_sock *vsk, + u32 remote_cid); =20 static struct virtio_transport virtio_transport =3D { .transport =3D { @@ -593,11 +594,15 @@ static struct virtio_transport virtio_transport =3D { .can_msgzerocopy =3D virtio_transport_can_msgzerocopy, }; =20 -static bool virtio_transport_seqpacket_allow(u32 remote_cid) +static bool +virtio_transport_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid) { struct virtio_vsock *vsock; bool seqpacket_allow; =20 + if (!vsock_net_mode_global(vsk)) + return false; + seqpacket_allow =3D false; rcu_read_lock(); vsock =3D rcu_dereference(the_virtio_vsock); diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio= _transport_common.c index dcc8a1d5851e..fdb8f5b3fa60 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -1043,9 +1043,9 @@ bool virtio_transport_stream_is_active(struct vsock_s= ock *vsk) } EXPORT_SYMBOL_GPL(virtio_transport_stream_is_active); =20 -bool virtio_transport_stream_allow(u32 cid, u32 port) +bool virtio_transport_stream_allow(struct vsock_sock *vsk, u32 cid, u32 po= rt) { - return true; + return vsock_net_mode(sock_net(sk_vsock(vsk))) =3D=3D VSOCK_NET_MODE_GLOB= AL; } EXPORT_SYMBOL_GPL(virtio_transport_stream_allow); =20 @@ -1056,7 +1056,7 @@ int virtio_transport_dgram_bind(struct vsock_sock *vs= k, } EXPORT_SYMBOL_GPL(virtio_transport_dgram_bind); =20 -bool virtio_transport_dgram_allow(u32 cid, u32 port) +bool virtio_transport_dgram_allow(struct vsock_sock *vsk, u32 cid, u32 por= t) { return false; } diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 7eccd6708d66..00f6bbdb035a 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -646,13 +646,17 @@ static int vmci_transport_recv_dgram_cb(void *data, s= truct vmci_datagram *dg) return VMCI_SUCCESS; } =20 -static bool vmci_transport_stream_allow(u32 cid, u32 port) +static bool vmci_transport_stream_allow(struct vsock_sock *vsk, u32 cid, + u32 port) { static const u32 non_socket_contexts[] =3D { VMADDR_CID_LOCAL, }; int i; =20 + if (!vsock_net_mode_global(vsk)) + return false; + BUILD_BUG_ON(sizeof(cid) !=3D sizeof(*non_socket_contexts)); =20 for (i =3D 0; i < ARRAY_SIZE(non_socket_contexts); i++) { @@ -682,12 +686,10 @@ static int vmci_transport_recv_stream_cb(void *data, = struct vmci_datagram *dg) err =3D VMCI_SUCCESS; bh_process_pkt =3D false; =20 - /* Ignore incoming packets from contexts without sockets, or resources - * that aren't vsock implementations. + /* Ignore incoming packets from resources that aren't vsock + * implementations. */ - - if (!vmci_transport_stream_allow(dg->src.context, -1) - || vmci_transport_peer_rid(dg->src.context) !=3D dg->src.resource) + if (vmci_transport_peer_rid(dg->src.context) !=3D dg->src.resource) return VMCI_ERROR_NO_ACCESS; =20 if (VMCI_DG_SIZE(dg) < sizeof(*pkt)) @@ -749,6 +751,12 @@ static int vmci_transport_recv_stream_cb(void *data, s= truct vmci_datagram *dg) goto out; } =20 + /* Ignore incoming packets from contexts without sockets. */ + if (!vmci_transport_stream_allow(vsk, dg->src.context, -1)) { + err =3D VMCI_ERROR_NO_ACCESS; + goto out; + } + /* We do most everything in a work queue, but let's fast path the * notification of reads and writes to help data transfer performance. * We can only do this if there is no process context code executing @@ -1784,8 +1792,12 @@ static int vmci_transport_dgram_dequeue(struct vsock= _sock *vsk, return err; } =20 -static bool vmci_transport_dgram_allow(u32 cid, u32 port) +static bool vmci_transport_dgram_allow(struct vsock_sock *vsk, u32 cid, + u32 port) { + if (!vsock_net_mode_global(vsk)) + return false; + if (cid =3D=3D VMADDR_CID_HYPERVISOR) { /* Registrations of PBRPC Servers do not modify VMX/Hypervisor * state and are allowed. diff --git a/net/vmw_vsock/vsock_loopback.c b/net/vmw_vsock/vsock_loopback.c index bc2ff918b315..deff68c64a09 100644 --- a/net/vmw_vsock/vsock_loopback.c +++ b/net/vmw_vsock/vsock_loopback.c @@ -46,7 +46,8 @@ static int vsock_loopback_cancel_pkt(struct vsock_sock *v= sk) return 0; } =20 -static bool vsock_loopback_seqpacket_allow(u32 remote_cid); +static bool vsock_loopback_seqpacket_allow(struct vsock_sock *vsk, + u32 remote_cid); static bool vsock_loopback_msgzerocopy_allow(void) { return true; @@ -106,9 +107,10 @@ static struct virtio_transport loopback_transport =3D { .send_pkt =3D vsock_loopback_send_pkt, }; =20 -static bool vsock_loopback_seqpacket_allow(u32 remote_cid) +static bool +vsock_loopback_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid) { - return true; + return vsock_net_mode_global(vsk); } =20 static void vsock_loopback_work(struct work_struct *work) --=20 2.47.3