[PATCH] usbip: tools: add --sockfd option to usbip attach
From: Raul E Rangel
Date: Wed Oct 21 2026 - 17:49:35 EST
'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@xxxxxxxxxxxx>
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@xxxxxxxxxxx>
*/
+#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