[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3] qemu-ga: Add guest-network-info command
From: |
Michal Privoznik |
Subject: |
[Qemu-devel] [PATCH v3] qemu-ga: Add guest-network-info command |
Date: |
Fri, 17 Feb 2012 13:06:25 +0100 |
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 <address@hidden>
---
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 | 29 ++++++++
qga/guest-agent-commands.c | 164 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 193 insertions(+), 0 deletions(-)
diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 5f8a18d..ca4fdc5 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -219,3 +219,32 @@
##
{ 'command': 'guest-fsfreeze-thaw',
'returns': 'int' }
+
+##
+# @guest-network-info:
+#
+# Get list of guest IP addresses, MAC addresses
+# and netmasks.
+#
+# @name: The name of interface for which info are being delivered
+#
+# @ipaddr: IP address assigned to @name
+#
+# @ipaddrtype: Type of @ipaddr (e.g. ipv4, ipv6)
+#
+# @prefix: Network prefix length
+#
+# @hwaddr: Hardware address of @name
+#
+# Returns: List of GuestNetworkInfo on success.
+#
+# Since: 1.1
+##
+{ 'enum': 'GuestIpAddrType',
+ 'data': [ 'ipv4', 'ipv6' ] }
+{ 'type': 'GuestNetworkInfo',
+ 'data': {'iface': {'name': 'str', 'ipaddr': 'str',
+ 'ipaddrtype': 'GuestIpAddrType',
+ 'prefix': 'int', 'hwaddr': 'str'} } }
+{ 'command': 'guest-network-info',
+ 'returns': ['GuestNetworkInfo'] }
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
index a09c8ca..04bab68 100644
--- a/qga/guest-agent-commands.c
+++ b/qga/guest-agent-commands.c
@@ -5,6 +5,7 @@
*
* Authors:
* Michael Roth <address@hidden>
+ * Michal Privoznik <address@hidden>
*
* 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 <sys/types.h>
#include <sys/ioctl.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
#include "qerror.h"
@@ -583,3 +588,162 @@ 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)
+{
+ uint32_t count = 0;
+ for (count = 0; x; count++) {
+ x &= x-1;
+ }
+ return count;
+}
+
+/*
+ * Get the list of interfaces among with their
+ * IP/MAC addresses, prefixes and IP versions
+ */
+GuestNetworkInfoList *qmp_guest_network_info(Error **err)
+{
+ GuestNetworkInfoList *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 error;
+ }
+
+ ifa = ifap;
+ while (ifa) {
+ GuestNetworkInfoList *info = NULL;
+ char addr4[INET_ADDRSTRLEN];
+ char addr6[INET6_ADDRSTRLEN];
+ unsigned char *mac_addr;
+ void *tmp_addr_ptr = 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 */
+ tmp_addr_ptr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
+ inet_ntop(AF_INET, tmp_addr_ptr, 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);
+ info->value->iface.ipaddrtype = GUEST_IP_ADDR_TYPE_IPV4;
+
+ /* This is safe as '1' and '0' cannot be shuffled in netmask. */
+ tmp_addr_ptr = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
+ info->value->iface.prefix =
+ count_one_bits(((uint32_t *) tmp_addr_ptr)[0]);
+ } else if (family == AF_INET6) {
+ /* interface with IPv6 address */
+ tmp_addr_ptr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ inet_ntop(AF_INET6, tmp_addr_ptr, 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);
+ info->value->iface.ipaddrtype = GUEST_IP_ADDR_TYPE_IPV6;
+
+ /* This is safe as '1' and '0' cannot be shuffled in netmask. */
+ tmp_addr_ptr = &((struct sockaddr_in6
*)ifa->ifa_netmask)->sin6_addr;
+ info->value->iface.prefix =
+ count_one_bits(((uint32_t *) tmp_addr_ptr)[0]) +
+ count_one_bits(((uint32_t *) tmp_addr_ptr)[1]) +
+ count_one_bits(((uint32_t *) tmp_addr_ptr)[2]) +
+ count_one_bits(((uint32_t *) tmp_addr_ptr)[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;
+ }
+
+ 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 error;
+ }
+
+ 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 error;
+ }
+
+ 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 error;
+ }
+
+ close(sock);
+ }
+
+ freeifaddrs(ifap);
+ return head;
+
+error:
+ freeifaddrs(ifap);
+ qapi_free_GuestNetworkInfoList(head);
+ return NULL;
+}
--
1.7.3.4
- [Qemu-devel] [PATCH v3] qemu-ga: Add guest-network-info command,
Michal Privoznik <=