[libvirt] [PATCH] network utilities

Laine Stump laine at laine.org
Thu Oct 22 20:42:20 UTC 2009


(AHA! Just figured out how to get a patch sent as an attached file into 
the reply when using Thunderbird - 1) make sure Thunderbird is 
configured to display text attachments inline, 2) select everything in 
the original message (with ctrl-A), *then* 3) hit the reply button)

On 10/22/2009 10:46 AM, Daniel Veillard wrote:
>   Revamping my previous patches to parse IPv4/6 numeric addresses,
> adding range computation and netmask checking entries too.
>
> As suggested I used an union for the socket address type:
>
> typedef union {
>      struct sockaddr_storage stor;
>      struct sockaddr_in inet4;
>      struct sockaddr_in6 inet6;
> } virSocketAddr;
> typedef virSocketAddr *virSocketAddrPtr;
>
>    But I wonder if it's not better to make it a struct and add
> the lenght of the address as this is usually needed when passing
> one of the struct sockaddr_* and that lenght is returned as part
> of parsing, keeping both together is probably more useful.
>    A macro to get the type AF_INET/AF_INET6 out of a virSocketAddr
> might be useful instead of having the users manually guess that
> ->stor.ss_family is teh filed to look at.
>
>    Also I wonder how useful the Ipv6 code for netmask and ranges
> really is, it seems that IPv6 mostly go with prefix and never use
> netmasks, maybe that's just dead code, maybe that's just wrong,
> anyway here it is.
>
>    I named the header and file network.[ch] as I think other
> network related utilities like validation or generation of
> MAC addresses could well be moved in that file too.
>
> Daniel
>
> -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ 
> daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/ 
> http://veillard.com/ | virtualization library http://libvirt.org/
> network_util.patch
>
>
> commit d5ab523c72242d76184838d8215aa0ebe720c08e
> Author: Daniel Veillard<veillard at redhat.com>
> Date:   Thu Oct 22 16:34:43 2009 +0200
>
>      Set of new network related utilities
>
>      * src/util/network.h src/util/network.c: utilities to parse network
>        addresses, check netmask and compute ranges
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 8e27ea7..8fbfb5f 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -55,6 +55,7 @@ UTIL_SOURCES =							\
>   		util/memory.c util/memory.h			\
>   		util/pci.c util/pci.h				\
>   		util/hostusb.c util/hostusb.h			\
> +		util/network.c util/network.h			\
>   		util/qparams.c util/qparams.h			\
>   		util/stats_linux.c util/stats_linux.h		\
>   		util/storage_file.c util/storage_file.h		\
> diff --git a/src/util/network.c b/src/util/network.c
> new file mode 100644
> index 0000000..7093dce
> --- /dev/null
> +++ b/src/util/network.c
> @@ -0,0 +1,324 @@
> +/*
> + * network.c: network helper APIs for libvirt
> + *
> + * Copyright (C) 2009-2009 Red Hat, Inc.
> + *
> + * See COPYING.LIB for the License of this software
> + *
> + * Daniel Veillard<veillard at redhat.com>
> + */
> +
> +#include<config.h>
> +
> +#include "memory.h"
> +#include "network.h"
> +
> +/*
> + * Helpers to extract the IP arrays from the virSocketAddrPtr
> + * That part is the less portable of the module
> + */
> +typedef unsigned char virIPv4Addr[4];
> +typedef virIPv4Addr *virIPv4AddrPtr;
> +typedef unsigned short virIPv6Addr[8];
>    


