Linux USB
 help / color / mirror / Atom feed
* [PATCH] usbip: tools: add --sockfd option to usbip attach
@ 2026-10-21 21:48 Raul E Rangel
  2026-10-21 21:54 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Raul E Rangel @ 2026-10-21 21:48 UTC (permalink / raw)
  To: Valentina Manea, Shuah Khan, Greg Kroah-Hartman
  Cc: linux-kernel, linux-usb, Hongren Zheng, Raul E Rangel

'usbip attach' always opens its own TCP connection to --remote. A client
that reaches the USB/IP server some other way, for example over a UNIX
domain socket or a channel forwarded by a multiplexed SSH connection,
therefore has to expose a listening TCP port on the local host just so
usbip can connect back to it. Any local user can connect to that port
while it is open.

Add a '--sockfd <fd>' option (short option '-s') that attaches the
device over an inherited, already-connected stream socket instead.
'--sockfd' and '--remote' are mutually exclusive. Because sudo closes
file descriptors >= 3 by default but preserves stdin, the typical
invocation passes the socket as stdin:

  sudo usbip attach --sockfd 0 --busid <busid>

The descriptor must be a connected SOCK_STREAM socket: fstat() must
report S_ISSOCK, getsockopt(SO_TYPE) must return SOCK_STREAM and
getpeername() must succeed. Both AF_UNIX and AF_INET/AF_INET6 sockets
are accepted, since vhci_hcd only requires a SOCK_STREAM socket. For
AF_INET/AF_INET6 sockets, TCP_NODELAY and SO_KEEPALIVE are set as
usbip_net_tcp_connect() does.

The connection recorded in /var/run/vhci_hcd/port<N> for 'usbip port'
uses the numeric peer address and port for AF_INET/AF_INET6 sockets.
UNIX sockets have no port, so 'unix' and '-' are recorded instead; this
keeps read_record() working and 'usbip port' shows
'usbip://unix:-/<busid>'.

Update doc/usbip.8 and the usage text accordingly.

Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Assisted-by: Gemini
---
Tested on x86_64 with vhci_hcd, against a userspace USB/IP server:
- AF_UNIX: stdin connected to the server over a UNIX socket. The device
  enumerated, 'usbip port' showed usbip://unix:-/<busid>, and closing
  the server's socket detached the device.
- AF_INET: stdin connected over TCP to 127.0.0.1. The device enumerated,
  'usbip port' showed usbip://127.0.0.1:<port>/<busid>, and closing the
  connection detached the device.
- Error paths: non-socket fd, SOCK_DGRAM socket, unconnected socket,
  invalid fd number, --sockfd together with --remote, and neither.
- 'usbip attach --remote' behaves as before.

 tools/usb/usbip/doc/usbip.8        |  20 +++++
 tools/usb/usbip/src/usbip_attach.c | 118 ++++++++++++++++++++++++++++-
 2 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/tools/usb/usbip/doc/usbip.8 b/tools/usb/usbip/doc/usbip.8
index 1f26e4a00638..7440eac3e16c 100644
--- a/tools/usb/usbip/doc/usbip.8
+++ b/tools/usb/usbip/doc/usbip.8
@@ -56,6 +56,21 @@ Attach a remote USB gadget.
 Only used when the remote usbipd is in device mode.
 .PP
 
+.HP
+\fBattach\fR \-\-sockfd=<\fIsockfd\fR> \-\-busid=<\fIbus_id\fR>
+.IP
+Attach a remote USB device using an already-connected stream socket
+inherited on file descriptor <sockfd>.
+.PP
+
+.HP
+\fBattach\fR \-\-sockfd=<\fIsockfd\fR> \-\-device=<\fIdev_id\fR>
+.IP
+Attach a remote USB gadget using an already-connected stream socket
+inherited on file descriptor <sockfd>.
+Only used when the remote usbipd is in device mode.
+.PP
+
 .HP
 \fBdetach\fR \-\-port=<\fIport\fR>
 .IP
@@ -117,6 +132,11 @@ List imported devices/gadgets.
     client:# usbip detach --port=0
         - Detach the usb device.
 
+The following example shows attaching over an inherited socket:
+
+    client:# sudo usbip attach --sockfd 0 --busid 1-2
+        - Connect a remote USB device using an inherited socket on file descriptor 0.
+
 The following example shows the usage of device mode
 
     server:# usbip list --device
diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c
index 531a415538f9..00c49038d427 100644
--- a/tools/usb/usbip/src/usbip_attach.c
+++ b/tools/usb/usbip/src/usbip_attach.c
@@ -7,11 +7,14 @@
  *               Krzysztof Opasiak <k.opasiak@samsung.com>
  */
 
+#include <sys/socket.h>
 #include <sys/stat.h>
 
 #include <limits.h>
+#include <netdb.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <fcntl.h>
@@ -27,6 +30,7 @@
 static const char usbip_attach_usage_string[] =
 	"usbip attach <args>\n"
 	"    -r, --remote=<host>      The machine with exported USB devices\n"
