From patchwork Wed Feb 29 16:02:23 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?TWljaGFsIFByw612b3puw61r?= X-Patchwork-Id: 143772 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 563C9B6F62 for ; Thu, 1 Mar 2012 03:03:45 +1100 (EST) Received: from localhost ([::1]:51149 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S2m0F-000234-30 for incoming@patchwork.ozlabs.org; Wed, 29 Feb 2012 11:03:43 -0500 Received: from eggs.gnu.org ([208.118.235.92]:56125) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S2lzz-00021h-5P for qemu-devel@nongnu.org; Wed, 29 Feb 2012 11:03:37 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1S2lzZ-0005QP-QV for qemu-devel@nongnu.org; Wed, 29 Feb 2012 11:03:26 -0500 Received: from mx1.redhat.com ([209.132.183.28]:3824) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S2lzZ-0005Pz-H0 for qemu-devel@nongnu.org; Wed, 29 Feb 2012 11:03:01 -0500 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q1TG2R02030584 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 29 Feb 2012 11:02:27 -0500 Received: from bart.brq.redhat.com (dhcp-13-77.brq.redhat.com [10.34.13.77] (may be forged)) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q1TG2OIf031847; Wed, 29 Feb 2012 11:02:25 -0500 From: Michal Privoznik To: qemu-devel@nongnu.org Date: Wed, 29 Feb 2012 17:02:23 +0100 Message-Id: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: mdroth@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH v6] qemu-ga: Add guest-network-get-interfaces command X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This command returns an array of: [ifname, hwaddr, [ipaddr, ipaddr_family, prefix] ] for each interface in the system. Currently, only IPv4 and IPv6 are supported. Signed-off-by: Michal Privoznik diff to v5: -another change of command diff to v4: -change return type diff to v3: -use ctpop32() instead of separate count_one_bits() diff to v2: -Properly set IP addr family for IPv6 diff to v1: -move from guest-getip to guest-network-info -replace black boxed algorithm for population count -several coding styles improvements qapi-schema-guest.json | 59 ++++++++++++++++ qga/commands-posix.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ qga/commands-win32.c | 6 ++ 3 files changed, 246 insertions(+), 0 deletions(-) diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index 706925d..669018d 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -295,3 +295,62 @@ ## { 'command': 'guest-fsfreeze-thaw', 'returns': 'int' } + +## +# @GuestIpAddressType: +# +# An enumeration of supported IP address types +# +# @ipv4: IP version 4 +# +# @ipv6: IP version 6 +# +# Since: 1.1 +## +{ 'enum': 'GuestIpAddressType', + 'data': [ 'ipv4', 'ipv6' ] } + +## +# @GuestIpAddress: +# +# @ip-address: IP address +# +# @ip-address-type: Type of @ip-address (e.g. ipv4, ipv6) +# +# @prefix: Network prefix length of @ip-address +# +# Since: 1.1 +## +{ 'type': 'GuestIpAddress', + 'data': {'ip-address': 'str', + 'ip-address-type': 'GuestIpAddressType', + 'prefix': 'int'} } + +## +# @GuestNetworkInterface: +# +# @name: The name of interface for which info are being delivered +# +# @hardware-address: Hardware address of @name +# +# @ip-addresses: List of addresses assigned to @name +# +# Since: 1.1 +## +{ 'type': 'GuestNetworkInterface', + 'data': {'name': 'str', + '*hardware-address': 'str', + '*ip-addresses': ['GuestIpAddress'] } } + +## +# @guest-get-network-interfaces: +# +# Get list of guest IP addresses, MAC addresses +# and netmasks. +# +# Returns: List of GuestNetworkInfo on success. +# +# Since: 1.1 +## +{ 'command': 'guest-network-get-interfaces', + 'returns': ['GuestNetworkInterface'] } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 126127a..913123e 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -5,6 +5,7 @@ * * Authors: * Michael Roth + * Michal Privoznik * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -23,10 +24,15 @@ #include #include +#include +#include +#include +#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qerror.h" #include "qemu-queue.h" +#include "host-utils.h" static GAState *ga_state; @@ -526,3 +532,178 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #endif ga_command_state_add(cs, guest_file_init, NULL); } + +static GuestNetworkInterfaceList * +guest_find_interface(GuestNetworkInterfaceList *head, + const char *name) +{ + for (; head; head = head->next) { + if (strcmp(head->value->name, name) == 0) { + break; + } + } + + return head; +} + +/* + * Build information about guest interfaces + */ +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) +{ + GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; + struct ifaddrs *ifap, *ifa; + char err_msg[512]; + + if (getifaddrs(&ifap) < 0) { + snprintf(err_msg, sizeof(err_msg), + "getifaddrs failed: %s", strerror(errno)); + error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + GuestNetworkInterfaceList *info; + GuestIpAddressList **address_list = NULL, *address_item = NULL; + char addr4[INET_ADDRSTRLEN]; + char addr6[INET6_ADDRSTRLEN]; + int sock; + struct ifreq ifr; + unsigned char *mac_addr; + void *p; + + g_debug("Processing %s interface", ifa->ifa_name); + + info = guest_find_interface(head, ifa->ifa_name); + + if (!info) { + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(ifa->ifa_name); + + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + + if (!info->value->has_hardware_address && + ifa->ifa_flags & SIOCGIFHWADDR) { + /* we haven't obtained HW address yet */ + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) { + snprintf(err_msg, sizeof(err_msg), + "failed to create socket: %s", strerror(errno)); + error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, info->value->name, IF_NAMESIZE); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { + snprintf(err_msg, sizeof(err_msg), + "failed to get MAC addres of %s: %s", + ifa->ifa_name, + strerror(errno)); + error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; + + if (asprintf(&info->value->hardware_address, + "%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]) == -1) { + snprintf(err_msg, sizeof(err_msg), + "failed to format MAC: %s", strerror(errno)); + error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + info->value->has_hardware_address = true; + close(sock); + } + + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_INET) { + /* interface with IPv4 address */ + address_item = g_malloc0(sizeof(*address_item)); + address_item->value = g_malloc0(sizeof(*address_item->value)); + p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { + snprintf(err_msg, sizeof(err_msg), + "inet_ntop failed : %s", strerror(errno)); + error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + address_item->value->ip_address = g_strdup(addr4); + address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; + + if (ifa->ifa_netmask) { + /* Count the number of set bits in netmask. + * This is safe as '1' and '0' cannot be shuffled in netmask. */ + p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; + address_item->value->prefix = ctpop32(((uint32_t *) p)[0]); + } + } else if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_INET6) { + /* interface with IPv6 address */ + address_item = g_malloc0(sizeof(*address_item)); + address_item->value = g_malloc0(sizeof(*address_item->value)); + p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { + snprintf(err_msg, sizeof(err_msg), + "inet_ntop failed : %s", strerror(errno)); + error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + address_item->value->ip_address = g_strdup(addr6); + address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; + + if (ifa->ifa_netmask) { + /* Count the number of set bits in netmask. + * This is safe as '1' and '0' cannot be shuffled in netmask. */ + p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + address_item->value->prefix = + ctpop32(((uint32_t *) p)[0]) + + ctpop32(((uint32_t *) p)[1]) + + ctpop32(((uint32_t *) p)[2]) + + ctpop32(((uint32_t *) p)[3]); + } + } + + if (!address_item) { + continue; + } + + 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; + + + } + + freeifaddrs(ifap); + return head; + +error: + freeifaddrs(ifap); + qapi_free_GuestNetworkInterfaceList(head); + return NULL; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 4aa0f0d..c9b94e7 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -128,3 +128,9 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) void ga_command_state_init(GAState *s, GACommandState *cs) { } + +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) +{ + error_set(err, QERR_UNSUPPORTED); + return NULL; +}