* [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-28 12:55 [Qemu-devel] [PATCH 0/4] ipv6 and autoport patches Gerd Hoffmann
@ 2008-10-28 12:55 ` Gerd Hoffmann
2008-10-28 13:15 ` Daniel P. Berrange
0 siblings, 1 reply; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-28 12:55 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This patch creates a new source and header file qemu-sockets.[ch] with
a bunch of helper functions to create listening and connected sockets.
New features of this code are (a) support for searching for a free port
in a given range and (b) support for IPv6.
The following patches put that code into use.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
Makefile.target | 1 +
qemu-sockets.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-sockets.h | 8 +
vl.c | 13 ++
4 files changed, 413 insertions(+), 0 deletions(-)
create mode 100644 qemu-sockets.c
create mode 100644 qemu-sockets.h
diff --git a/Makefile.target b/Makefile.target
index 547ac02..5f71972 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -580,6 +580,7 @@ ifndef CONFIG_USER_ONLY
OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o
OBJS+=fw_cfg.o aio.o buffered_file.o migration.o migration-tcp.o
+OBJS+=qemu-sockets.o
ifdef CONFIG_WIN32
OBJS+=block-raw-win32.o
else
diff --git a/qemu-sockets.c b/qemu-sockets.c
new file mode 100644
index 0000000..99adcd7
--- /dev/null
+++ b/qemu-sockets.c
@@ -0,0 +1,391 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "qemu-sockets.h"
+
+static int default_family = PF_UNSPEC;
+static int sockets_debug = 1;
+
+static int inet_getport(struct addrinfo *e)
+{
+ struct sockaddr_in *i4;
+ struct sockaddr_in6 *i6;
+
+ switch (e->ai_family) {
+ case PF_INET6:
+ i6 = (void*)e->ai_addr;
+ return ntohs(i6->sin6_port);
+ case PF_INET:
+ i4 = (void*)e->ai_addr;
+ return ntohs(i4->sin_port);
+ default:
+ return 0;
+ }
+}
+
+static void inet_setport(struct addrinfo *e, int port)
+{
+ struct sockaddr_in *i4;
+ struct sockaddr_in6 *i6;
+
+ switch (e->ai_family) {
+ case PF_INET6:
+ i6 = (void*)e->ai_addr;
+ i6->sin6_port = htons(port);
+ break;
+ case PF_INET:
+ i4 = (void*)e->ai_addr;
+ i4->sin_port = htons(port);
+ break;
+ }
+}
+
+static const char *inet_strfamily(int family)
+{
+ switch (family) {
+ case PF_INET6: return "ipv6";
+ case PF_INET: return "ipv4";
+ case PF_UNIX: return "unix";
+ }
+ return "????";
+}
+
+static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
+{
+ struct addrinfo *e;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
+ tag, inet_strfamily(e->ai_family), uaddr, uport);
+ }
+}
+
+void inet_set_ipv4(void)
+{
+ default_family = PF_INET;
+}
+
+void inet_set_ipv6(void)
+{
+ default_family = PF_INET6;
+}
+
+int inet_listen(const char *str, char *ostr, int olen,
+ int socktype, int port_offset)
+{
+ static const int on=1, off=0;
+ struct addrinfo ai,*res,*e;
+ char addr[64];
+ char port[33];
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ const char *opts, *h;
+ int slisten,rc,pos,to,try_next;
+
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_PASSIVE;
+ ai.ai_family = default_family;
+ ai.ai_socktype = socktype;
+
+ /* parse string */
+ if (str[0] == ':') {
+ /* no host given */
+ strcpy(addr,"");
+ if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
+ fprintf(stderr, "%s: portonly parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ } else if (str[0] == '[') {
+ /* IPv6 addr */
+ if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET6;
+ } else if (isdigit(str[0])) {
+ /* IPv4 addr */
+ if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET;
+ } else {
+ /* hostname */
+ if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: hostname parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ }
+ opts = str + pos;
+ h = strstr(opts, ",to=");
+ to = h ? atoi(h+4) : 0;
+
+ /* lookup */
+ if (port_offset)
+ snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
+ if (0 != (rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res))) {
+ fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
+ addr, port, gai_strerror(rc));
+ return -1;
+ }
+ if (sockets_debug)
+ inet_print_addrinfo(__FUNCTION__, res);
+
+ /* create socket + bind */
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (-1 == (slisten = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), strerror(errno));
+ continue;
+ }
+
+ setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
+ if (e->ai_family == PF_INET6) {
+ if (default_family == PF_INET6)
+ setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on));
+ else
+ setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&off,sizeof(off));
+ }
+
+ for (;;) {
+ if (0 == bind(slisten, e->ai_addr, e->ai_addrlen)) {
+ if (sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), uaddr, inet_getport(e));
+ goto listen;
+ }
+ try_next = to && (inet_getport(e) <= to + port_offset);
+ if (!try_next || sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), uaddr, inet_getport(e),
+ strerror(errno));
+ if (try_next) {
+ inet_setport(e, inet_getport(e) + 1);
+ continue;
+ }
+ break;
+ }
+ close(slisten);
+ }
+ fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
+ return -1;
+
+listen:
+ if (0 != listen(slisten,1)) {
+ perror("listen");
+ close(slisten);
+ return -1;
+ }
+ if (ostr) {
+ if (e->ai_family == PF_INET6) {
+ snprintf(ostr, olen, "[%s]:%d%s", uaddr,
+ inet_getport(e) - port_offset, opts);
+ } else {
+ snprintf(ostr, olen, "%s:%d%s", uaddr,
+ inet_getport(e) - port_offset, opts);
+ }
+ }
+ return slisten;
+}
+
+int inet_connect(const char *str, int socktype)
+{
+ static const int on=1;
+ struct addrinfo ai,*res,*e;
+ char addr[64];
+ char port[33];
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int sock,rc;
+
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_CANONNAME;
+ ai.ai_family = default_family;
+ ai.ai_socktype = socktype;
+
+ /* parse string */
+ if (str[0] == '[') {
+ /* IPv6 addr */
+ if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
+ fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET6;
+ } else if (isdigit(str[0])) {
+ /* IPv4 addr */
+ if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
+ fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET;
+ } else {
+ /* hostname */
+ if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
+ fprintf(stderr, "%s: hostname parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ }
+
+ /* lookup */
+ if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
+ addr, port);
+ return -1;
+ }
+ if (sockets_debug)
+ inet_print_addrinfo(__FUNCTION__, res);
+
+ for (e = res; e != NULL; e = e->ai_next) {
+ if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
+ continue;
+ }
+ if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), strerror(errno));
+ continue;
+ }
+ setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
+
+ /* connect to peer */
+ if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
+ if (sockets_debug || NULL == e->ai_next)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family),
+ e->ai_canonname, uaddr, uport, strerror(errno));
+ close(sock);
+ continue;
+ }
+ if (sockets_debug)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
+ inet_strfamily(e->ai_family),
+ e->ai_canonname, uaddr, uport);
+ return sock;
+ }
+ return -1;
+}
+
+#ifndef _WIN32
+
+int unix_listen(const char *str, char *ostr, int olen)
+{
+ struct sockaddr_un un;
+ char *path, *opts;
+ int sock, fd, len;
+
+ if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ opts = strchr(str, ',');
+ if (opts) {
+ len = opts - str;
+ path = malloc(len+1);
+ snprintf(path, len+1, "%.*s", len, str);
+ } else
+ path = strdup(str);
+
+ un.sun_family = AF_UNIX;
+ if (path && strlen(path)) {
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ } else {
+ char *tmpdir = getenv("TMPDIR");
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
+ tmpdir ? tmpdir : "/tmp");
+ /*
+ * This dummy fd usage silences the mktemp() unsecure warning.
+ * Using mkstemp() doesn't make things more secure here
+ * though. bind() complains about existing files, so we have
+ * to unlink first and thus re-open the race window. The
+ * worst case possible is bind() failing, i.e. a DoS attack.
+ */
+ fd = mkstemp(un.sun_path); close(fd);
+ }
+ snprintf(ostr, olen, "%s%s", un.sun_path, opts ? opts : "");
+
+ unlink(un.sun_path);
+ if (bind(sock, (struct sockaddr*) &un, sizeof(un)) == -1) {
+ fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+ if (listen(sock, 1) == -1) {
+ fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
+ free(path);
+ return sock;
+
+err:
+ free(path);
+ close(sock);
+ return -1;
+}
+
+int unix_connect(const char *path)
+{
+ struct sockaddr_un un;
+ int sock;
+
+ if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ if (-1 == connect(sock, (struct sockaddr*) &un, sizeof(un))) {
+ fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "connect(unix:%s): OK\n", path);
+ return sock;
+}
+
+#else
+
+int unix_listen(const char *path, char *ostr, int olen)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+int unix_connect(const char *path)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+#endif
diff --git a/qemu-sockets.h b/qemu-sockets.h
new file mode 100644
index 0000000..8793af3
--- /dev/null
+++ b/qemu-sockets.h
@@ -0,0 +1,8 @@
+int inet_listen(const char *str, char *ostr, int olen,
+ int socktype, int port_offset);
+int inet_connect(const char *str, int socktype);
+void inet_set_ipv4(void);
+void inet_set_ipv6(void);
+
+int unix_listen(const char *path, char *ostr, int olen);
+int unix_connect(const char *path);
diff --git a/vl.c b/vl.c
index 114f6db..2112585 100644
--- a/vl.c
+++ b/vl.c
@@ -37,6 +37,7 @@
#include "gdbstub.h"
#include "qemu-timer.h"
#include "qemu-char.h"
+#include "qemu-sockets.h"
#include "block.h"
#include "audio/audio.h"
#include "migration.h"
@@ -8386,6 +8387,8 @@ enum {
QEMU_OPTION_bootp,
QEMU_OPTION_smb,
QEMU_OPTION_redir,
+ QEMU_OPTION_ipv4,
+ QEMU_OPTION_ipv6,
QEMU_OPTION_kernel,
QEMU_OPTION_append,
@@ -8489,6 +8492,10 @@ static const QEMUOption qemu_options[] = {
#endif
{ "redir", HAS_ARG, QEMU_OPTION_redir },
#endif
+ { "4", 0, QEMU_OPTION_ipv4 },
+ { "6", 0, QEMU_OPTION_ipv6 },
+ { "ipv4", 0, QEMU_OPTION_ipv4 },
+ { "ipv6", 0, QEMU_OPTION_ipv6 },
{ "kernel", HAS_ARG, QEMU_OPTION_kernel },
{ "append", HAS_ARG, QEMU_OPTION_append },
@@ -9151,6 +9158,12 @@ int main(int argc, char **argv)
net_slirp_redir(optarg);
break;
#endif
+ case QEMU_OPTION_ipv4:
+ inet_set_ipv4();
+ break;
+ case QEMU_OPTION_ipv6:
+ inet_set_ipv6();
+ break;
#ifdef HAS_AUDIO
case QEMU_OPTION_audio_help:
AUD_help ();
--
1.5.6.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-28 12:55 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
@ 2008-10-28 13:15 ` Daniel P. Berrange
2008-10-28 14:22 ` Gerd Hoffmann
0 siblings, 1 reply; 16+ messages in thread
From: Daniel P. Berrange @ 2008-10-28 13:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
On Tue, Oct 28, 2008 at 01:55:16PM +0100, Gerd Hoffmann wrote:
> This patch creates a new source and header file qemu-sockets.[ch] with
> a bunch of helper functions to create listening and connected sockets.
>
> New features of this code are (a) support for searching for a free port
> in a given range and (b) support for IPv6.
This is great - adding IPv6 was on my todo list for ages !
> +
> +int inet_listen(const char *str, char *ostr, int olen,
> + int socktype, int port_offset)
> +{
> + static const int on=1, off=0;
> + struct addrinfo ai,*res,*e;
> + char addr[64];
> + char port[33];
> + char uaddr[INET6_ADDRSTRLEN+1];
> + char uport[33];
> + const char *opts, *h;
> + int slisten,rc,pos,to,try_next;
> +
> + memset(&ai,0, sizeof(ai));
> + ai.ai_flags = AI_PASSIVE;
You should also set AI_ADDRCONFIG here. This ensure that it only
returns IPv6 addresses if a network interface actally has IPv6
enabled. So if someone's disabled IPv6 on a machine, and DNS still
has IPv6 addrs, AI_ADDRCONFIG will stop QEMU pointlessly attempting
to create IPv6 sockets that won't do anything
> + ai.ai_family = default_family;
> + ai.ai_socktype = socktype;
> +
> + /* parse string */
> + if (str[0] == ':') {
> + /* no host given */
> + strcpy(addr,"");
> + if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
> + fprintf(stderr, "%s: portonly parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + } else if (str[0] == '[') {
> + /* IPv6 addr */
> + if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
> + fprintf(stderr, "%s: ipv6 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET6;
> + } else if (isdigit(str[0])) {
> + /* IPv4 addr */
> + if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
> + fprintf(stderr, "%s: ipv4 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET;
> + } else {
> + /* hostname */
> + if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
> + fprintf(stderr, "%s: hostname parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + }
> + opts = str + pos;
> + h = strstr(opts, ",to=");
> + to = h ? atoi(h+4) : 0;
> +
> + /* lookup */
> + if (port_offset)
> + snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
> + if (0 != (rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res))) {
> + fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
> + addr, port, gai_strerror(rc));
> + return -1;
> + }
> + if (sockets_debug)
> + inet_print_addrinfo(__FUNCTION__, res);
> +
> + /* create socket + bind */
> + for (e = res; e != NULL; e = e->ai_next) {
> + getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
> + uaddr,INET6_ADDRSTRLEN,uport,32,
> + NI_NUMERICHOST | NI_NUMERICSERV);
> + if (-1 == (slisten = socket(e->ai_family, e->ai_socktype,
> + e->ai_protocol))) {
> + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), strerror(errno));
> + continue;
> + }
> +
> + setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
> + if (e->ai_family == PF_INET6) {
> + if (default_family == PF_INET6)
> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on));
> + else
> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&off,sizeof(off));
> + }
> +
> + for (;;) {
> + if (0 == bind(slisten, e->ai_addr, e->ai_addrlen)) {
> + if (sockets_debug)
> + fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), uaddr, inet_getport(e));
> + goto listen;
> + }
> + try_next = to && (inet_getport(e) <= to + port_offset);
> + if (!try_next || sockets_debug)
> + fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), uaddr, inet_getport(e),
> + strerror(errno));
> + if (try_next) {
> + inet_setport(e, inet_getport(e) + 1);
> + continue;
> + }
> + break;
> + }
> + close(slisten);
> + }
> + fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
> + return -1;
> +
> +listen:
> + if (0 != listen(slisten,1)) {
> + perror("listen");
> + close(slisten);
> + return -1;
> + }
> + if (ostr) {
> + if (e->ai_family == PF_INET6) {
> + snprintf(ostr, olen, "[%s]:%d%s", uaddr,
> + inet_getport(e) - port_offset, opts);
> + } else {
> + snprintf(ostr, olen, "%s:%d%s", uaddr,
> + inet_getport(e) - port_offset, opts);
> + }
> + }
> + return slisten;
> +}
One small problem here - for a server you need to expect more than one
socket will be required. This is because some operating systems require
you to bind to IPv4 and IPv6 sockets separately. On Linux by default
a IPv6 socket can accept IPv4 client connections, but this behaviour
can be turned off by sysctl, and other non-Linux OS don't allow this
at all. So we really need an array of server sockets, and attempt to
bind to all addresses returned by getaddrinfo().
There's more info on this here
http://people.redhat.com/drepper/userapi-ipv6.html
> +int inet_connect(const char *str, int socktype)
> +{
> + static const int on=1;
> + struct addrinfo ai,*res,*e;
> + char addr[64];
> + char port[33];
> + char uaddr[INET6_ADDRSTRLEN+1];
> + char uport[33];
> + int sock,rc;
> +
> + memset(&ai,0, sizeof(ai));
> + ai.ai_flags = AI_CANONNAME;
This also needs AI_ADDRCONFIG set
> + ai.ai_family = default_family;
> + ai.ai_socktype = socktype;
> +
> + /* parse string */
> + if (str[0] == '[') {
> + /* IPv6 addr */
> + if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
> + fprintf(stderr, "%s: ipv6 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET6;
> + } else if (isdigit(str[0])) {
> + /* IPv4 addr */
> + if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
> + fprintf(stderr, "%s: ipv4 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET;
> + } else {
> + /* hostname */
> + if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
> + fprintf(stderr, "%s: hostname parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + }
> +
> + /* lookup */
> + if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
> + fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
> + addr, port);
> + return -1;
> + }
> + if (sockets_debug)
> + inet_print_addrinfo(__FUNCTION__, res);
> +
> + for (e = res; e != NULL; e = e->ai_next) {
> + if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
> + uaddr,INET6_ADDRSTRLEN,uport,32,
> + NI_NUMERICHOST | NI_NUMERICSERV)) {
> + fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
> + continue;
> + }
> + if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
> + e->ai_protocol))) {
> + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), strerror(errno));
> + continue;
> + }
> + setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
> +
> + /* connect to peer */
> + if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
> + if (sockets_debug || NULL == e->ai_next)
> + fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family),
> + e->ai_canonname, uaddr, uport, strerror(errno));
> + close(sock);
> + continue;
> + }
> + if (sockets_debug)
> + fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
> + inet_strfamily(e->ai_family),
> + e->ai_canonname, uaddr, uport);
> + return sock;
> + }
> + return -1;
> +}
> +
REgards,
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-28 13:15 ` Daniel P. Berrange
@ 2008-10-28 14:22 ` Gerd Hoffmann
2008-10-28 14:31 ` Daniel P. Berrange
0 siblings, 1 reply; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-28 14:22 UTC (permalink / raw)
To: Daniel P. Berrange; +Cc: qemu-devel
Daniel P. Berrange wrote:
>> + memset(&ai,0, sizeof(ai));
>> + ai.ai_flags = AI_PASSIVE;
>
> You should also set AI_ADDRCONFIG here. This ensure that it only
> returns IPv6 addresses if a network interface actally has IPv6
> enabled. So if someone's disabled IPv6 on a machine, and DNS still
> has IPv6 addrs, AI_ADDRCONFIG will stop QEMU pointlessly attempting
> to create IPv6 sockets that won't do anything
Done.
>> + if (e->ai_family == PF_INET6) {
>> + if (default_family == PF_INET6)
>> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on));
>> + else
>> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&off,sizeof(off));
>> + }
> One small problem here - for a server you need to expect more than one
> socket will be required. This is because some operating systems require
> you to bind to IPv4 and IPv6 sockets separately.
Sure? I've googled a bit on this issue, looked what apache does here.
I've figured this can be changed per socket, with a system-wide default
configurable via sysctl (and different OSes have different default
configs here).
The setsockopt code snippet quoted above should take care about that
issue and turn off the v6only option for the socket (unless the user
explicitly asked for IPv6 using the '-ipv6' command line option).
> So we really need an array of server sockets, and attempt to
> bind to all addresses returned by getaddrinfo().
I'm trying to get around that if possible ...
> There's more info on this here
>
> http://people.redhat.com/drepper/userapi-ipv6.html
Hmm, IPV6_V6ONLY not mentioned there ...
>> + memset(&ai,0, sizeof(ai));
>> + ai.ai_flags = AI_CANONNAME;
>
> This also needs AI_ADDRCONFIG set
Done.
cheers,
Gerd
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-28 14:22 ` Gerd Hoffmann
@ 2008-10-28 14:31 ` Daniel P. Berrange
2008-10-28 15:10 ` Gerd Hoffmann
0 siblings, 1 reply; 16+ messages in thread
From: Daniel P. Berrange @ 2008-10-28 14:31 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On Tue, Oct 28, 2008 at 03:22:35PM +0100, Gerd Hoffmann wrote:
> Daniel P. Berrange wrote:
> >> + memset(&ai,0, sizeof(ai));
> >> + ai.ai_flags = AI_PASSIVE;
> >
> > You should also set AI_ADDRCONFIG here. This ensure that it only
> > returns IPv6 addresses if a network interface actally has IPv6
> > enabled. So if someone's disabled IPv6 on a machine, and DNS still
> > has IPv6 addrs, AI_ADDRCONFIG will stop QEMU pointlessly attempting
> > to create IPv6 sockets that won't do anything
>
> Done.
>
> >> + if (e->ai_family == PF_INET6) {
> >> + if (default_family == PF_INET6)
> >> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on));
> >> + else
> >> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&off,sizeof(off));
> >> + }
>
> > One small problem here - for a server you need to expect more than one
> > socket will be required. This is because some operating systems require
> > you to bind to IPv4 and IPv6 sockets separately.
>
> Sure? I've googled a bit on this issue, looked what apache does here.
> I've figured this can be changed per socket, with a system-wide default
> configurable via sysctl (and different OSes have different default
> configs here).
Ah, I was mis-understanding what IPV6_V6ONLY was doing here. If that's
portable to BSD like OS too, then I reckon that's sufficient and would
avoid need for separate sockets.
> The setsockopt code snippet quoted above should take care about that
> issue and turn off the v6only option for the socket (unless the user
> explicitly asked for IPv6 using the '-ipv6' command line option).
>
> > So we really need an array of server sockets, and attempt to
> > bind to all addresses returned by getaddrinfo().
>
> I'm trying to get around that if possible ...
>
> > There's more info on this here
> >
> > http://people.redhat.com/drepper/userapi-ipv6.html
>
> Hmm, IPV6_V6ONLY not mentioned there ...
Uli was demonstrating how to achieve total protocol independance
in your code. So if someone invents something better even than
IPv6 your code would still work. If you're happy to limit yourself
to just IPv4 & IPv6, then the IPV6_V6ONLY trick would work.
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-28 14:31 ` Daniel P. Berrange
@ 2008-10-28 15:10 ` Gerd Hoffmann
0 siblings, 0 replies; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-28 15:10 UTC (permalink / raw)
To: Daniel P. Berrange; +Cc: qemu-devel
Daniel P. Berrange wrote:
> On Tue, Oct 28, 2008 at 03:22:35PM +0100, Gerd Hoffmann wrote:
>> Daniel P. Berrange wrote:
>> Sure? I've googled a bit on this issue, looked what apache does here.
>> I've figured this can be changed per socket, with a system-wide default
>> configurable via sysctl (and different OSes have different default
>> configs here).
>
> Ah, I was mis-understanding what IPV6_V6ONLY was doing here. If that's
> portable to BSD like OS too, then I reckon that's sufficient and would
> avoid need for separate sockets.
I hope so, but didn't actually test that on something BSDish. Maybe the
BSD folks on the list can comment/test?
> Uli was demonstrating how to achieve total protocol independance
> in your code. So if someone invents something better even than
> IPv6 your code would still work. If you're happy to limit yourself
> to just IPv4 & IPv6, then the IPV6_V6ONLY trick would work.
Right now there isn't anything else, and I'd prefer to tackle the issue
of other protocols once we have them. There are also some more ipv4/v6
assumptions in the code, so other protocols wouldn't work out-of-the-box
anyway.
cheers,
Gerd
^ permalink raw reply [flat|nested] 16+ messages in thread
* [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches.
@ 2008-10-31 12:47 Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Hi,
This patch series starts adding support for auto-allocating tcp ports
and for ipv6 to qemu. It adds a new "info chardev" command (needed to
query the auto-allocated ports), adds socket helper functiond with
ipv6 support and finally switches chardevs and vnc over to the new
socket helpers. Individual patches have more vebose descriptions.
New in v2:
* address review comments from Daniel P. Berrange and Blue Swirl.
* fixed windows build (mingw32 crosscompiler).
* added -ipv4 and -ipv6 switches to the help text.
cheers,
Gerd
^ permalink raw reply [flat|nested] 16+ messages in thread
* [Qemu-devel] [PATCH 1/4] Implement "info chardev" command.
2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
2008-10-31 17:32 ` Anthony Liguori
2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
` (2 subsequent siblings)
3 siblings, 1 reply; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This patch makes qemu keep track of the character devices in use and
implements a "info chardev" monitor command to print a list.
qemu_chr_open() sticks the devices into a linked list now. It got a new
argument (label), so there is a name for each device. It also assigns a
filename to each character device. By default it just copyes the
filename passed in. Individual drivers can fill in something else
though. qemu_chr_open_pty() sets the filename to name of the pseudo tty
allocated.
Output looks like this:
(qemu) info chardev
monitor: filename=unix:/tmp/run.sh-26827/monitor,server,nowait
serial0: filename=unix:/tmp/run.sh-26827/console,server
serial1: filename=pty:/dev/pts/5
parallel0: filename=vc:640x480
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
gdbstub.c | 2 +-
hw/usb-serial.c | 5 ++-
monitor.c | 2 +
qemu-char.h | 7 +++-
vl.c | 98 ++++++++++++++++++++++++++++++++++++------------------
5 files changed, 78 insertions(+), 36 deletions(-)
diff --git a/gdbstub.c b/gdbstub.c
index 1a85eda..15d38f0 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1852,7 +1852,7 @@ int gdbserver_start(const char *port)
port = gdbstub_port_name;
}
- chr = qemu_chr_open(port);
+ chr = qemu_chr_open("gdb", port);
if (!chr)
return -1;
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 40d04cb..a6a756d 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -521,6 +521,8 @@ USBDevice *usb_serial_init(const char *filename)
USBSerialState *s;
CharDriverState *cdrv;
unsigned short vendorid = 0x0403, productid = 0x6001;
+ char label[32];
+ static int index;
while (*filename && *filename != ':') {
const char *p;
@@ -555,7 +557,8 @@ USBDevice *usb_serial_init(const char *filename)
if (!s)
return NULL;
- cdrv = qemu_chr_open(filename);
+ snprintf(label, sizeof(label), "usbserial%d", index++);
+ cdrv = qemu_chr_open(label, filename);
if (!cdrv)
goto fail;
s->cs = cdrv;
diff --git a/monitor.c b/monitor.c
index ae034e2..611bde5 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1470,6 +1470,8 @@ static const term_cmd_t info_cmds[] = {
"", "show the version of qemu" },
{ "network", "", do_info_network,
"", "show the network state" },
+ { "chardev", "", qemu_chr_info,
+ "", "show the character devices" },
{ "block", "", do_info_block,
"", "show the block devices" },
{ "blockstats", "", do_info_blockstats,
diff --git a/qemu-char.h b/qemu-char.h
index 05d6899..55d81cb 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -1,6 +1,7 @@
#ifndef QEMU_CHAR_H
#define QEMU_CHAR_H
+#include "sys-queue.h"
/* character device */
#define CHR_EVENT_BREAK 0 /* serial break char */
@@ -55,9 +56,12 @@ struct CharDriverState {
void *opaque;
int focus;
QEMUBH *bh;
+ char *label;
+ char *filename;
+ TAILQ_ENTRY(CharDriverState) next;
};
-CharDriverState *qemu_chr_open(const char *filename);
+CharDriverState *qemu_chr_open(const char *label, const char *filename);
void qemu_chr_close(CharDriverState *chr);
void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
@@ -72,6 +76,7 @@ void qemu_chr_reset(CharDriverState *s);
int qemu_chr_can_read(CharDriverState *s);
void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len);
void qemu_chr_accept_input(CharDriverState *s);
+void qemu_chr_info(void);
/* async I/O support */
diff --git a/vl.c b/vl.c
index 0278101..a9fae17 100644
--- a/vl.c
+++ b/vl.c
@@ -2582,7 +2582,7 @@ static CharDriverState *qemu_chr_open_pty(void)
CharDriverState *chr;
PtyCharDriver *s;
struct termios tty;
- int slave_fd;
+ int slave_fd, len;
#if defined(__OpenBSD__)
char pty_name[PATH_MAX];
#define q_ptsname(x) pty_name
@@ -2609,6 +2609,9 @@ static CharDriverState *qemu_chr_open_pty(void)
tcsetattr(slave_fd, TCSAFLUSH, &tty);
close(slave_fd);
+ len = strlen(q_ptsname(s->fd)) + 5;
+ chr->filename = qemu_malloc(len);
+ snprintf(chr->filename, len, "pty:%s", q_ptsname(s->fd));
fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd));
chr->opaque = s;
@@ -3768,90 +3771,115 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
return NULL;
}
-CharDriverState *qemu_chr_open(const char *filename)
+static TAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs
+= TAILQ_HEAD_INITIALIZER(chardevs);
+
+CharDriverState *qemu_chr_open(const char *label, const char *filename)
{
const char *p;
+ CharDriverState *chr;
if (!strcmp(filename, "vc")) {
- return text_console_init(&display_state, 0);
- } else if (strstart(filename, "vc:", &p)) {
- return text_console_init(&display_state, p);
- } else if (!strcmp(filename, "null")) {
- return qemu_chr_open_null();
+ chr = text_console_init(&display_state, 0);
+ } else
+ if (strstart(filename, "vc:", &p)) {
+ chr = text_console_init(&display_state, p);
+ } else
+ if (!strcmp(filename, "null")) {
+ chr = qemu_chr_open_null();
} else
if (strstart(filename, "tcp:", &p)) {
- return qemu_chr_open_tcp(p, 0, 0);
+ chr = qemu_chr_open_tcp(p, 0, 0);
} else
if (strstart(filename, "telnet:", &p)) {
- return qemu_chr_open_tcp(p, 1, 0);
+ chr = qemu_chr_open_tcp(p, 1, 0);
} else
if (strstart(filename, "udp:", &p)) {
- return qemu_chr_open_udp(p);
+ chr = qemu_chr_open_udp(p);
} else
if (strstart(filename, "mon:", &p)) {
- CharDriverState *drv = qemu_chr_open(p);
- if (drv) {
- drv = qemu_chr_open_mux(drv);
- monitor_init(drv, !nographic);
- return drv;
+ chr = qemu_chr_open(label, p);
+ if (chr) {
+ chr = qemu_chr_open_mux(chr);
+ monitor_init(chr, !nographic);
+ } else {
+ printf("Unable to open driver: %s\n", p);
}
- printf("Unable to open driver: %s\n", p);
- return 0;
} else
#ifndef _WIN32
if (strstart(filename, "unix:", &p)) {
- return qemu_chr_open_tcp(p, 0, 1);
+ chr = qemu_chr_open_tcp(p, 0, 1);
} else if (strstart(filename, "file:", &p)) {
- return qemu_chr_open_file_out(p);
+ chr = qemu_chr_open_file_out(p);
} else if (strstart(filename, "pipe:", &p)) {
- return qemu_chr_open_pipe(p);
+ chr = qemu_chr_open_pipe(p);
} else if (!strcmp(filename, "pty")) {
- return qemu_chr_open_pty();
+ chr = qemu_chr_open_pty();
} else if (!strcmp(filename, "stdio")) {
- return qemu_chr_open_stdio();
+ chr = qemu_chr_open_stdio();
} else
#if defined(__linux__)
if (strstart(filename, "/dev/parport", NULL)) {
- return qemu_chr_open_pp(filename);
+ chr = qemu_chr_open_pp(filename);
} else
#endif
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
if (strstart(filename, "/dev/", NULL)) {
- return qemu_chr_open_tty(filename);
+ chr = qemu_chr_open_tty(filename);
} else
#endif
#else /* !_WIN32 */
if (strstart(filename, "COM", NULL)) {
- return qemu_chr_open_win(filename);
+ chr = qemu_chr_open_win(filename);
} else
if (strstart(filename, "pipe:", &p)) {
- return qemu_chr_open_win_pipe(p);
+ chr = qemu_chr_open_win_pipe(p);
} else
if (strstart(filename, "con:", NULL)) {
- return qemu_chr_open_win_con(filename);
+ chr = qemu_chr_open_win_con(filename);
} else
if (strstart(filename, "file:", &p)) {
- return qemu_chr_open_win_file_out(p);
+ chr = qemu_chr_open_win_file_out(p);
} else
#endif
#ifdef CONFIG_BRLAPI
if (!strcmp(filename, "braille")) {
- return chr_baum_init();
+ chr = chr_baum_init();
} else
#endif
{
- return NULL;
+ chr = NULL;
+ }
+
+ if (chr) {
+ if (!chr->filename)
+ chr->filename = qemu_strdup(filename);
+ chr->label = qemu_strdup(label);
+ TAILQ_INSERT_TAIL(&chardevs, chr, next);
}
+ return chr;
}
void qemu_chr_close(CharDriverState *chr)
{
+ TAILQ_REMOVE(&chardevs, chr, next);
if (chr->chr_close)
chr->chr_close(chr);
+ qemu_free(chr->filename);
+ qemu_free(chr->label);
qemu_free(chr);
}
+void qemu_chr_info(void)
+{
+ CharDriverState *chr;
+
+ TAILQ_FOREACH(chr, &chardevs, next) {
+ term_printf("%s: filename=%s\n", chr->label, chr->filename);
+ }
+}
+
/***********************************************************/
/* network device redirectors */
@@ -9719,7 +9747,7 @@ int main(int argc, char **argv)
}
}
if (monitor_device) {
- monitor_hd = qemu_chr_open(monitor_device);
+ monitor_hd = qemu_chr_open("monitor", monitor_device);
if (!monitor_hd) {
fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
exit(1);
@@ -9730,7 +9758,9 @@ int main(int argc, char **argv)
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
const char *devname = serial_devices[i];
if (devname && strcmp(devname, "none")) {
- serial_hds[i] = qemu_chr_open(devname);
+ char label[32];
+ snprintf(label, sizeof(label), "serial%d", i);
+ serial_hds[i] = qemu_chr_open(label, devname);
if (!serial_hds[i]) {
fprintf(stderr, "qemu: could not open serial device '%s'\n",
devname);
@@ -9744,7 +9774,9 @@ int main(int argc, char **argv)
for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
const char *devname = parallel_devices[i];
if (devname && strcmp(devname, "none")) {
- parallel_hds[i] = qemu_chr_open(devname);
+ char label[32];
+ snprintf(label, sizeof(label), "parallel%d", i);
+ parallel_hds[i] = qemu_chr_open(label, devname);
if (!parallel_hds[i]) {
fprintf(stderr, "qemu: could not open parallel device '%s'\n",
devname);
--
1.5.6.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
2008-10-31 17:37 ` Anthony Liguori
2008-10-31 12:47 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
3 siblings, 1 reply; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This patch creates a new source and header file qemu-sockets.[ch] with
a bunch of helper functions to create listening and connected sockets.
New features of this code are (a) support for searching for a free port
in a given range and (b) support for IPv6.
The following patches put that code into use.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
Makefile.target | 1 +
qemu-sockets.c | 409 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-sockets.h | 8 +
vl.c | 16 ++
4 files changed, 434 insertions(+), 0 deletions(-)
create mode 100644 qemu-sockets.c
create mode 100644 qemu-sockets.h
diff --git a/Makefile.target b/Makefile.target
index 547ac02..5f71972 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -580,6 +580,7 @@ ifndef CONFIG_USER_ONLY
OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o
OBJS+=fw_cfg.o aio.o buffered_file.o migration.o migration-tcp.o
+OBJS+=qemu-sockets.o
ifdef CONFIG_WIN32
OBJS+=block-raw-win32.o
else
diff --git a/qemu-sockets.c b/qemu-sockets.c
new file mode 100644
index 0000000..9944d42
--- /dev/null
+++ b/qemu-sockets.c
@@ -0,0 +1,409 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef _WIN32
+# define WINVER 0x0501 /* needed for ipv6 bits */
+# include <windows.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# define AI_ADDRCONFIG 0
+# define close closesocket
+#else
+# include <unistd.h>
+# include <errno.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <netdb.h>
+#endif
+
+#include "qemu-sockets.h"
+
+static int default_family = PF_UNSPEC;
+static int sockets_debug = 0;
+static const int on=1, off=0;
+
+static int inet_getport(struct addrinfo *e)
+{
+ struct sockaddr_in *i4;
+ struct sockaddr_in6 *i6;
+
+ switch (e->ai_family) {
+ case PF_INET6:
+ i6 = (void*)e->ai_addr;
+ return ntohs(i6->sin6_port);
+ case PF_INET:
+ i4 = (void*)e->ai_addr;
+ return ntohs(i4->sin_port);
+ default:
+ return 0;
+ }
+}
+
+static void inet_setport(struct addrinfo *e, int port)
+{
+ struct sockaddr_in *i4;
+ struct sockaddr_in6 *i6;
+
+ switch (e->ai_family) {
+ case PF_INET6:
+ i6 = (void*)e->ai_addr;
+ i6->sin6_port = htons(port);
+ break;
+ case PF_INET:
+ i4 = (void*)e->ai_addr;
+ i4->sin_port = htons(port);
+ break;
+ }
+}
+
+static const char *inet_strfamily(int family)
+{
+ switch (family) {
+ case PF_INET6: return "ipv6";
+ case PF_INET: return "ipv4";
+ case PF_UNIX: return "unix";
+ }
+ return "????";
+}
+
+static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
+{
+ struct addrinfo *e;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
+ tag, inet_strfamily(e->ai_family), uaddr, uport);
+ }
+}
+
+void inet_set_ipv4(void)
+{
+ default_family = PF_INET;
+}
+
+void inet_set_ipv6(void)
+{
+ default_family = PF_INET6;
+}
+
+int inet_listen(const char *str, char *ostr, int olen,
+ int socktype, int port_offset)
+{
+ struct addrinfo ai,*res,*e;
+ char addr[64];
+ char port[33];
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ const char *opts, *h;
+ int slisten,rc,pos,to,try_next;
+
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ ai.ai_family = default_family;
+ ai.ai_socktype = socktype;
+
+ /* parse string */
+ if (str[0] == ':') {
+ /* no host given */
+ strcpy(addr,"");
+ if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
+ fprintf(stderr, "%s: portonly parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ } else if (str[0] == '[') {
+ /* IPv6 addr */
+ if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET6;
+ } else if (isdigit(str[0])) {
+ /* IPv4 addr */
+ if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET;
+ } else {
+ /* hostname */
+ if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: hostname parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ }
+ opts = str + pos;
+ h = strstr(opts, ",to=");
+ to = h ? atoi(h+4) : 0;
+
+ /* lookup */
+ if (port_offset)
+ snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
+ if (0 != (rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res))) {
+ fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
+ addr, port, gai_strerror(rc));
+ return -1;
+ }
+ if (sockets_debug)
+ inet_print_addrinfo(__FUNCTION__, res);
+
+ /* create socket + bind */
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (-1 == (slisten = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), strerror(errno));
+ continue;
+ }
+
+ setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+#ifdef IPV6_V6ONLY
+ if (e->ai_family == PF_INET6) {
+ if (default_family == PF_INET6) {
+ /* user specified -ipv6: listen on ipv6 only */
+ setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&on,sizeof(on));
+ } else {
+ /* default: listen on both ipv4 and ipv6 */
+ setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,sizeof(off));
+ }
+ }
+#endif
+
+ for (;;) {
+ if (0 == bind(slisten, e->ai_addr, e->ai_addrlen)) {
+ if (sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), uaddr, inet_getport(e));
+ goto listen;
+ }
+ try_next = to && (inet_getport(e) <= to + port_offset);
+ if (!try_next || sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), uaddr, inet_getport(e),
+ strerror(errno));
+ if (try_next) {
+ inet_setport(e, inet_getport(e) + 1);
+ continue;
+ }
+ break;
+ }
+ close(slisten);
+ }
+ fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
+ freeaddrinfo(res);
+ return -1;
+
+listen:
+ if (0 != listen(slisten,1)) {
+ perror("listen");
+ close(slisten);
+ return -1;
+ }
+ if (ostr) {
+ if (e->ai_family == PF_INET6) {
+ snprintf(ostr, olen, "[%s]:%d%s", uaddr,
+ inet_getport(e) - port_offset, opts);
+ } else {
+ snprintf(ostr, olen, "%s:%d%s", uaddr,
+ inet_getport(e) - port_offset, opts);
+ }
+ }
+ freeaddrinfo(res);
+ return slisten;
+}
+
+int inet_connect(const char *str, int socktype)
+{
+ struct addrinfo ai,*res,*e;
+ char addr[64];
+ char port[33];
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int sock,rc;
+
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+ ai.ai_family = default_family;
+ ai.ai_socktype = socktype;
+
+ /* parse string */
+ if (str[0] == '[') {
+ /* IPv6 addr */
+ if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
+ fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET6;
+ } else if (isdigit(str[0])) {
+ /* IPv4 addr */
+ if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
+ fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ ai.ai_family = PF_INET;
+ } else {
+ /* hostname */
+ if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
+ fprintf(stderr, "%s: hostname parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ }
+
+ /* lookup */
+ if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
+ addr, port);
+ return -1;
+ }
+ if (sockets_debug)
+ inet_print_addrinfo(__FUNCTION__, res);
+
+ for (e = res; e != NULL; e = e->ai_next) {
+ if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
+ continue;
+ }
+ if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), strerror(errno));
+ continue;
+ }
+ setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+
+ /* connect to peer */
+ if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
+ if (sockets_debug || NULL == e->ai_next)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family),
+ e->ai_canonname, uaddr, uport, strerror(errno));
+ close(sock);
+ continue;
+ }
+ if (sockets_debug)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
+ inet_strfamily(e->ai_family),
+ e->ai_canonname, uaddr, uport);
+ freeaddrinfo(res);
+ return sock;
+ }
+ freeaddrinfo(res);
+ return -1;
+}
+
+#ifndef _WIN32
+
+int unix_listen(const char *str, char *ostr, int olen)
+{
+ struct sockaddr_un un;
+ char *path, *opts;
+ int sock, fd, len;
+
+ if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ opts = strchr(str, ',');
+ if (opts) {
+ len = opts - str;
+ path = malloc(len+1);
+ snprintf(path, len+1, "%.*s", len, str);
+ } else
+ path = strdup(str);
+
+ un.sun_family = AF_UNIX;
+ if (path && strlen(path)) {
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ } else {
+ char *tmpdir = getenv("TMPDIR");
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
+ tmpdir ? tmpdir : "/tmp");
+ /*
+ * This dummy fd usage silences the mktemp() unsecure warning.
+ * Using mkstemp() doesn't make things more secure here
+ * though. bind() complains about existing files, so we have
+ * to unlink first and thus re-open the race window. The
+ * worst case possible is bind() failing, i.e. a DoS attack.
+ */
+ fd = mkstemp(un.sun_path); close(fd);
+ }
+ snprintf(ostr, olen, "%s%s", un.sun_path, opts ? opts : "");
+
+ unlink(un.sun_path);
+ if (bind(sock, (struct sockaddr*) &un, sizeof(un)) == -1) {
+ fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+ if (listen(sock, 1) == -1) {
+ fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
+ free(path);
+ return sock;
+
+err:
+ free(path);
+ close(sock);
+ return -1;
+}
+
+int unix_connect(const char *path)
+{
+ struct sockaddr_un un;
+ int sock;
+
+ if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ if (-1 == connect(sock, (struct sockaddr*) &un, sizeof(un))) {
+ fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "connect(unix:%s): OK\n", path);
+ return sock;
+}
+
+#else
+
+int unix_listen(const char *path, char *ostr, int olen)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+int unix_connect(const char *path)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+#endif
diff --git a/qemu-sockets.h b/qemu-sockets.h
new file mode 100644
index 0000000..8793af3
--- /dev/null
+++ b/qemu-sockets.h
@@ -0,0 +1,8 @@
+int inet_listen(const char *str, char *ostr, int olen,
+ int socktype, int port_offset);
+int inet_connect(const char *str, int socktype);
+void inet_set_ipv4(void);
+void inet_set_ipv6(void);
+
+int unix_listen(const char *path, char *ostr, int olen);
+int unix_connect(const char *path);
diff --git a/vl.c b/vl.c
index a9fae17..e9fe478 100644
--- a/vl.c
+++ b/vl.c
@@ -37,6 +37,7 @@
#include "gdbstub.h"
#include "qemu-timer.h"
#include "qemu-char.h"
+#include "qemu-sockets.h"
#include "block.h"
#include "audio/audio.h"
#include "migration.h"
@@ -8280,6 +8281,9 @@ static void help(int exitcode)
"-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
" redirect TCP or UDP connections from host to guest [-net user]\n"
#endif
+ "-ipv4 Use IPv4 only.\n"
+ "-ipv6 Use UPv6 only.\n"
+ " By default qemu uses what is available on your machine.\n"
"\n"
"Linux boot specific:\n"
"-kernel bzImage use 'bzImage' as kernel image\n"
@@ -8386,6 +8390,8 @@ enum {
QEMU_OPTION_bootp,
QEMU_OPTION_smb,
QEMU_OPTION_redir,
+ QEMU_OPTION_ipv4,
+ QEMU_OPTION_ipv6,
QEMU_OPTION_kernel,
QEMU_OPTION_append,
@@ -8489,6 +8495,10 @@ static const QEMUOption qemu_options[] = {
#endif
{ "redir", HAS_ARG, QEMU_OPTION_redir },
#endif
+ { "4", 0, QEMU_OPTION_ipv4 },
+ { "6", 0, QEMU_OPTION_ipv6 },
+ { "ipv4", 0, QEMU_OPTION_ipv4 },
+ { "ipv6", 0, QEMU_OPTION_ipv6 },
{ "kernel", HAS_ARG, QEMU_OPTION_kernel },
{ "append", HAS_ARG, QEMU_OPTION_append },
@@ -9151,6 +9161,12 @@ int main(int argc, char **argv)
net_slirp_redir(optarg);
break;
#endif
+ case QEMU_OPTION_ipv4:
+ inet_set_ipv4();
+ break;
+ case QEMU_OPTION_ipv6:
+ inet_set_ipv6();
+ break;
#ifdef HAS_AUDIO
case QEMU_OPTION_audio_help:
AUD_help ();
--
1.5.6.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation.
2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
3 siblings, 0 replies; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This patch switches the vnc code ofer to the new socket helper
functions.
It adds support IPv6 support and for automatically allocating an unused
vnc display port. The latter is handled ising a to= option, specifying
the upper limit for the display number to try. Scanning is started at
the display number given in the display specification, i.e. this command
line:
-vnc localhost:7,to=11
will try displays 7 to 11 (inclusive).
The display actually allocated can be queried using the "info vnc"
monitor command.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
vl.c | 1 +
vnc.c | 114 +++++++++++++++-------------------------------------------------
2 files changed, 28 insertions(+), 87 deletions(-)
diff --git a/vl.c b/vl.c
index e9fe478..e4648f9 100644
--- a/vl.c
+++ b/vl.c
@@ -8284,6 +8284,7 @@ static void help(int exitcode)
"-ipv4 Use IPv4 only.\n"
"-ipv6 Use UPv6 only.\n"
" By default qemu uses what is available on your machine.\n"
+ " Applies to -vnc.\n"
"\n"
"Linux boot specific:\n"
"-kernel bzImage use 'bzImage' as kernel image\n"
diff --git a/vnc.c b/vnc.c
index 9df4dbe..9aeaaa0 100644
--- a/vnc.c
+++ b/vnc.c
@@ -28,6 +28,7 @@
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
+#include "qemu-sockets.h"
#define VNC_REFRESH_INTERVAL (1000 / 30)
@@ -2139,8 +2140,6 @@ static void vnc_listen_read(void *opaque)
}
}
-extern int parse_host_port(struct sockaddr_in *saddr, const char *str);
-
void vnc_display_init(DisplayState *ds)
{
VncState *vs;
@@ -2291,18 +2290,11 @@ int vnc_display_password(DisplayState *ds, const char *password)
int vnc_display_open(DisplayState *ds, const char *display)
{
- struct sockaddr *addr;
- struct sockaddr_in iaddr;
-#ifndef _WIN32
- struct sockaddr_un uaddr;
- const char *p;
-#endif
- int reuse_addr, ret;
- socklen_t addrlen;
VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
const char *options;
int password = 0;
int reverse = 0;
+ int to_port = 0;
#ifdef CONFIG_VNC_TLS
int tls = 0, x509 = 0;
#endif
@@ -2321,6 +2313,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
password = 1; /* Require password auth */
} else if (strncmp(options, "reverse", 7) == 0) {
reverse = 1;
+ } else if (strncmp(options, "to=", 3) == 0) {
+ to_port = atoi(options+3) + 5900;
#ifdef CONFIG_VNC_TLS
} else if (strncmp(options, "tls", 3) == 0) {
tls = 1; /* Require TLS */
@@ -2398,67 +2392,14 @@ int vnc_display_open(DisplayState *ds, const char *display)
}
#endif
}
-#ifndef _WIN32
- if (strstart(display, "unix:", &p)) {
- addr = (struct sockaddr *)&uaddr;
- addrlen = sizeof(uaddr);
-
- vs->lsock = socket(PF_UNIX, SOCK_STREAM, 0);
- if (vs->lsock == -1) {
- fprintf(stderr, "Could not create socket\n");
- free(vs->display);
- vs->display = NULL;
- return -1;
- }
-
- uaddr.sun_family = AF_UNIX;
- memset(uaddr.sun_path, 0, 108);
- snprintf(uaddr.sun_path, 108, "%s", p);
-
- if (!reverse) {
- unlink(uaddr.sun_path);
- }
- } else
-#endif
- {
- addr = (struct sockaddr *)&iaddr;
- addrlen = sizeof(iaddr);
-
- if (parse_host_port(&iaddr, display) < 0) {
- fprintf(stderr, "Could not parse VNC address\n");
- free(vs->display);
- vs->display = NULL;
- return -1;
- }
-
- iaddr.sin_port = htons(ntohs(iaddr.sin_port) + (reverse ? 0 : 5900));
-
- vs->lsock = socket(PF_INET, SOCK_STREAM, 0);
- if (vs->lsock == -1) {
- fprintf(stderr, "Could not create socket\n");
- free(vs->display);
- vs->display = NULL;
- return -1;
- }
-
- reuse_addr = 1;
- ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR,
- (const char *)&reuse_addr, sizeof(reuse_addr));
- if (ret == -1) {
- fprintf(stderr, "setsockopt() failed\n");
- close(vs->lsock);
- vs->lsock = -1;
- free(vs->display);
- vs->display = NULL;
- return -1;
- }
- }
if (reverse) {
- if (connect(vs->lsock, addr, addrlen) == -1) {
- fprintf(stderr, "Connection to VNC client failed\n");
- close(vs->lsock);
- vs->lsock = -1;
+ /* connect to viewer */
+ if (strncmp(display, "unix:", 5) == 0)
+ vs->lsock = unix_connect(display+5);
+ else
+ vs->lsock = inet_connect(display, SOCK_STREAM);
+ if (-1 == vs->lsock) {
free(vs->display);
vs->display = NULL;
return -1;
@@ -2466,26 +2407,25 @@ int vnc_display_open(DisplayState *ds, const char *display)
vs->csock = vs->lsock;
vs->lsock = -1;
vnc_connect(vs);
- return 0;
}
- }
-
- if (bind(vs->lsock, addr, addrlen) == -1) {
- fprintf(stderr, "bind() failed\n");
- close(vs->lsock);
- vs->lsock = -1;
- free(vs->display);
- vs->display = NULL;
- return -1;
- }
+ return 0;
- if (listen(vs->lsock, 1) == -1) {
- fprintf(stderr, "listen() failed\n");
- close(vs->lsock);
- vs->lsock = -1;
- free(vs->display);
- vs->display = NULL;
- return -1;
+ } else {
+ /* listen for connects */
+ char *dpy;
+ dpy = qemu_malloc(256);
+ if (strncmp(display, "unix:", 5) == 0) {
+ strcpy(dpy, "unix:");
+ vs->lsock = unix_listen(display, dpy+5, 256-5);
+ } else {
+ vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900);
+ }
+ if (-1 == vs->lsock) {
+ free(dpy);
+ } else {
+ free(vs->display);
+ vs->display = dpy;
+ }
}
return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
--
1.5.6.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions.
2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
` (2 preceding siblings ...)
2008-10-31 12:47 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
3 siblings, 0 replies; 16+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This switches the tcp, telnet and unix socket support for character
devices (serial/parallel, ...) to the new socket helpers. Thereby they
gain IPv6 support and also get ability to search for a free tcp port.
Syntax is the same as for vnc, using a to= option, like this:
-serial tcp:localhost:5000,to=5099,server
This will check the 5000 -> 5099 port range (inclusive) for a free tcp
port. Likewise you can get auto-allocated unix sockets by specifying an
empty path:
-serial unix:,server
qemu will create a randomly named socket in $TMPDIR then. You can use
the "info chardev" command added by the first patch in this series to
figure the tcp port / unix socket actually allocated.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
vl.c | 131 +++++++++++++++++-------------------------------------------------
1 files changed, 33 insertions(+), 98 deletions(-)
diff --git a/vl.c b/vl.c
index e4648f9..b6c61be 100644
--- a/vl.c
+++ b/vl.c
@@ -3370,9 +3370,6 @@ static void udp_chr_update_read_handler(CharDriverState *chr)
}
}
-#ifndef _WIN32
-static int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
-#endif
int parse_host_src_port(struct sockaddr_in *haddr,
struct sockaddr_in *saddr,
const char *str);
@@ -3629,32 +3626,11 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
- int fd = -1, ret, err, val;
+ int fd = -1, offset = 0;
int is_listen = 0;
int is_waitconnect = 1;
int do_nodelay = 0;
const char *ptr;
- struct sockaddr_in saddr;
-#ifndef _WIN32
- struct sockaddr_un uaddr;
-#endif
- struct sockaddr *addr;
- socklen_t addrlen;
-
-#ifndef _WIN32
- if (is_unix) {
- addr = (struct sockaddr *)&uaddr;
- addrlen = sizeof(uaddr);
- if (parse_unix_path(&uaddr, host_str) < 0)
- goto fail;
- } else
-#endif
- {
- addr = (struct sockaddr *)&saddr;
- addrlen = sizeof(saddr);
- if (parse_host_port(&saddr, host_str) < 0)
- goto fail;
- }
ptr = host_str;
while((ptr = strchr(ptr,','))) {
@@ -3665,6 +3641,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
is_waitconnect = 0;
} else if (!strncmp(ptr,"nodelay",6)) {
do_nodelay = 1;
+ } else if (!strncmp(ptr,"to=",3)) {
+ /* nothing, inet_listen() parses this one */;
} else {
printf("Unknown option: %s\n", ptr);
goto fail;
@@ -3680,13 +3658,31 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
if (!s)
goto fail;
-#ifndef _WIN32
- if (is_unix)
- fd = socket(PF_UNIX, SOCK_STREAM, 0);
- else
-#endif
- fd = socket(PF_INET, SOCK_STREAM, 0);
-
+ if (is_listen) {
+ chr->filename = qemu_malloc(256);
+ if (is_unix) {
+ strcpy(chr->filename, "unix:");
+ } else if (is_telnet) {
+ strcpy(chr->filename, "telnet:");
+ } else {
+ strcpy(chr->filename, "tcp:");
+ }
+ offset = strlen(chr->filename);
+ }
+ if (is_unix) {
+ if (is_listen) {
+ fd = unix_listen(host_str, chr->filename + offset, 256 - offset);
+ } else {
+ fd = unix_connect(host_str);
+ }
+ } else {
+ if (is_listen) {
+ fd = inet_listen(host_str, chr->filename + offset, 256 - offset,
+ SOCK_STREAM, 0);
+ } else {
+ fd = inet_connect(host_str, SOCK_STREAM);
+ }
+ }
if (fd < 0)
goto fail;
@@ -3704,61 +3700,20 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
chr->chr_close = tcp_chr_close;
if (is_listen) {
- /* allow fast reuse */
-#ifndef _WIN32
- if (is_unix) {
- char path[109];
- pstrcpy(path, sizeof(path), uaddr.sun_path);
- unlink(path);
- } else
-#endif
- {
- val = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
- }
-
- ret = bind(fd, addr, addrlen);
- if (ret < 0)
- goto fail;
-
- ret = listen(fd, 0);
- if (ret < 0)
- goto fail;
-
s->listen_fd = fd;
qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
if (is_telnet)
s->do_telnetopt = 1;
} else {
- for(;;) {
- ret = connect(fd, addr, addrlen);
- if (ret < 0) {
- err = socket_error();
- if (err == EINTR || err == EWOULDBLOCK) {
- } else if (err == EINPROGRESS) {
- break;
-#ifdef _WIN32
- } else if (err == WSAEALREADY) {
- break;
-#endif
- } else {
- goto fail;
- }
- } else {
- s->connected = 1;
- break;
- }
- }
+ s->connected = 1;
s->fd = fd;
socket_set_nodelay(fd);
- if (s->connected)
- tcp_chr_connect(chr);
- else
- qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+ tcp_chr_connect(chr);
}
if (is_listen && is_waitconnect) {
- printf("QEMU waiting for connection on: %s\n", host_str);
+ printf("QEMU waiting for connection on: %s\n",
+ chr->filename ? chr->filename : host_str);
tcp_chr_accept(chr);
socket_set_nonblock(s->listen_fd);
}
@@ -4035,26 +3990,6 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str)
return 0;
}
-#ifndef _WIN32
-static int parse_unix_path(struct sockaddr_un *uaddr, const char *str)
-{
- const char *p;
- int len;
-
- len = MIN(108, strlen(str));
- p = strchr(str, ',');
- if (p)
- len = MIN(len, p - str);
-
- memset(uaddr, 0, sizeof(*uaddr));
-
- uaddr->sun_family = AF_UNIX;
- memcpy(uaddr->sun_path, str, len);
-
- return 0;
-}
-#endif
-
/* find or alloc a new VLAN */
VLANState *qemu_find_vlan(int id)
{
@@ -8284,7 +8219,7 @@ static void help(int exitcode)
"-ipv4 Use IPv4 only.\n"
"-ipv6 Use UPv6 only.\n"
" By default qemu uses what is available on your machine.\n"
- " Applies to -vnc.\n"
+ " Applies to -vnc and -serial tcp.\n"
"\n"
"Linux boot specific:\n"
"-kernel bzImage use 'bzImage' as kernel image\n"
--
1.5.6.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 1/4] Implement "info chardev" command.
2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
@ 2008-10-31 17:32 ` Anthony Liguori
0 siblings, 0 replies; 16+ messages in thread
From: Anthony Liguori @ 2008-10-31 17:32 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Gerd Hoffmann wrote:
> This patch makes qemu keep track of the character devices in use and
> implements a "info chardev" monitor command to print a list.
>
> qemu_chr_open() sticks the devices into a linked list now. It got a new
> argument (label), so there is a name for each device. It also assigns a
> filename to each character device. By default it just copyes the
> filename passed in. Individual drivers can fill in something else
> though. qemu_chr_open_pty() sets the filename to name of the pseudo tty
> allocated.
>
> Output looks like this:
>
> (qemu) info chardev
> monitor: filename=unix:/tmp/run.sh-26827/monitor,server,nowait
> serial0: filename=unix:/tmp/run.sh-26827/console,server
> serial1: filename=pty:/dev/pts/5
> parallel0: filename=vc:640x480
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>
Applied. Thanks.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
@ 2008-10-31 17:37 ` Anthony Liguori
2008-10-31 17:50 ` Daniel P. Berrange
2008-11-03 15:35 ` Gerd Hoffmann
0 siblings, 2 replies; 16+ messages in thread
From: Anthony Liguori @ 2008-10-31 17:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Gerd Hoffmann wrote:
> This patch creates a new source and header file qemu-sockets.[ch] with
> a bunch of helper functions to create listening and connected sockets.
>
> New features of this code are (a) support for searching for a free port
> in a given range and (b) support for IPv6.
>
> The following patches put that code into use.
>
I like this patch, but we already have a qemu_socket.h. Please remove
qemu_socket.h if you're going to introduce qemu-sockets.h.
> +
> +listen:
> + if (0 != listen(slisten,1)) {
>
Please try to avoid this style of if().
> diff --git a/vl.c b/vl.c
> index a9fae17..e9fe478 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -37,6 +37,7 @@
> #include "gdbstub.h"
> #include "qemu-timer.h"
> #include "qemu-char.h"
> +#include "qemu-sockets.h"
> #include "block.h"
> #include "audio/audio.h"
> #include "migration.h"
> @@ -8280,6 +8281,9 @@ static void help(int exitcode)
> "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
> " redirect TCP or UDP connections from host to guest [-net user]\n"
> #endif
> + "-ipv4 Use IPv4 only.\n"
> + "-ipv6 Use UPv6 only.\n"
> + " By default qemu uses what is available on your machine.\n"
> "\n"
> "Linux boot specific:\n"
> "-kernel bzImage use 'bzImage' as kernel image\n"
> @@ -8386,6 +8390,8 @@ enum {
> QEMU_OPTION_bootp,
> QEMU_OPTION_smb,
> QEMU_OPTION_redir,
> + QEMU_OPTION_ipv4,
> + QEMU_OPTION_ipv6,
>
> QEMU_OPTION_kernel,
> QEMU_OPTION_append,
> @@ -8489,6 +8495,10 @@ static const QEMUOption qemu_options[] = {
> #endif
> { "redir", HAS_ARG, QEMU_OPTION_redir },
> #endif
> + { "4", 0, QEMU_OPTION_ipv4 },
> + { "6", 0, QEMU_OPTION_ipv6 },
> + { "ipv4", 0, QEMU_OPTION_ipv4 },
> + { "ipv6", 0, QEMU_OPTION_ipv6 },
I don't like the idea of aliasing these options. Please just stick with
one set of options.
And do we really need to have options for this? Can't we just do the
right thing? I can't believe that every application has to have an ipv6
switch to be ipv6 enabled.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-31 17:37 ` Anthony Liguori
@ 2008-10-31 17:50 ` Daniel P. Berrange
2008-10-31 17:58 ` Anthony Liguori
2008-11-03 15:35 ` Gerd Hoffmann
1 sibling, 1 reply; 16+ messages in thread
From: Daniel P. Berrange @ 2008-10-31 17:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
On Fri, Oct 31, 2008 at 12:37:16PM -0500, Anthony Liguori wrote:
> Gerd Hoffmann wrote:
> >@@ -8280,6 +8281,9 @@ static void help(int exitcode)
> > "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
> > " redirect TCP or UDP connections from host to
> > guest [-net user]\n"
> > #endif
> >+ "-ipv4 Use IPv4 only.\n"
> >+ "-ipv6 Use UPv6 only.\n"
> >+ " By default qemu uses what is available on
> >your machine.\n"
> > "\n"
> > "Linux boot specific:\n"
> > "-kernel bzImage use 'bzImage' as kernel image\n"
> >@@ -8386,6 +8390,8 @@ enum {
> > QEMU_OPTION_bootp,
> > QEMU_OPTION_smb,
> > QEMU_OPTION_redir,
> >+ QEMU_OPTION_ipv4,
> >+ QEMU_OPTION_ipv6,
> >
> > QEMU_OPTION_kernel,
> > QEMU_OPTION_append,
> >@@ -8489,6 +8495,10 @@ static const QEMUOption qemu_options[] = {
> > #endif
> > { "redir", HAS_ARG, QEMU_OPTION_redir },
> > #endif
> >+ { "4", 0, QEMU_OPTION_ipv4 },
> >+ { "6", 0, QEMU_OPTION_ipv6 },
> >+ { "ipv4", 0, QEMU_OPTION_ipv4 },
> >+ { "ipv6", 0, QEMU_OPTION_ipv6 },
>
> I don't like the idea of aliasing these options. Please just stick with
> one set of options.
>
> And do we really need to have options for this? Can't we just do the
> right thing? I can't believe that every application has to have an ipv6
> switch to be ipv6 enabled.
Gerd's patch does 'do the right thing' for 90% of people - it uses the
AI_ADDRCONFIG flag. This means that the getaddrinfo() name lookup will
only return an IPv6 address if there is at least one NIC with IPv6
configured, likewise only return an IPv4 address if there is at least
one NIC with IPv4 configured. There are still times when it is desirable
to restrict it to just IPv4 or just IPv6, regardless of your host NIC
config - mostly when needing to interact with other broken apps which are
not correctly IPv6 aware. Maybe it would be sufficient to just have it added
as a flag to the arg in question though, eg -serial tcp:localhost:123,ipv4
rather than a global -ipv4 flag
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-31 17:50 ` Daniel P. Berrange
@ 2008-10-31 17:58 ` Anthony Liguori
2008-10-31 19:44 ` Jamie Lokier
0 siblings, 1 reply; 16+ messages in thread
From: Anthony Liguori @ 2008-10-31 17:58 UTC (permalink / raw)
To: Daniel P. Berrange, qemu-devel; +Cc: Gerd Hoffmann
Daniel P. Berrange wrote:
> On Fri, Oct 31, 2008 at 12:37:16PM -0500, Anthony Liguori wrote:
>
> Gerd's patch does 'do the right thing' for 90% of people - it uses the
> AI_ADDRCONFIG flag. This means that the getaddrinfo() name lookup will
> only return an IPv6 address if there is at least one NIC with IPv6
> configured, likewise only return an IPv4 address if there is at least
> one NIC with IPv4 configured. There are still times when it is desirable
> to restrict it to just IPv4 or just IPv6, regardless of your host NIC
> config - mostly when needing to interact with other broken apps which are
> not correctly IPv6 aware. Maybe it would be sufficient to just have it added
> as a flag to the arg in question though, eg -serial tcp:localhost:123,ipv4
> rather than a global -ipv4 flag
>
Yes, this would be much nicer. I think having the options as a global
is likely to confuse users.
Regards,
Anthony Liguori
> Daniel
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-31 17:58 ` Anthony Liguori
@ 2008-10-31 19:44 ` Jamie Lokier
0 siblings, 0 replies; 16+ messages in thread
From: Jamie Lokier @ 2008-10-31 19:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Anthony Liguori wrote:
> >Maybe it would be sufficient to just have it added as a flag to the
> >arg in question though, eg -serial tcp:localhost:123,ipv4 rather
> >than a global -ipv4 flag
> >
>
> Yes, this would be much nicer. I think having the options as a global
> is likely to confuse users.
Yes. In those few cases where you need to restrict it, a global
option would be wrong too: if you have multiple interfaces, and you
need to override the automatic default for some reason, there's a fair
chance you don't need that on every interface.
-- Jamie
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
2008-10-31 17:37 ` Anthony Liguori
2008-10-31 17:50 ` Daniel P. Berrange
@ 2008-11-03 15:35 ` Gerd Hoffmann
1 sibling, 0 replies; 16+ messages in thread
From: Gerd Hoffmann @ 2008-11-03 15:35 UTC (permalink / raw)
To: Anthony Liguori; +Cc: qemu-devel
Anthony Liguori wrote:
> I like this patch, but we already have a qemu_socket.h. Please remove
> qemu_socket.h if you're going to introduce qemu-sockets.h.
Will fix.
>> +
>> +listen:
>> + if (0 != listen(slisten,1)) {
>
> Please try to avoid this style of if().
Yea, old habit from the days where gcc didn't warn on "if (a = -1)".
Pointless these days. Trying to get rid of it ...
>> + { "ipv4", 0, QEMU_OPTION_ipv4 },
>> + { "ipv6", 0, QEMU_OPTION_ipv6 },
>
> I don't like the idea of aliasing these options. Please just stick with
> one set of options.
> And do we really need to have options for this?
Yes, we do, unfortunaly.
> Can't we just do the
> right thing? I can't believe that every application has to have an ipv6
> switch to be ipv6 enabled.
98% of the users should never ever need that. Unfortunaly there are
always corner cases where you can't get it right automatically. You can
easily check the ipv6 setup on the local machine, but you still don't
know how the network is setup and whenever reaching machine $foo via
ipv6 actually works. Of course you can just try and in case it doesn't
work fallback to ipv4. Which is what the code does btw. But that
involves a noticeable delay, waiting for the ipv6 connect attempt time out.
Will make that a per-connection option as discussed further down this
thread.
Respin of the patches will follow later today or tomorrow.
cheers,
Gerd
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-11-03 15:36 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
2008-10-31 17:32 ` Anthony Liguori
2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
2008-10-31 17:37 ` Anthony Liguori
2008-10-31 17:50 ` Daniel P. Berrange
2008-10-31 17:58 ` Anthony Liguori
2008-10-31 19:44 ` Jamie Lokier
2008-11-03 15:35 ` Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
-- strict thread matches above, loose matches on Subject: below --
2008-10-28 12:55 [Qemu-devel] [PATCH 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-28 12:55 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
2008-10-28 13:15 ` Daniel P. Berrange
2008-10-28 14:22 ` Gerd Hoffmann
2008-10-28 14:31 ` Daniel P. Berrange
2008-10-28 15:10 ` Gerd Hoffmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).