qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.


From: Daniel P. Berrange
Subject: Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
Date: Tue, 28 Oct 2008 13:15:54 +0000
User-agent: Mutt/1.4.1i

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 :|




reply via email to

[Prev in Thread] Current Thread [Next in Thread]