Signed-off-by: zhanghailiang <zhang.zhanghaili...@huawei.com>
---
Hi,

This patch implements guest-network-get-interfaces command for
Windows.

This patch is RFC because the value of network 'prefix' length may be wrong
When there is an adapter with multiple IP which have different netmask.

The main reason is I get this value by hunting for matching prefix in linked 
list,
But unfortunately the order of linked IP_ADAPTER_UNICAST_ADDRESS structures 
pointed
to by the FirstUnicastAddress member does not have any relationship with the
order of linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix
member. So actually, we cannot match exactly prefix with unicast struct. :(

Yes, MSDN suggests we get prefix length value by reference to 
OnLinkPrefixLength which 
is a member of struct IP_ADAPTER_UNICAST_ADDRESS, but this structure member is 
only
available on Windows Vista and later, and it seems that the cross compiling 
environment is like Windows XP. Who know this?

Any comments and suggestion are welcomed.

You can test this by command:
'{"execute":"guest-network-get-interfaces"}'
The return value is like:
{"return":[{"name":"{FE2D1285-75FF-48E7-BDEF-50D19DA7D6B4}","ip-addresses":[{"ip-address-type":"ipv6","ip-address":"fe80::e4ca:8658:61e3:8b83","prefix":64},{"ip-address-type":"ipv4","ip-address":"9.61.170.170","prefix":16}],"hardware-address":"52:54:00:7b:4b:19"},{"name":"{846EE342-7039-11DE-9D20-806E6F6E6963}","ip-addresses":[{"ip-address-type":"ipv6","ip-address":"::1","prefix":128},{"ip-address-type":"ipv4","ip-address":"127.0.0.1","prefix":8}],"hardware-address":"52:54:00:7b:4b:19"}]}

---
 configure              |   2 +-
 qga/Makefile.objs      |   2 +-
 qga/commands-win32.c   | 201 ++++++++++++++++++++++++++++++++++++++++++++++++-
 qga/guest-agent-core.h |  11 +++
 qga/inet_ntop-win32.c  | 184 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 394 insertions(+), 6 deletions(-)
 create mode 100644 qga/inet_ntop-win32.c

diff --git a/configure b/configure
index cae588c..7cafbdd 100755
--- a/configure
+++ b/configure
@@ -717,7 +717,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=""
diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index 1c5986c..47ef4aa 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -1,6 +1,6 @@
 qga-obj-y = commands.o guest-agent-command-state.o main.o
 qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
-qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
+qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o 
inet_ntop-win32.o
 qga-obj-$(CONFIG_WIN32) += vss-win32.o
 qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
 qga-obj-y += qapi-generated/qga-qmp-marshal.o
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 3bcbeae..af4eb31 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -14,6 +14,9 @@
 #include <glib.h>
 #include <wtypes.h>
 #include <powrprof.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
 #include "qga/guest-agent-core.h"
 #include "qga/vss-win32.h"
 #include "qga-qmp-commands.h"
@@ -359,9 +362,200 @@ void qmp_guest_suspend_hybrid(Error **errp)
     error_set(errp, QERR_UNSUPPORTED);
 }
 
-GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
+#define WORKING_BUFFER_SIZE 15000
+#define MAX_TRIES 3
+#define IN_LINKLOCAL(a) ((((uint32_t) (a)) & 0xaffff0000) == 0xa9fe0000)
+
+/*
+ * For Vista and later version, we can get the prefix length value from
+ * OnLinkPrefixLength which is a member of IP_ADAPTER_UNICAST_ADDRESS 
structure.
+ * Otherwise we must hunt for matching prefix in linked list.
+ *
+ * Note:The order of linked IP_ADAPTER_UNICAST_ADDRESS structures pointed to by
+ * the FirstUnicastAddress member does not have any relationship with the
+ * order of linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix
+ * member, So the result may be incorrect for an adapter with multiple IP !!!
+ *
+ * More info can be found at:
+ 
*http://msdn.microsoft.com/en-us/library/windows/desktop/aa366066(v=vs.85).aspx
+ */
+static int64_t get_adapter_unicast_prefixlength(PIP_ADAPTER_ADDRESSES pAdapter,
+                                         PIP_ADAPTER_UNICAST_ADDRESS pUnicast)
 {
-    error_set(errp, QERR_UNSUPPORTED);
+    IP_ADAPTER_PREFIX *prefix;
+/*
+* Actually, here the cross compiling envirtonment for windows qemu-ga,
+* the IP_ADAPTER_UNICAST_ADDRESS structure is defined as
+* IP_ADAPTER_UNICAST_ADDRESS_XP.
+*/
+#if 0
+    if (IsWindowsVistaOrGreater()) {
+        return pUnicast->OnLinkPrefixLength;
+    }
+#endif
+    for (prefix = pAdapter->FirstPrefix; prefix; prefix = prefix->Next) {
+        LPSOCKADDR lpSockaddr = prefix->Address.lpSockaddr;
+
+        if (lpSockaddr->sa_family != pUnicast->Address.lpSockaddr->sa_family) {
+            continue;
+        }
+        if (lpSockaddr->sa_family == AF_INET) {
+            char addr4[INET_ADDRSTRLEN];
+            struct sockaddr_in* sa_in = (struct sockaddr_in *)lpSockaddr;
+            inet_ntop(AF_INET, &(sa_in->sin_addr), addr4, sizeof(addr4));
+            g_debug("fuck:%s\n", addr4);
+        }
+        /* special cases */
+        /* RFC2863: IPv4 interface not up */
+        if (lpSockaddr->sa_family == AF_INET &&
+            pAdapter->OperStatus != IfOperStatusUp) {
+            /* RFC3927: link-local IPv4 always has 16-bit CIDR */
+            if (IN_LINKLOCAL(ntohl(((struct sockaddr_in *)
+                 (pUnicast->Address.lpSockaddr))->sin_addr.s_addr))) {
+                g_debug("Assuming 16-bit prefix length for"
+                        "link-local IPv4 adapter %s", pAdapter->AdapterName);
+                return 16;
+            } else {
+                g_debug("Prefix length unavailable for IPv4 adapter %s.",
+                        pAdapter->AdapterName);
+            }
+            break;
+        }
+        /* default IPv6 route */
+        if (lpSockaddr->sa_family == AF_INET6 && 0 == prefix->PrefixLength &&
+            IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)
+                                     (lpSockaddr))->sin6_addr)) {
+            continue;
+        }
+
+        return prefix->PrefixLength;
+    }
+    return -1;
+}
+
+GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error** errp)
+{
+    PIP_ADAPTER_ADDRESSES pAddresses = NULL, pCurrAddresses = NULL;
+    ULONG outBufLen = 0;
+    DWORD dwRetVal = 0;
+    ULONG Iterations = 0;
+    ULONG flags = (GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST |
+                   GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME |
+                   GAA_FLAG_SKIP_MULTICAST);
+    GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
+
+     /* Allocate a 15 KB buffer to start with */
+    outBufLen = WORKING_BUFFER_SIZE;
+    do {
+        pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
+        if (pAddresses == NULL) {
+            error_setg(errp, "Memory allocation failed for"
+                            "IP_ADAPTER_ADDRESSES struct");
+            return NULL;
+        }
+
+        dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
+                                        pAddresses, &outBufLen);
+
+        if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+            free(pAddresses);
+            pAddresses = NULL;
+        } else {
+            break;
+        }
+        Iterations++;
+
+    } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
+
+    if (dwRetVal != NO_ERROR) {
+        error_setg(errp, "Call to GetAdaptersAddresses failed with error: %d",
+                          (int)dwRetVal);
+        goto error;
+    }
+
+    for (pCurrAddresses = pAddresses; pCurrAddresses;
+         pCurrAddresses = pCurrAddresses->Next) {
+        GuestNetworkInterfaceList *info;
+        PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
+        unsigned char *mac_addr;
+
+        if (pCurrAddresses->IfType == IF_TYPE_TUNNEL) { /* skip tunnel type? */
+            continue;
+        }
+
+        info = g_malloc0(sizeof(*info));
+        info->value = g_malloc0(sizeof(*info->value));
+        info->value->name = g_strdup(pCurrAddresses->AdapterName);
+
+        if (!cur_item) {
+            head = cur_item = info;
+        } else {
+            cur_item->next = info;
+            cur_item = info;
+        }
+
+        mac_addr = pAddresses->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;
+
+        for (pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast;
+             pUnicast = pUnicast->Next) {
+            GuestIpAddressList **address_list = NULL, *address_item = NULL;
+            char addr4[INET_ADDRSTRLEN];
+            char addr6[INET6_ADDRSTRLEN];
+
+            address_item = g_malloc0(sizeof(*address_item));
+            address_item->value = g_malloc0(sizeof(*address_item->value));
+            address_item->value->prefix = get_adapter_unicast_prefixlength(
+                                                     pCurrAddresses, pUnicast);
+            switch (pUnicast->Address.lpSockaddr->sa_family) {
+            case  AF_INET: {
+                struct sockaddr_in *sa_in = (struct sockaddr_in *)
+                                             pUnicast->Address.lpSockaddr;
+                inet_ntop(AF_INET, &(sa_in->sin_addr), addr4, sizeof(addr4));
+                address_item->value->ip_address_type
+                                    = GUEST_IP_ADDRESS_TYPE_IPV4;
+                address_item->value->ip_address = g_strdup(addr4);
+                break;
+            }
+            case  AF_INET6: {
+                struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)
+                                               pUnicast->Address.lpSockaddr;
+                inet_ntop(AF_INET6, &(sa_in6->sin6_addr), addr6, 
sizeof(addr6));
+                address_item->value->ip_address_type
+                                    = GUEST_IP_ADDRESS_TYPE_IPV6;
+                address_item->value->ip_address = g_strdup(addr6);
+                break;
+            }
+            default:
+                break;
+            }
+
+            address_list = &info->value->ip_addresses;
+
+            while (*address_list && (*address_list)->next) {
+                address_list = &(*address_list)->next;
+            }
+
+            if (!*address_list) {
+                *address_list = address_item;
+            } else {
+                (*address_list)->next = address_item;
+            }
+
+            info->value->has_ip_addresses = true;
+        }
+    }
+    free(pAddresses);
+    return head;
+error:
+    free(pAddresses);
+    qapi_free_GuestNetworkInterfaceList(head);
     return NULL;
 }
 
