qemu-ga: add guest-network-get-interfaces command
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 <mprivozn@redhat.com>
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 79571bf..5b77fa9 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -5,6 +5,7 @@
*
* Authors:
* Michael Roth <mdroth@linux.vnet.ibm.com>
+ * Michal Privoznik <mprivozn@redhat.com>
*
* 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,11 +24,16 @@
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
#include <sys/wait.h>
#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;
@@ -725,6 +731,181 @@
guest_suspend("pm-suspend-hybrid", NULL, err);
}
+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;
+}
+
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{