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 <kal...@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",