From patchwork Thu Feb 16 17:25:03 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Privoznik X-Patchwork-Id: 141650 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D2BD41007D3 for ; Fri, 17 Feb 2012 04:25:38 +1100 (EST) Received: from localhost ([::1]:48162 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ry55L-0000zS-Js for incoming@patchwork.ozlabs.org; Thu, 16 Feb 2012 12:25:35 -0500 Received: from eggs.gnu.org ([140.186.70.92]:49466) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ry554-0000xi-5q for qemu-devel@nongnu.org; Thu, 16 Feb 2012 12:25:24 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Ry54y-0000Sc-Mq for qemu-devel@nongnu.org; Thu, 16 Feb 2012 12:25:18 -0500 Received: from mx1.redhat.com ([209.132.183.28]:62487) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ry54y-0000RY-4h for qemu-devel@nongnu.org; Thu, 16 Feb 2012 12:25:12 -0500 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q1GHPAhd007263 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 16 Feb 2012 12:25:10 -0500 Received: from bart.brq.redhat.com (dhcp-27-226.brq.redhat.com [10.34.27.226]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q1GHP8bV013420 for ; Thu, 16 Feb 2012 12:25:09 -0500 From: Michal Privoznik To: qemu-devel@nongnu.org Date: Thu, 16 Feb 2012 18:25:03 +0100 Message-Id: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH] qemu-ga: Add guest-getip 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, ipaddr, ipaddr_family, prefix, hwaddr] for each interface in the system that has an IP address. Currently, only IPv4 and IPv6 are supported. Signed-off-by: Michal Privoznik --- qapi-schema-guest.json | 16 +++++ qga/guest-agent-commands.c | 156 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 0 deletions(-) diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index 5f8a18d..14de133 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -219,3 +219,19 @@ ## { 'command': 'guest-fsfreeze-thaw', 'returns': 'int' } + +## +# @guest-getip: +# +# Get list of guest IP addresses, MAC address +# and netmasks +# +# Returns: List of GuestIFAddress +# +# Since: 1.1 +## +{ 'type': 'GuestIFAddress', + 'data': {'iface': {'name': 'str', 'ipaddr': 'str', 'ipaddrtype': 'int', + 'prefix': 'int', 'hwaddr': 'str'} } } +{ 'command': 'guest-getip', + 'returns': ['GuestIFAddress'] } diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c index a09c8ca..4caffa7 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.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,6 +24,10 @@ #include #include +#include +#include +#include +#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qerror.h" @@ -583,3 +588,154 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #endif ga_command_state_add(cs, guest_file_init, NULL); } + +/* Count the number of '1' bits in @x */ +static int32_t +count_one_bits(uint32_t x) +{ + x = ((x & 0xaaaaaaaaU) >> 1) + (x & 0x55555555U); + x = ((x & 0xccccccccU) >> 2) + (x & 0x33333333U); + x = (x >> 16) + (x & 0xffff); + x = ((x & 0xf0f0) >> 4) + (x & 0x0f0f); + return (x >> 8) + (x & 0x00ff); +} + +/* + * Get the list of interfaces among with their + * IP/MAC addresses, prefixes and IP versions + */ +GuestIFAddressList * qmp_guest_getip(Error **err) +{ + GuestIFAddressList *head = NULL, *cur_item = NULL; + struct ifaddrs *ifap = NULL, *ifa = NULL; + char err_msg[512]; + + slog("guest-getip called"); + + if (getifaddrs(&ifap) < 0) { + snprintf(err_msg, sizeof(err_msg), + "getifaddrs failed : %s", strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto cleanup; + } + + ifa = ifap; + while (ifa) { + GuestIFAddressList *info = NULL; + char addr4[INET_ADDRSTRLEN]; + char addr6[INET6_ADDRSTRLEN]; + unsigned char *mac_addr; + void *tmpAddrPtr = NULL; + int sock, family; + int mac_supported; + struct ifreq ifr; + + /* Step over interfaces without an address */ + if (!ifa->ifa_addr) + continue; + + g_debug("Processing %s interface", ifa->ifa_name); + + family = ifa->ifa_addr->sa_family; + + if (family == AF_INET) { + /* interface with IPv4 address */ + tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + inet_ntop(AF_INET, tmpAddrPtr, addr4, sizeof(addr4)); + + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->iface.name = g_strdup(ifa->ifa_name); + info->value->iface.ipaddr = g_strdup(addr4); + + /* This is safe as '1' and '0' cannot be shuffled in netmask. */ + tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; + info->value->iface.prefix = count_one_bits(((uint32_t *) tmpAddrPtr)[0]); + } else if (family == AF_INET6) { + /* interface with IPv6 address */ + tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + inet_ntop(AF_INET6, tmpAddrPtr, addr6, sizeof(addr6)); + + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->iface.name = g_strdup(ifa->ifa_name); + info->value->iface.ipaddr = g_strdup(addr6); + + /* This is safe as '1' and '0' cannot be shuffled in netmask. */ + tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + info->value->iface.prefix = count_one_bits(((uint32_t *) tmpAddrPtr)[0]) + + count_one_bits(((uint32_t *) tmpAddrPtr)[1]) + + count_one_bits(((uint32_t *) tmpAddrPtr)[2]) + + count_one_bits(((uint32_t *) tmpAddrPtr)[3]); + } + + /* Add new family here */ + + mac_supported = ifa->ifa_flags & SIOCGIFHWADDR; + + ifa = ifa->ifa_next; + + if (!info) + continue; + + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + + info->value->iface.ipaddrtype = family; + + g_debug("Appending to return: iface=%s, ip=%s, type=%llu, prefix=%llu", + info->value->iface.name, + info->value->iface.ipaddr, + (unsigned long long) info->value->iface.ipaddrtype, + (unsigned long long) info->value->iface.prefix); + + /* If we can't get get MAC address on this interface, + * don't even try but continue to another interface */ + if (!mac_supported) + continue; + + 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(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto cleanup; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, info->value->iface.name, IF_NAMESIZE); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { + snprintf(err_msg, sizeof(err_msg), + "failed to get MAC addres of %s: %s", + info->value->iface.name, + strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto cleanup; + } + + mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; + + if (asprintf(&info->value->iface.hwaddr, + "%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(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto cleanup; + } + + close(sock); + } + +cleanup: + freeifaddrs(ifap); + + return head; +}