Commit f49ad35c authored by Dominik Paulus's avatar Dominik Paulus Committed by Greg Kroah-Hartman

staging: usbip: Fix IPv6 support in usbipd

getaddrinfo() leaves the order of the returned addrinfo structs
unspecified. On systems with bindv6only disabled (this is the default),
PF_INET6 sockets bind to IPv4, too. Thus, IPv6 support in usbipd was
broken when getaddrinfo returned first IPv4 and then IPv6 addrinfos, as
the IPv6 bind failed with EADDRINUSE.

This patch uses seperate sockets for IPv4 and IPv6 and sets IPV6_V6ONLY
on all IPv6 sockets. Two command line arguments, -4 and -6 were added to
manually select the socket family.
Signed-off-by: default avatarTobias Polzer <tobias.polzer@fau.de>
Signed-off-by: default avatarDominik Paulus <dominik.paulus@fau.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 414ef695
......@@ -243,6 +243,18 @@ int usbip_net_set_keepalive(int sockfd)
return ret;
}
int usbip_net_set_v6only(int sockfd)
{
const int val = 1;
int ret;
ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
if (ret < 0)
dbg("setsockopt: IPV6_V6ONLY");
return ret;
}
/*
* IPv6 Ready
*/
......
......@@ -180,6 +180,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code);
int usbip_net_set_reuseaddr(int sockfd);
int usbip_net_set_nodelay(int sockfd);
int usbip_net_set_keepalive(int sockfd);
int usbip_net_set_v6only(int sockfd);
int usbip_net_tcp_connect(char *hostname, char *port);
#endif /* __USBIP_NETWORK_H */
......@@ -56,6 +56,13 @@ static const char usbip_version_string[] = PACKAGE_STRING;
static const char usbipd_help_string[] =
"usage: usbipd [options]\n"
"\n"
" -4, --ipv4\n"
" Bind to IPv4. Default is both.\n"
"\n"
" -6, --ipv6\n"
" Bind to IPv6. Default is both.\n"
"\n"
" -D, --daemon\n"
" Run as a daemon process.\n"
"\n"
......@@ -354,14 +361,15 @@ static void addrinfo_to_text(struct addrinfo *ai, char buf[],
snprintf(buf, buf_size, "%s:%s", hbuf, sbuf);
}
static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[],
int maxsockfd)
{
struct addrinfo *ai;
int ret, nsockfd = 0;
const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2;
char ai_buf[ai_buf_size];
for (ai = ai_head; ai && nsockfd < MAXSOCKFD; ai = ai->ai_next) {
for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) {
int sock;
addrinfo_to_text(ai, ai_buf, ai_buf_size);
dbg("opening %s", ai_buf);
......@@ -374,6 +382,9 @@ static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
usbip_net_set_reuseaddr(sock);
usbip_net_set_nodelay(sock);
/* We use seperate sockets for IPv4 and IPv6
* (see do_standalone_mode()) */
usbip_net_set_v6only(sock);
if (sock >= FD_SETSIZE) {
err("FD_SETSIZE: %s: sock=%d, max=%d",
......@@ -402,11 +413,6 @@ static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
sockfdlist[nsockfd++] = sock;
}
if (nsockfd == 0)
return -1;
dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
return nsockfd;
}
......@@ -473,11 +479,11 @@ static void remove_pid_file()
}
}
static int do_standalone_mode(int daemonize)
static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
{
struct addrinfo *ai_head;
int sockfdlist[MAXSOCKFD];
int nsockfd;
int nsockfd, family;
int i, terminate;
struct pollfd *fds;
struct timespec timeout;
......@@ -501,21 +507,36 @@ static int do_standalone_mode(int daemonize)
set_signal();
write_pid_file();
ai_head = do_getaddrinfo(NULL, PF_UNSPEC);
info("starting " PROGNAME " (%s)", usbip_version_string);
/*
* To suppress warnings on systems with bindv6only disabled
* (default), we use seperate sockets for IPv6 and IPv4 and set
* IPV6_V6ONLY on the IPv6 sockets.
*/
if (ipv4 && ipv6)
family = AF_UNSPEC;
else if (ipv4)
family = AF_INET;
else
family = AF_INET6;
ai_head = do_getaddrinfo(NULL, family);
if (!ai_head) {
usbip_host_driver_close();
return -1;
}
info("starting " PROGNAME " (%s)", usbip_version_string);
nsockfd = listen_all_addrinfo(ai_head, sockfdlist);
nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
sizeof(sockfdlist) / sizeof(*sockfdlist));
freeaddrinfo(ai_head);
if (nsockfd <= 0) {
err("failed to open a listening socket");
freeaddrinfo(ai_head);
usbip_host_driver_close();
return -1;
}
dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
fds = calloc(nsockfd, sizeof(struct pollfd));
for (i = 0; i < nsockfd; i++) {
fds[i].fd = sockfdlist[i];
......@@ -551,7 +572,6 @@ static int do_standalone_mode(int daemonize)
info("shutting down " PROGNAME);
free(fds);
freeaddrinfo(ai_head);
usbip_host_driver_close();
return 0;
......@@ -560,6 +580,9 @@ static int do_standalone_mode(int daemonize)
int main(int argc, char *argv[])
{
static const struct option longopts[] = {
{ "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' },
{ "daemon", no_argument, NULL, 'D' },
{ "daemon", no_argument, NULL, 'D' },
{ "debug", no_argument, NULL, 'd' },
{ "pid", optional_argument, NULL, 'P' },
......@@ -576,6 +599,7 @@ int main(int argc, char *argv[])
} cmd;
int daemonize = 0;
int ipv4 = 0, ipv6 = 0;
int opt, rc = -1;
pid_file = NULL;
......@@ -587,12 +611,18 @@ int main(int argc, char *argv[])
cmd = cmd_standalone_mode;
for (;;) {
opt = getopt_long(argc, argv, "DdP::t:hv", longopts, NULL);
opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
if (opt == -1)
break;
switch (opt) {
case '4':
ipv4 = 1;
break;
case '6':
ipv6 = 1;
break;
case 'D':
daemonize = 1;
break;
......@@ -618,9 +648,12 @@ int main(int argc, char *argv[])
}
}
if (!ipv4 && !ipv6)
ipv4 = ipv6 = 1;
switch (cmd) {
case cmd_standalone_mode:
rc = do_standalone_mode(daemonize);
rc = do_standalone_mode(daemonize, ipv4, ipv6);
remove_pid_file();
break;
case cmd_version:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment