All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32
@ 2015-03-16 15:09 Kirk Allan
  2015-03-30 20:44 ` Kirk Allan
  0 siblings, 1 reply; 4+ messages in thread
From: Kirk Allan @ 2015-03-16 15:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kirk Allan, mdroth

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

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32
  2015-03-16 15:09 [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32 Kirk Allan
@ 2015-03-30 20:44 ` Kirk Allan
  2015-03-30 21:15   ` Andreas Färber
  0 siblings, 1 reply; 4+ messages in thread
From: Kirk Allan @ 2015-03-30 20:44 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32
  2015-03-30 20:44 ` Kirk Allan
@ 2015-03-30 21:15   ` Andreas Färber
  2015-03-30 22:26     ` Kirk Allan
  0 siblings, 1 reply; 4+ messages in thread
From: Andreas Färber @ 2015-03-30 21:15 UTC (permalink / raw)
  To: Kirk Allan, qemu-devel; +Cc: Stefan Weil, mdroth

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

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Jennifer Guild, Dilip Upmanyu,
Graham Norton; HRB 21284 (AG Nürnberg)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32
  2015-03-30 21:15   ` Andreas Färber
@ 2015-03-30 22:26     ` Kirk Allan
  0 siblings, 0 replies; 4+ messages in thread
From: Kirk Allan @ 2015-03-30 22:26 UTC (permalink / raw)
  To: qemu-devel, Andreas Färber; +Cc: Stefan Weil, mdroth



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

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2015-03-30 22:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-16 15:09 [Qemu-devel] [PATCH] qga: implement qmp_guest_network_get_interfaces for win32 Kirk Allan
2015-03-30 20:44 ` Kirk Allan
2015-03-30 21:15   ` Andreas Färber
2015-03-30 22:26     ` Kirk Allan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.