From: "Daniel P. Berrange" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: Gerd Hoffmann <kraxel@redhat.com>
Subject: Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
Date: Tue, 28 Oct 2008 13:15:54 +0000 [thread overview]
Message-ID: <20081028131554.GV18016@redhat.com> (raw)
In-Reply-To: <1225198518-16529-3-git-send-email-kraxel@redhat.com>
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 :|
next prev parent reply other threads:[~2008-10-28 13:15 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-10-28 12:55 [Qemu-devel] [PATCH 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-28 12:55 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
2008-10-28 17:08 ` Blue Swirl
2008-10-28 19:58 ` Gerd Hoffmann
2008-10-29 11:02 ` Gerd Hoffmann
2008-10-29 18:30 ` Blue Swirl
2008-10-28 20:19 ` Daniel P. Berrange
2008-10-28 21:28 ` 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 [this message]
2008-10-28 14:22 ` Gerd Hoffmann
2008-10-28 14:31 ` Daniel P. Berrange
2008-10-28 15:10 ` Gerd Hoffmann
2008-10-28 12:55 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
2008-10-28 17:25 ` Blue Swirl
2008-10-28 19:57 ` Gerd Hoffmann
2008-10-29 10:46 ` Gerd Hoffmann
2008-10-28 12:55 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
2008-10-28 15:53 ` [Qemu-devel] [PATCH 0/4] ipv6 and autoport patches Anthony Liguori
-- strict thread matches above, loose matches on Subject: below --
2008-10-31 12:47 [Qemu-devel] [PATCH v2 " Gerd Hoffmann
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081028131554.GV18016@redhat.com \
--to=berrange@redhat.com \
--cc=kraxel@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).