diff mbox

qga: implement qmp_guest_network_get_interfaces for win32

Message ID 1426518542-28222-1-git-send-email-kallan@suse.com
State New
Headers show

Commit Message

Kirk Allan March 16, 2015, 3:09 p.m. UTC
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 <kallan@suse.com>
---
 configure            |  22 +++-
 qga/commands-win32.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 336 insertions(+), 5 deletions(-)

Comments

Kirk Allan March 30, 2015, 8:44 p.m. UTC | #1
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 <kallan@suse.com>
> ---
>  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",
Andreas Färber March 30, 2015, 9:15 p.m. UTC | #2
Hi Kirk,

Am 30.03.2015 um 22:44 schrieb Kirk Allan:
> Ping.
> 
> Looking for feedback anyone might have regarding this patch.

I would recommend to make the patch smaller and the commit message more
intuitive by splitting off the configure changes into its own patch
(remember to include a short cover letter then). It feels strange to
have a new configure option for a single -D though... Can't this value
be overridden via --extra-cflags=-D_WINNT_WINVER=0xf00 already?

CC'ing some people that know about Windows might help, too. Stefan W.
comes to mind.

Also note that we're in Hard Freeze for 2.3 at the moment, which shifts
people's attention to bug fixes.

Regards,
Andreas
Kirk Allan March 30, 2015, 10:26 p.m. UTC | #3
>>>
> Hi Kirk,
> 
> Am 30.03.2015 um 22:44 schrieb Kirk Allan:
>> Ping.
>> 
>> Looking for feedback anyone might have regarding this patch.
> 
> I would recommend to make the patch smaller and the commit message more
> intuitive by splitting off the configure changes into its own patch
> (remember to include a short cover letter then). 

OK, I'll do that.

> It feels strange to
> have a new configure option for a single -D though... Can't this value
> be overridden via --extra-cflags=-D_WINNT_WINVER=0xf00 already?

Good point.  I'll create a new patch that used --extra-cflags to do the job.

> 
> CC'ing some people that know about Windows might help, too. Stefan W.
> comes to mind.
> 
> Also note that we're in Hard Freeze for 2.3 at the moment, which shifts
> people's attention to bug fixes.
> 
> Regards,
> Andreas
diff mbox

Patch

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",