+	"    -s, --sockfd=<fd>        Connect to inherited socket on <fd>\n"
 	"    -b, --busid=<busid>    Busid of the device on <host>\n"
 	"    -d, --device=<devid>    Id of the virtual UDC on <host>\n";
 
@@ -36,7 +40,8 @@ void usbip_attach_usage(void)
 }
 
 #define MAX_BUFF 100
-static int record_connection(char *host, char *port, char *busid, int rhport)
+static int record_connection(const char *host, const char *port,
+			     const char *busid, int rhport)
 {
 	int fd;
 	char path[PATH_MAX+1];
@@ -198,21 +203,117 @@ static int attach_device(char *host, char *busid)
 	return 0;
 }
 
+static int parse_sockfd(const char *str)
+{
+	char *endptr;
+	long val;
+
+	errno = 0;
+	val = strtol(str, &endptr, 10);
+	if (errno != 0 || endptr == str || *endptr != '\0' ||
+	    val < 0 || val > INT_MAX) {
+		err("invalid sockfd: %s", str);
+		return -1;
+	}
+
+	return (int)val;
+}
+
+static int attach_device_sockfd(const char *sockfd_str, char *busid)
+{
+	struct sockaddr_storage ss;
+	socklen_t len = sizeof(ss);
+	struct stat st;
+	int type = 0;
+	socklen_t optlen = sizeof(type);
+	char host[NI_MAXHOST];
+	char port[NI_MAXSERV];
+	int sockfd;
+	int rc;
+	int rhport;
+
+	sockfd = parse_sockfd(sockfd_str);
+	if (sockfd < 0)
+		return -1;
+
+	if (fstat(sockfd, &st) < 0) {
+		err("fstat failed on sockfd %d: %s", sockfd, strerror(errno));
+		return -1;
+	}
+
+	if (!S_ISSOCK(st.st_mode)) {
+		err("sockfd %d is not a socket", sockfd);
+		return -1;
+	}
+
+	if (getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &type, &optlen) < 0) {
+		err("getsockopt(SO_TYPE) failed on sockfd %d: %s",
+		    sockfd, strerror(errno));
+		return -1;
+	}
+
+	if (type != SOCK_STREAM) {
+		err("sockfd %d is not a SOCK_STREAM socket", sockfd);
+		return -1;
+	}
+
+	if (getpeername(sockfd, (struct sockaddr *)&ss, &len) < 0) {
+		err("getpeername failed on sockfd %d: %s",
+		    sockfd, strerror(errno));
+		return -1;
+	}
+
+	if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
+		usbip_net_set_nodelay(sockfd);
+		usbip_net_set_keepalive(sockfd);
+
+		rc = getnameinfo((struct sockaddr *)&ss, len,
+				 host, sizeof(host), port, sizeof(port),
+				 NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			err("getnameinfo: %s", gai_strerror(rc));
+			return -1;
+		}
+	} else if (ss.ss_family == AF_UNIX) {
+		snprintf(host, sizeof(host), "unix");
+		snprintf(port, sizeof(port), "-");
+	} else {
+		snprintf(host, sizeof(host), "unknown");
+		snprintf(port, sizeof(port), "-");
+	}
+
+	rhport = query_import_device(sockfd, busid);
+	if (rhport < 0)
+		return -1;
+
+	close(sockfd);
+
+	rc = record_connection(host, port, busid, rhport);
+	if (rc < 0) {
+		err("record connection");
+		return -1;
+	}
+
+	return 0;
+}
+
 int usbip_attach(int argc, char *argv[])
 {
 	static const struct option opts[] = {
 		{ "remote", required_argument, NULL, 'r' },
 		{ "busid",  required_argument, NULL, 'b' },
 		{ "device",  required_argument, NULL, 'd' },
+		{ "sockfd", required_argument, NULL, 's' },
 		{ NULL, 0,  NULL, 0 }
 	};
 	char *host = NULL;
 	char *busid = NULL;
+	char *sockfd_str = NULL;
 	int opt;
 	int ret = -1;
 
 	for (;;) {
-		opt = getopt_long(argc, argv, "d:r:b:", opts, NULL);
+		opt = getopt_long(argc, argv, "d:r:b:s:", opts, NULL);
 
 		if (opt == -1)
 			break;
@@ -225,15 +326,24 @@ int usbip_attach(int argc, char *argv[])
 		case 'b':
 			busid = optarg;
 			break;
+		case 's':
+			sockfd_str = optarg;
+			break;
 		default:
 			goto err_out;
 		}
 	}
 
-	if (!host || !busid)
+	if (!busid)
+		goto err_out;
+
+	if ((host && sockfd_str) || (!host && !sockfd_str))
 		goto err_out;
 
-	ret = attach_device(host, busid);
+	if (host)
+		ret = attach_device(host, busid);
+	else
+		ret = attach_device_sockfd(sockfd_str, busid);
 	goto out;
 
 err_out:
-- 
2.56.0.218.g1ec904b173-goog


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

end of thread, other threads:[~2026-10-21 21:54 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-21 21:48 [PATCH] usbip: tools: add --sockfd option to usbip attach Raul E Rangel
2026-10-21 21:54 ` sashiko-bot

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