qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces


From: Kirk Allan
Subject: Re: [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32
Date: Mon, 30 Mar 2015 14:44:49 -0600

Ping.

Looking for feedback anyone might have regarding this patch.

Thanks,
Kirk

 >>>
> For Windows guest greater than or equal to Vista/2008, _WIN32_WINNT may be
> defined via the configure option with-win32-winnt=.  The use of
> with-win32-wint= to set _WIN32_WINNT is optional.  Setting this value to at
> least 0x0600 e.g. with-win32-wint=0x0600, provides access to the
> OnLinkPrefixLength fied in the IP_ADAPTER_UNICAST_ADDRESS structure which
> contains the prefix for IPv4 and IPv6 addresses.
> 
> If _WIN32_WINNT is less than 0x0600, the default, the patch will derive the
> prefix on its own.  Due to the nature of addresses and prefixes contained in
> the linked lists not being in an a predictable order, matching an IPv6 
> address
> with its corresponding prefix is not addressed in this patch and the
> unknown/undetermined value of -1 is returned.  For IPv4 addresses it is
> possible to match the address with its prefix and so the corresponding 
> prefix
> is returned.
> 
> Additionally, if _WIN32_WINNT >= 0x600 and the guest agent is also being 
> built
> for 64 bit, inet_ntop is available for retrieving the address and is used.
> Otherwise, IPv4 and IPv6 address are derived and formatted form the 
> available
> structures.
> 
> With the use of with-win32-wint= to set _WIN32_WINNT, it is possible to build
> the guest agent for 32 bit, 64 bit, 32 bit for Windows Vista/2008 or 
> greater,
> and 64 bit for Windows Vista/2008 or greater.  The non Vista/2008 builds 
> will
> run on Vista/2008 and greater without the extended functionality described
> above.
> 
> Signed-off-by: Kirk Allan <address@hidden>
> ---
>  configure            |  22 +++-
>  qga/commands-win32.c | 319 
> ++++++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 336 insertions(+), 5 deletions(-)
> 
> diff --git a/configure b/configure
> index 7ba4bcb..6767358 100755
> --- a/configure
> +++ b/configure
> @@ -317,6 +317,7 @@ bzip2=""
>  guest_agent=""
>  guest_agent_with_vss="no"
>  vss_win32_sdk=""
> +win32_winnt="0x501"
>  win_sdk="no"
>  want_tools="yes"
>  libiscsi=""
> @@ -700,9 +701,9 @@ fi
>  if test "$mingw32" = "yes" ; then
>    EXESUF=".exe"
>    DSOSUF=".dll"
> -  QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS"
> +  QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN $QEMU_CFLAGS"
>    # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later)
> -  QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS"
> +  QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 -DARCH_$ARCH $QEMU_CFLAGS"
>    LIBS="-lwinmm -lws2_32 -liphlpapi $LIBS"
>  cat > $TMPC << EOF
>  int main(void) { return 0; }
> @@ -718,7 +719,7 @@ EOF
>    sysconfdir="\${prefix}"
>    local_statedir=
>    confsuffix=""
> -  libs_qga="-lws2_32 -lwinmm -lpowrprof $libs_qga"
> +  libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi $libs_qga"
>  fi
>  
>  werror=""
> @@ -1081,6 +1082,10 @@ for opt do
>    ;;
>    --without-win-sdk) win_sdk="no"
>    ;;
> +  --with-win32-winnt) win32_winnt="0x501"
> +  ;;
> +  --with-win32-winnt=*) win32_winnt="$optarg"
> +  ;;
>    --enable-tools) want_tools="yes"
>    ;;
>    --disable-tools) want_tools="no"
> @@ -1385,6 +1390,7 @@ Advanced options (experts only):
>    --enable-guest-agent     enable building of the QEMU Guest Agent
>    --with-vss-sdk=SDK-path  enable Windows VSS support in QEMU Guest Agent
>    --with-win-sdk=SDK-path  path to Windows Platform SDK (to build VSS .tlb)
> +  --with-win32-winnt=ver   used to overwrite the default _WIN32_WINNT version
>    --disable-seccomp        disable seccomp support
>    --enable-seccomp         enable seccomp support
>    --with-coroutine=BACKEND coroutine backend. Supported options:
> @@ -3803,6 +3809,15 @@ if test "$mingw32" = "yes" -a "$guest_agent" != "no" 
> -a 
> "$guest_agent_with_vss"
>  fi
>  
>  ##########################################
> +# check if building Windows qemu-ga for for a specified _WIN32_WINNT version
> +if test "$mingw32" = "yes" -a "$guest_agent" != "no" ; then
> +  if test "$win32_winnt" = "0x501" ; then
> +    # Default to Windows XP
> +    QEMU_CFLAGS="-DWINVER=0x501 $QEMU_CFLAGS"
> +  else
> +    QEMU_CFLAGS="-D_WIN32_WINNT=$win32_winnt -DWINVER=$win32_winnt 
> $QEMU_CFLAGS"
> +  fi
> +fi
>  
>  ##########################################
>  # check if we have fdatasync
> @@ -4395,6 +4410,7 @@ echo "libiscsi support  $libiscsi"
>  echo "libnfs support    $libnfs"
>  echo "build guest agent $guest_agent"
>  echo "QGA VSS support   $guest_agent_with_vss"
> +echo "_WIN32_WINNT      $win32_winnt"
>  echo "seccomp support   $seccomp"
>  echo "coroutine backend $coroutine"
>  echo "coroutine pool    $coroutine_pool"
> diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> index 3ef0549..635d3ca 100644
> --- a/qga/commands-win32.c
> +++ b/qga/commands-win32.c
> @@ -16,11 +16,17 @@
>  #include <powrprof.h>
>  #include <stdio.h>
>  #include <string.h>
> +#include <winsock2.h>
> +#include <ws2tcpip.h>
> +#include <ws2ipdef.h>
> +#include <iptypes.h>
> +#include <iphlpapi.h>
>  #include "qga/guest-agent-core.h"
>  #include "qga/vss-win32.h"
>  #include "qga-qmp-commands.h"
>  #include "qapi/qmp/qerror.h"
>  #include "qemu/queue.h"
> +#include "qemu/host-utils.h"
>  
>  #ifndef SHTDN_REASON_FLAG_PLANNED
>  #define SHTDN_REASON_FLAG_PLANNED 0x80000000
> @@ -589,9 +595,318 @@ void qmp_guest_suspend_hybrid(Error **errp)
>      error_set(errp, QERR_UNSUPPORTED);
>  }
>  
> +#define WORKING_BUFFER_SIZE 15000
> +#define MAX_ALLOC_TRIES 2
> +
> +static DWORD guest_get_adapter_addresses(IP_ADAPTER_ADDRESSES 
> **adptr_addrs)
> +{
> +    ULONG out_buf_len = 0;
> +    int alloc_tries = 0;
> +    DWORD ret = ERROR_SUCCESS;
> +
> +    /* Allocate a 15 KB buffer to start with.  If not enough, out_buf_len
> +     * will have the amount needed after the call to GetAdaptersAddresses.
> +     */
> +    out_buf_len = WORKING_BUFFER_SIZE;
> +
> +    do {
> +        *adptr_addrs = (IP_ADAPTER_ADDRESSES *) g_malloc(out_buf_len);
> +        if (*adptr_addrs == NULL) {
> +            return ERROR_NOT_ENOUGH_MEMORY;
> +        }
> +
> +        ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
> +            NULL, *adptr_addrs, &out_buf_len);
> +
> +        if (ret == ERROR_BUFFER_OVERFLOW) {
> +            g_free(*adptr_addrs);
> +            *adptr_addrs = NULL;
> +            alloc_tries++;
> +        }
> +
> +    } while ((ret == ERROR_BUFFER_OVERFLOW) && (alloc_tries < 
> MAX_ALLOC_TRIES));
> +
> +    if (ret != ERROR_SUCCESS) {
> +        if (*adptr_addrs) {
> +            g_free(*adptr_addrs);
> +            *adptr_addrs = NULL;
> +        }
> +    }
> +    return ret;
> +}
> +
> +#if (_WIN32_WINNT < 0x0600)
> +static DWORD guest_get_adapters_info(IP_ADAPTER_INFO **adptr_info)
> +{
> +    ULONG out_buf_len = 0;
> +    int alloc_tries = 0;
> +    DWORD ret = ERROR_SUCCESS;
> +
> +    out_buf_len = sizeof(IP_ADAPTER_INFO);
> +    do {
> +        *adptr_info = (IP_ADAPTER_INFO *)g_malloc(out_buf_len);
> +        if (*adptr_info == NULL) {
> +            return ERROR_NOT_ENOUGH_MEMORY;
> +        }
> +
> +        ret = GetAdaptersInfo(*adptr_info, &out_buf_len);
> +
> +        if (ret == ERROR_BUFFER_OVERFLOW) {
> +            g_free(*adptr_info);
> +            *adptr_info = NULL;
> +            alloc_tries++;
> +        }
> +
> +    } while ((ret == ERROR_BUFFER_OVERFLOW) && (alloc_tries < 
> MAX_ALLOC_TRIES));
> +
> +    if (ret != ERROR_SUCCESS) {
> +        if (*adptr_info) {
> +            g_free(*adptr_info);
> +            *adptr_info = NULL;
> +        }
> +    }
> +    return ret;
> +}
> +#endif
> +
> +static char *guest_wcstombs_dup(WCHAR *wstr)
> +{
> +    char *str;
> +    int i;
> +
> +    i = wcslen(wstr) + 1;
> +    str = g_malloc(i);
> +    if (str) {
> +        wcstombs(str, wstr, i);
> +    }
> +    return str;
> +}
> +
> +static char *guest_inet_ntop(int af, void *cp, char *buf, socklen_t len)
> +{
> +#if (_WIN32_WINNT >= 0x0600) && defined(ARCH_x86_64)
> +    /* If built for 64 bit Windows Vista/2008 or newer, inet_ntop() is
> +     * available for use.  Otherwise, do our best to derive it.
> +     */
> +    return (char *)inet_ntop(af, cp, buf, len);
> +#else
> +    u_char *p;
> +
> +    p = (u_char *)cp;
> +    if (af == AF_INET) {
> +        snprintf(buf, len, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
> +    } else if (af == AF_INET6) {
> +        int i, compressed_zeros, added_to_buf;
> +        char t[sizeof "::ffff"];
> +
> +        buf[0] = '\0';
> +        compressed_zeros = 0;
> +        added_to_buf = 0;
> +        for (i = 0; i < 16; i += 2) {
> +            if (p[i] != 0 || p[i + 1] != 0) {
> +                if (compressed_zeros) {
> +                    if (len > sizeof "::") {
> +                        strcat(buf, "::");
> +                        len -= sizeof "::" - 1;
> +                    }
> +                    added_to_buf++;
> +                } else {
> +                    if (added_to_buf) {
> +                        if (len > 1) {
> +                            strcat(buf, ":");
> +                            len--;
> +                        }
> +                    }
> +                    added_to_buf++;
> +                }
> +
> +                /* Take into account leading zeros. */
> +                if (p[i]) {
> +                    len -= snprintf(t, sizeof(t), "%x%02x", p[i], p[i+1]);
> +                } else {
> +                    len -= snprintf(t, sizeof(t), "%x", p[i+1]);
> +                }
> +
> +                /* Ensure there's enough room for the NULL. */
> +                if (len > 0) {
> +                    strcat(buf, t);
> +                }
> +                compressed_zeros = 0;
> +            } else {
> +                compressed_zeros++;
> +            }
> +        }
> +        if (compressed_zeros && !added_to_buf) {
> +            /* The address was all zeros. */
> +            strcat(buf, "::");
> +        }
> +    }
> +    return buf;
> +#endif
> +}
> +
> +static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
> +{
> +#if (_WIN32_WINNT >= 0x0600)
> +    /* If built for Windows Vista/2008 or newer, use the OnLinkPrefixLength
> +     * field to obtain the prefix.  Otherwise, do our best to figure it 
> out.
> +     */
> +    return ip_addr->OnLinkPrefixLength;
> +#else
> +    int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined 
> values. */
> +
> +    if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
> +        IP_ADAPTER_INFO *adptr_info, *info;
> +        IP_ADDR_STRING *ip;
> +        struct in_addr *p;
> +
> +        if (guest_get_adapters_info(&adptr_info) != ERROR_SUCCESS) {
> +            return prefix;
> +        }
> +
> +        /* Match up the passed in ip_addr with one found in adaptr_info.
> +         * The matching one in adptr_info will have the netmask.
> +         */
> +        p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr;
> +        for (info = adptr_info; info && prefix == -1; info = info->Next) {
> +            for (ip = &info->IpAddressList; ip; ip = ip->Next) {
> +                if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) {
> +                    prefix = ctpop32(inet_addr(ip->IpMask.String));
> +                    break;
> +                }
> +            }
> +        }
> +        g_free(adptr_info);
> +    }
> +    return prefix;
> +#endif
> +}
> +
>  GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
>  {
> -    error_set(errp, QERR_UNSUPPORTED);
> +    IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
> +    GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
> +    GuestIpAddressList *head_addr, *cur_addr;
> +    DWORD ret;
> +
> +    ret = guest_get_adapter_addresses(&adptr_addrs);
> +    if (ret != ERROR_SUCCESS) {
> +        error_setg(errp, "failed to get adapter addresses %lu", ret);
> +        return NULL;
> +    }
> +
> +    for (addr = adptr_addrs; addr; addr = addr->Next) {
> +        GuestNetworkInterfaceList *info;
> +        GuestIpAddressList *address_item = NULL;
> +        char addr4[INET_ADDRSTRLEN];
> +        char addr6[INET6_ADDRSTRLEN];
> +        unsigned char *mac_addr;
> +        void *p;
> +        IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
> +
> +        info = g_malloc0(sizeof(*info));
> +        if (!info) {
> +            error_setg(errp, "failed to alloc a network interface list");
> +            goto error;
> +        }
> +
> +        if (!cur_item) {
> +            head = cur_item = info;
> +        } else {
> +            cur_item->next = info;
> +            cur_item = info;
> +        }
> +
> +        info->value = g_malloc0(sizeof(*info->value));
> +        if (!info->value) {
> +            error_setg(errp, "failed to alloc a network interface");
> +            goto error;
> +        }
> +        info->value->name = guest_wcstombs_dup(addr->FriendlyName);
> +
> +        if (addr->PhysicalAddressLength) {
> +            mac_addr = addr->PhysicalAddress;
> +
> +            info->value->hardware_address =
> +                g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
> +                    (int) mac_addr[0], (int) mac_addr[1],
> +                    (int) mac_addr[2], (int) mac_addr[3],
> +                    (int) mac_addr[4], (int) mac_addr[5]);
> +
> +            info->value->has_hardware_address = true;
> +        }
> +
> +        head_addr = NULL;
> +        cur_addr = NULL;
> +        for (ip_addr = addr->FirstUnicastAddress;
> +                ip_addr;
> +                ip_addr = ip_addr->Next) {
> +            address_item = g_malloc0(sizeof(*address_item));
> +            if (!address_item) {
> +                error_setg(errp, "failed to alloc an Ip address list");
> +                goto error;
> +            }
> +
> +            if (!cur_addr) {
> +                head_addr = cur_addr = address_item;
> +            } else {
> +                cur_addr->next = address_item;
> +                cur_addr = address_item;
> +            }
> +
> +            address_item->value = g_malloc0(sizeof(*address_item->value));
> +            if (!address_item->value) {
> +                error_setg(errp, "failed to alloc an Ip address");
> +                goto error;
> +            }
> +
> +            if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
> +                p = &((struct sockaddr_in *)
> +                    ip_addr->Address.lpSockaddr)->sin_addr;
> +
> +                if (!guest_inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
> +                    error_setg(errp,
> +                               "failed address presentation form 
> conversion");
> +                    goto error;
> +                }
> +
> +                address_item->value->ip_address = g_strdup(addr4);
> +                address_item->value->ip_address_type =
> +                    GUEST_IP_ADDRESS_TYPE_IPV4;
> +            } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
> +                p = &((struct sockaddr_in6 *)
> +                    ip_addr->Address.lpSockaddr)->sin6_addr;
> +
> +                if (!guest_inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
> +                    error_setg(errp,
> +                               "failed address presentation form 
> conversion");
> +                    goto error;
> +                }
> +
> +                address_item->value->ip_address = g_strdup(addr6);
> +                address_item->value->ip_address_type =
> +                    GUEST_IP_ADDRESS_TYPE_IPV6;
> +            }
> +            address_item->value->prefix = guest_ip_prefix(ip_addr);
> +        }
> +        if (head_addr) {
> +            info->value->has_ip_addresses = true;
> +            info->value->ip_addresses = head_addr;
> +        }
> +    }
> +
> +    if (adptr_addrs) {
> +        g_free(adptr_addrs);
> +    }
> +    return head;
> +
> +error:
> +    if (adptr_addrs) {
> +        g_free(adptr_addrs);
> +    }
> +    if (head) {
> +        qapi_free_GuestNetworkInterfaceList(head);
> +    }
>      return NULL;
>  }
>  
> @@ -707,7 +1022,7 @@ GuestMemoryBlockInfo 
> *qmp_guest_get_memory_block_info(Error **errp)
>  GList *ga_command_blacklist_init(GList *blacklist)
>  {
>      const char *list_unsupported[] = {
> -        "guest-suspend-hybrid", "guest-network-get-interfaces",
> +        "guest-suspend-hybrid",
>          "guest-get-vcpus", "guest-set-vcpus",
>          "guest-set-user-password",
>          "guest-get-memory-blocks", "guest-set-memory-blocks",




reply via email to

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