@@ -452,8 +646,7 @@ GList *ga_command_blacklist_init(GList *blacklist)
     const char *list_unsupported[] = {
         "guest-file-open", "guest-file-close", "guest-file-read",
         "guest-file-write", "guest-file-seek", "guest-file-flush",
-        "guest-suspend-hybrid", "guest-network-get-interfaces",
-        "guest-get-vcpus", "guest-set-vcpus",
+        "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
         "guest-fsfreeze-freeze-list", "guest-get-fsinfo",
         "guest-fstrim", NULL};
     char **p = (char **)list_unsupported;
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e92c6ab..e82afc5 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -12,6 +12,9 @@
  */
 #include "qapi/qmp/dispatch.h"
 #include "qemu-common.h"
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#endif
 
 #define QGA_READ_COUNT_DEFAULT 4096
 
@@ -41,3 +44,11 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp);
 #ifndef _WIN32
 void reopen_fd_to_null(int fd);
 #endif
+
+#ifdef _WIN32
+/* Convert a Internet address in binary network format for interface
+   type AF in buffer starting at CP to presentation form and place
+   result in buffer of length LEN astarting at BUF.  */
+extern const char *inet_ntop(int __af, const void *__cp,
+                             char *__buf, socklen_t __len);
+#endif
diff --git a/qga/inet_ntop-win32.c b/qga/inet_ntop-win32.c
new file mode 100644
index 0000000..2de961d
--- /dev/null
+++ b/qga/inet_ntop-win32.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+#include <errno.h>
+#include <ws2tcpip.h>
+#include "qga/guest-agent-core.h"
+
+
+#define SPRINTF(x) ((socklen_t)sprintf x)
+#define NS_INT16SZ       2
+#define NS_IN6ADDRSZ     16
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4(const u_char *src, char *dst, socklen_t size);
+static const char *inet_ntop6(const u_char *src, char *dst, socklen_t size);
+
+/* char *
+ * inet_ntop(af, src, dst, size)
+ *  convert a network format address to presentation format.
+ * return:
+ *  pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ *  Paul Vixie, 1996.
+ */
+const char *
+inet_ntop(int af, const void *src, char *dst, socklen_t size)
+{
+    switch (af) {
+    case AF_INET:
+        return inet_ntop4(src, dst, size);
+    case AF_INET6:
+        return inet_ntop6(src, dst, size);
+    default:
+        errno = EAFNOSUPPORT;
+        return NULL;
+    }
+    /* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ *  format an IPv4 address
+ * return:
+ *  `dst' (as a const)
+ * notes:
+ *  (1) uses no statics
+ *  (2) takes a u_char* not an in_addr as input
+ * author:
+ *  Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(const u_char *src, char *dst, socklen_t size)
+{
+    static const char fmt[] = "%u.%u.%u.%u";
+    char tmp[sizeof "255.255.255.255"];
+
+    if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) {
+        errno = ENOSPC;
+        return NULL;
+    }
+    return strcpy(dst, tmp);
+}
+
+/* const char *
+ * inet_ntop6(src, dst, size)
+ *  convert IPv6 binary address into presentation (printable) format
+ * author:
+ *  Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop6(const u_char *src, char *dst, socklen_t size)
+{
+    /*
+     * Note that int32_t and int16_t need only be "at least" large enough
+     * to contain a value of the specified size.  On some systems, like
+     * Crays, there is no such thing as an integer variable with 16 bits.
+     * Keep this in mind if you think this function should have been coded
+     * to use pointer overlays.  All the world's not a VAX.
+     */
+    char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+    struct { int base, len; } best, cur;
+    u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+    int i;
+
+    /*
+     * Preprocess:
+     *  Copy the input (bytewise) array into a wordwise array.
+     *  Find the longest run of 0x00's in src[] for :: shorthanding.
+     */
+    memset(words, '\0', sizeof words);
+    for (i = 0; i < NS_IN6ADDRSZ; i += 2) {
+        words[i / 2] = (src[i] << 8) | src[i + 1];
+    }
+    best.base = -1;
+    cur.base = -1;
+    best.len = 0;
+    cur.len = 0;
+    for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+        if (words[i] == 0) {
+            if (cur.base == -1) {
+                cur.base = i;
+                cur.len = 1;
+            } else {
+                cur.len++;
+            }
+        } else {
+            if (cur.base != -1) {
+                if (best.base == -1 || cur.len > best.len) {
+                    best = cur;
+                }
+                cur.base = -1;
+            }
+        }
+    }
+    if (cur.base != -1) {
+        if (best.base == -1 || cur.len > best.len) {
+            best = cur;
+        }
+    }
+    if (best.base != -1 && best.len < 2) {
+        best.base = -1;
+    }
+
+    /*
+     * Format the result.
+     */
+    tp = tmp;
+    for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+        /* Are we inside the best run of 0x00's? */
+        if (best.base != -1 && i >= best.base &&
+            i < (best.base + best.len)) {
+            if (i == best.base) {
+                *tp++ = ':';
+            }
+            continue;
+        }
+        /* Are we following an initial run of 0x00s or any real hex? */
+        if (i != 0) {
+            *tp++ = ':';
+        }
+        /* Is this address an encapsulated IPv4? */
+        if (i == 6 && best.base == 0 &&
+            (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+            if (!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp))) {
+                return NULL;
+            }
+            tp += strlen(tp);
+            break;
+        }
+        tp += SPRINTF((tp, "%x", words[i]));
+    }
+    /* Was it a trailing run of 0x00's? */
+    if (best.base != -1 && (best.base + best.len) ==
+        (NS_IN6ADDRSZ / NS_INT16SZ)) {
+        *tp++ = ':';
+    }
+    *tp++ = '\0';
+
+    /*
+     * Check for overflow, copy, and we're done.
+     */
+    if ((socklen_t)(tp - tmp) > size) {
+        errno = ENOSPC;
+        return NULL;
+    }
+    return strcpy(dst, tmp);
+}
+
-- 
1.7.12.4



Reply via email to