Are you defining this an an array of short for efficiency? It's much 
easier to deal with if you just look at it as a byte array - you don't 
need to worry about network vs. host ordering when doing comparisons and 
bit shifts/masks (IPv6 addresses, like IPv4 addresses, are *always* in 
network order, even when being passed around in applications - see 
section 3.2 of
http://tools.ietf.org/html/rfc3493.)


> +typedef virIPv6Addr *virIPv6AddrPtr;
> +
> +static int getIPv4Addr(virSocketAddrPtr addr, virIPv4AddrPtr tab) {
> +    unsigned long val;
> +    int i;
> +
> +    if ((addr == NULL) || (tab == NULL) || (addr->stor.ss_family != AF_INET))
> +        return(-1);
> +
> +    val = ntohl(addr->inet4.sin_addr.s_addr);
> +
> +    for (i = 0;i<  4;i++) {
> +        (*tab)[i] = val&  0xFF;
> +        val>>= 8;
> +    }
> +
> +    return(0);
> +}
>    

I get the sense you don't like typecasts ;-)

Since the IP address is just a byte array, and the bytes are always in 
the same order whether on the network or on a little or big endian host, 
why not just do something like this:

return *((int *)&addr->inet4.sin_addr.s_addr);

(it's only the port that you need to worry about swapping)


> +
> +static int getIPv6Addr(virSocketAddrPtr addr, virIPv6AddrPtr tab) {
> +    virIPv6AddrPtr val;
> +    int i;
> +
> +    if ((addr == NULL) || (tab == NULL) || (addr->stor.ss_family != AF_INET6))
> +        return(-1);
> +
> +    val = (virIPv6AddrPtr)&(addr->inet6.sin6_addr.__in6_u.__u6_addr16);
> +
> +    for (i = 0;i<  8;i++) {
> +        (*tab)[i] = ntohs((*val)[i]);
> +    }
> +
> +    return(0);
> +}
>    

Likewise with this - the address bytes in the sockaddr_in6 are in 
network order, so the extra work of swapping the bytes is going to mess 
things up. You could do a loop copying each element of __u6_addr8, 
__u6_addr16, or __u6_addr32, or you could just take advantage of struct 
assignment and do it with a single line of code:

     *val = *((virIPv6AddrPtr) &(addr->inet6.sin6_addr.in6_u));

(I think I got the *'s and &'s right ;-)

> +/**
> + * virSocketParseAddr:
> + * @val: a numeric network address IPv4 or IPv6
> + * @addr: where to store the return value.
> + * @hint: optional hint to pass down to getaddrinfo
> + *
> + * Mostly a wrapper for getaddrinfo() extracting the address storage
> + * from the numeric string like 1.2.3.4 or 2001:db8:85a3:0:0:8a2e:370:7334
> + *
> + * Returns the lenght of the network address or -1 in case of error.
>    


(Too bad getaddrinfo doesn't return a pointer just past the last byte it 
counted as part of the address, or at least the number of characters it 
ate - that would be a much more useful return value, since the length of 
the address is already implied in the address family that's set in the 
sockaddr.)


> + */
> +int
> +virSocketParseAddr(const char *val, virSocketAddrPtr addr, int hint) {
> +    int len;
> +    struct addrinfo hints;
> +    struct addrinfo *res = NULL;
> +
> +    if ((val == NULL) || (addr == NULL))
> +        return(-1);
> +
> +    memset(&hints, 0, sizeof(hints));
> +    hints.ai_flags = AI_NUMERICHOST | hint;
> +    if ((getaddrinfo(val, NULL,&hints,&res) != 0) || (res ==  NULL)) {
> +        return(-1);
> +    }
> +
> +    len = res->ai_addrlen;
> +    memcpy(&addr->stor, res->ai_addr, len);
> +
> +    freeaddrinfo(res);
> +    return(len);
> +}
> +
> +/*
> + * virSocketParseIpv4Addr:
> + * @val: an IPv4 numeric address
> + * @addr: the loacation to store the result
> + *
> + * Extract the address storage from an IPv4 numeric address
> + *
> + * Returns the lenght of the network address or -1 in case of error.
> + */
> +int
> +virSocketParseIpv4Addr(const char *val, virSocketAddrPtr addr) {
> +    return(virSocketParseAddr(val, addr, AF_INET));
> +}
> +
> +/*
> + * virSocketParseIpv6Addr:
> + * @val: an IPv6 numeric address
> + * @addr: the loacation to store the result
> + *
> + * Extract the address storage from an IPv6 numeric address
> + *
> + * Returns the lenght of the network address or -1 in case of error.
> + */
> +int
> +virSocketParseIpv6Addr(const char *val, virSocketAddrPtr addr) {
> +    return(virSocketParseAddr(val, addr, AF_INET6));
> +}
> +
>    


(I didn't check over the bit arithmetic in the netmask stuff, because it 
will all change anyway if you  store the IPv6 addresses as an array[16] 
of bytes rather than an array[8] of host order shorts).)


> +/**
> + * virSocketAddrIsNetmask:
> + * @netmask: the netmask address
> + *
> + * Check that @netmask is a proper network mask
> + *
> + * Returns 0 in case of success and -1 in case of error
> + */
> +int virSocketAddrIsNetmask(virSocketAddrPtr netmask) {
> +    int i;
> +
> +    if (netmask == NULL)
> +        return(-1);
> +
> +    if (netmask->stor.ss_family == AF_INET) {
> +        virIPv4Addr tm;
> +        unsigned char tmp;
> +        int ok = 0;
> +
> +        if (getIPv4Addr(netmask,&tm)<  0)
> +            return(-1);
> +
> +        for (i = 0;i<  4;i++) {
> +           if (tm[i] != 0)
> +               break;
> +        }
> +
> +        if (i>= 4)
> +            return(0);
> +
> +        tmp = 0xFF;
> +        do {
> +            if (tm[i] == tmp) {
> +                ok = 1;
> +                break;
> +            }
> +            tmp<<= 1;
> +        } while (tmp != 0);
> +        if (ok == 0)
> +            return(-1);
> +        i++;
> +
> +        if (i>= 4)
> +            return(0);
> +
> +        for (;i<  4;i++) {
> +            if (tm[i] != 0xFF)
> +                return(-1);
> +        }
> +    } else if (netmask->stor.ss_family == AF_INET6) {
> +        virIPv6Addr tm;
> +        unsigned short tmp;
> +        int ok = 0;
> +
> +        /*
> +         * Hum, on IPv6 people use prefixes instead of netmask
> +         */
> +        if (getIPv6Addr(netmask,&tm)<  0)
> +            return(-1);
> +
> +        for (i = 0;i<  8;i++) {
> +           if (tm[i] != 0)
> +               break;
> +        }
> +
> +        if (i>= 8)
> +            return(0);
> +
> +        tmp = 0xFFFF;
> +        do {
> +            if (tm[i] == tmp) {
> +                ok = 1;
> +                break;
> +            }
> +            tmp<<= 1;
> +        } while (tmp != 0);
> +        if (ok == 0)
> +            return(-1);
> +        i++;
> +
> +        if (i>= 8)
> +            return(0);
> +
> +        for (;i<  8;i++) {
> +            if (tm[i] != 0xFFFF)
> +                return(-1);
> +        }
> +    } else {
> +        return(-1);
> +    }
> +    return(0);
> +}
> +
> +/**
> + * virSocketCheckNetmask:
> + * @addr1: a first network address
> + * @addr2: a second network address
> + * @netmask: the netmask address
> + *
> + * Check that @addr1 and @addr2 pertain to the same @netmask address
> + * range and returns the size of the range
> + *
> + * Returns 1 in case of success and 0 in case of failure and
> + *         -1 in case of error
> + */
> +int virSocketCheckNetmask(virSocketAddrPtr addr1, virSocketAddrPtr addr2,
> +                          virSocketAddrPtr netmask) {
> +    int i;
> +
> +    if ((addr1 == NULL) || (addr2 == NULL) || (netmask == NULL))
> +        return(-1);
> +    if ((addr1->stor.ss_family != addr2->stor.ss_family) ||
> +        (addr1->stor.ss_family != netmask->stor.ss_family))
> +        return(-1);
> +
> +    if (virSocketAddrIsNetmask(netmask) != 0)
> +        return(-1);
> +
> +    if (addr1->stor.ss_family == AF_INET) {
> +        virIPv4Addr t1, t2, tm;
> +
> +        if ((getIPv4Addr(addr1,&t1)<  0) ||
> +            (getIPv4Addr(addr2,&t2)<  0) ||
> +            (getIPv4Addr(netmask,&tm)<  0))
> +            return(-1);
> +
> +        for (i = 0;i<  4;i++) {
> +            if ((t1[i]&  tm[i]) != (t2[i]&  tm[i]))
> +                return(0);
> +        }
> +
> +    } else if (addr1->stor.ss_family == AF_INET) {
> +        virIPv6Addr t1, t2, tm;
> +
> +        if ((getIPv6Addr(addr1,&t1)<  0) ||
> +            (getIPv6Addr(addr2,&t2)<  0) ||
> +            (getIPv6Addr(netmask,&tm)<  0))
> +            return(-1);
> +
> +        for (i = 0;i<  8;i++) {
> +            if ((t1[i]&  tm[i]) != (t2[i]&  tm[i]))
> +                return(0);
> +        }
> +
> +    } else {
> +        return(-1);
> +    }
> +    return(1);
> +}
> +
> +/**
> + * virSocketGetRange:
> + * @start: start of an IP range
> + * @end: end of an IP range
> + *
> + * Check the order of the 2 addresses and compute the range, this
> + * will return 1 for identical addresses. Errors can come from incompatible
> + * addresses type, excessive range (>= 2^^16) where the two addresses are
> + * unrelated or inverted start and end.
> + *
> + * Returns the size of the range or -1 in case of failure
> + */
> +int virSocketGetRange(virSocketAddrPtr start, virSocketAddrPtr end) {
> +    int ret = 0, i;
> +
> +    if ((start == NULL) || (end == NULL))
> +        return(-1);
> +    if (start->stor.ss_family != end->stor.ss_family)
> +        return(-1);
> +
> +    if (start->stor.ss_family == AF_INET) {
> +        virIPv4Addr t1, t2;
> +
> +        if ((getIPv4Addr(start,&t1)<  0) ||
> +            (getIPv4Addr(end,&t2)<  0))
> +            return(-1);
> +
> +        for (i = 0;i<  2;i++) {
> +            if (t1[i] != t2[i])
> +                return(-1);
> +        }
> +        ret = (t2[2] - t1[2]) * 256 + (t2[3] - t1[3]);
> +        if (ret<  0)
> +            return(-1);
> +        ret++;
> +    } else if (start->stor.ss_family == AF_INET6) {
> +        virIPv6Addr t1, t2;
> +
> +        if ((getIPv6Addr(start,&t1)<  0) ||
> +            (getIPv6Addr(end,&t2)<  0))
> +            return(-1);
> +
> +        for (i = 0;i<  7;i++) {
> +            if (t1[i] != t2[i])
> +                return(-1);
> +        }
> +        ret = t2[7] - t1[7];
> +        if (ret<  0)
> +            return(-1);
> +        ret++;
> +    } else {
> +        return(-1);
> +    }
> +    return(ret);
> +}
> +
> diff --git a/src/util/network.h b/src/util/network.h
> new file mode 100644
> index 0000000..e590747
> --- /dev/null
> +++ b/src/util/network.h
> @@ -0,0 +1,49 @@
> +/*
> + * network.h: network helper APIs for libvirt
> + *
> + * Copyright (C) 2009-2009 Red Hat, Inc.
> + *
> + * See COPYING.LIB for the License of this software
> + *
> + * Daniel Veillard<veillard at redhat.com>
> + */
> +
> +#ifndef __VIR_NETWORK_H__
> +#define __VIR_NETWORK_H__
> +
> +#include "internal.h"
> +
> +#include<sys/types.h>
> +#include<sys/socket.h>
> +#include<netdb.h>
> +
> +typedef union {
> +    struct sockaddr_storage stor;
> +    struct sockaddr_in inet4;
> +    struct sockaddr_in6 inet6;
> +} virSocketAddr;
> +typedef virSocketAddr *virSocketAddrPtr;
> +
> +int virSocketParseAddr    (const char *val,
> +                           virSocketAddrPtr addr,
> +                           int hint);
> +
> +int virSocketParseIpv4Addr(const char *val,
> +                           virSocketAddrPtr addr);
> +
> +int virSocketParseIpv6Addr(const char *val,
> +                           virSocketAddrPtr addr);
> +
> +int virSocketAddrInNetwork(virSocketAddrPtr addr1,
> +                           virSocketAddrPtr addr2,
> +                           virSocketAddrPtr netmask);
> +
> +int virSocketGetRange     (virSocketAddrPtr start,
> +                           virSocketAddrPtr end);
> +
> +int virSocketAddrIsNetmask(virSocketAddrPtr netmask);
> +
> +int virSocketCheckNetmask (virSocketAddrPtr addr1,
> +                           virSocketAddrPtr addr2,
> +                           virSocketAddrPtr netmask);
> +#endif /* __VIR_NETWORK_H__ */
>    
>
>
> --
> Libvir-list mailing list
> Libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list
>    




More information about the libvir-list mailing list