304 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>4 * 2005-2007 Takahiro Hirofuchi5 */6 7#include <sys/socket.h>8 9#include <string.h>10 11#include <arpa/inet.h>12#include <netdb.h>13#include <netinet/tcp.h>14#include <unistd.h>15 16#ifdef HAVE_LIBWRAP17#include <tcpd.h>18#endif19 20#include "usbip_common.h"21#include "usbip_network.h"22 23int usbip_port = 3240;24char *usbip_port_string = "3240";25 26void usbip_setup_port_number(char *arg)27{28 dbg("parsing port arg '%s'", arg);29 char *end;30 unsigned long int port = strtoul(arg, &end, 10);31 32 if (end == arg) {33 err("port: could not parse '%s' as a decimal integer", arg);34 return;35 }36 37 if (*end != '\0') {38 err("port: garbage at end of '%s'", arg);39 return;40 }41 42 if (port > UINT16_MAX) {43 err("port: %s too high (max=%d)",44 arg, UINT16_MAX);45 return;46 }47 48 usbip_port = port;49 usbip_port_string = arg;50 info("using port %d (\"%s\")", usbip_port, usbip_port_string);51}52 53uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num)54{55 uint32_t i;56 57 if (pack)58 i = htonl(num);59 else60 i = ntohl(num);61 62 return i;63}64 65uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num)66{67 uint16_t i;68 69 if (pack)70 i = htons(num);71 else72 i = ntohs(num);73 74 return i;75}76 77void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev)78{79 udev->busnum = usbip_net_pack_uint32_t(pack, udev->busnum);80 udev->devnum = usbip_net_pack_uint32_t(pack, udev->devnum);81 udev->speed = usbip_net_pack_uint32_t(pack, udev->speed);82 83 udev->idVendor = usbip_net_pack_uint16_t(pack, udev->idVendor);84 udev->idProduct = usbip_net_pack_uint16_t(pack, udev->idProduct);85 udev->bcdDevice = usbip_net_pack_uint16_t(pack, udev->bcdDevice);86}87 88void usbip_net_pack_usb_interface(int pack __attribute__((unused)),89 struct usbip_usb_interface *udev90 __attribute__((unused)))91{92 /* uint8_t members need nothing */93}94 95static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,96 int sending)97{98 ssize_t nbytes;99 ssize_t total = 0;100 101 if (!bufflen)102 return 0;103 104 do {105 if (sending)106 nbytes = send(sockfd, buff, bufflen, 0);107 else108 nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);109 110 if (nbytes <= 0)111 return -1;112 113 buff = (void *)((intptr_t) buff + nbytes);114 bufflen -= nbytes;115 total += nbytes;116 117 } while (bufflen > 0);118 119 return total;120}121 122ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)123{124 return usbip_net_xmit(sockfd, buff, bufflen, 0);125}126 127ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)128{129 return usbip_net_xmit(sockfd, buff, bufflen, 1);130}131 132static inline void usbip_net_pack_op_common(int pack,133 struct op_common *op_common)134{135 op_common->version = usbip_net_pack_uint16_t(pack, op_common->version);136 op_common->code = usbip_net_pack_uint16_t(pack, op_common->code);137 op_common->status = usbip_net_pack_uint32_t(pack, op_common->status);138}139 140int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)141{142 struct op_common op_common;143 int rc;144 145 memset(&op_common, 0, sizeof(op_common));146 147 op_common.version = USBIP_VERSION;148 op_common.code = code;149 op_common.status = status;150 151 usbip_net_pack_op_common(1, &op_common);152 153 rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));154 if (rc < 0) {155 dbg("usbip_net_send failed: %d", rc);156 return -1;157 }158 159 return 0;160}161 162int usbip_net_recv_op_common(int sockfd, uint16_t *code, int *status)163{164 struct op_common op_common;165 int rc;166 167 memset(&op_common, 0, sizeof(op_common));168 169 rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));170 if (rc < 0) {171 dbg("usbip_net_recv failed: %d", rc);172 goto err;173 }174 175 usbip_net_pack_op_common(0, &op_common);176 177 if (op_common.version != USBIP_VERSION) {178 err("USBIP Kernel and tool version mismatch: %d %d:",179 op_common.version, USBIP_VERSION);180 goto err;181 }182 183 switch (*code) {184 case OP_UNSPEC:185 break;186 default:187 if (op_common.code != *code) {188 dbg("unexpected pdu %#0x for %#0x", op_common.code,189 *code);190 /* return error status */191 *status = ST_ERROR;192 goto err;193 }194 }195 196 *status = op_common.status;197 198 if (op_common.status != ST_OK) {199 dbg("request failed at peer: %d", op_common.status);200 goto err;201 }202 203 *code = op_common.code;204 205 return 0;206err:207 return -1;208}209 210int usbip_net_set_reuseaddr(int sockfd)211{212 const int val = 1;213 int ret;214 215 ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));216 if (ret < 0)217 dbg("setsockopt: SO_REUSEADDR");218 219 return ret;220}221 222int usbip_net_set_nodelay(int sockfd)223{224 const int val = 1;225 int ret;226 227 ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));228 if (ret < 0)229 dbg("setsockopt: TCP_NODELAY");230 231 return ret;232}233 234int usbip_net_set_keepalive(int sockfd)235{236 const int val = 1;237 int ret;238 239 ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));240 if (ret < 0)241 dbg("setsockopt: SO_KEEPALIVE");242 243 return ret;244}245 246int usbip_net_set_v6only(int sockfd)247{248 const int val = 1;249 int ret;250 251 ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));252 if (ret < 0)253 dbg("setsockopt: IPV6_V6ONLY");254 255 return ret;256}257 258/*259 * IPv6 Ready260 */261int usbip_net_tcp_connect(char *hostname, char *service)262{263 struct addrinfo hints, *res, *rp;264 int sockfd;265 int ret;266 267 memset(&hints, 0, sizeof(hints));268 hints.ai_family = AF_UNSPEC;269 hints.ai_socktype = SOCK_STREAM;270 271 /* get all possible addresses */272 ret = getaddrinfo(hostname, service, &hints, &res);273 if (ret < 0) {274 dbg("getaddrinfo: %s service %s: %s", hostname, service,275 gai_strerror(ret));276 return ret;277 }278 279 /* try the addresses */280 for (rp = res; rp; rp = rp->ai_next) {281 sockfd = socket(rp->ai_family, rp->ai_socktype,282 rp->ai_protocol);283 if (sockfd < 0)284 continue;285 286 /* should set TCP_NODELAY for usbip */287 usbip_net_set_nodelay(sockfd);288 /* TODO: write code for heartbeat */289 usbip_net_set_keepalive(sockfd);290 291 if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)292 break;293 294 close(sockfd);295 }296 297 freeaddrinfo(res);298 299 if (!rp)300 return EAI_SYSTEM;301 302 return sockfd;303}304