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>
This commit is contained in:
		
							parent
							
								
									f54603b6aa
								
							
						
					
					
						commit
						3424fc9f16
					
				| 
						 | 
				
			
			@ -370,3 +370,62 @@
 | 
			
		|||
# Since: 1.1
 | 
			
		||||
##
 | 
			
		||||
{ 'command': 'guest-suspend-hybrid' }
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# @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-network-get-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'] }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 @@ void qmp_guest_suspend_hybrid(Error **err)
 | 
			
		|||
    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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -263,6 +263,12 @@ void qmp_guest_suspend_hybrid(Error **err)
 | 
			
		|||
    error_set(err, QERR_UNSUPPORTED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err)
 | 
			
		||||
{
 | 
			
		||||
    error_set(err, QERR_UNSUPPORTED);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* register init/cleanup routines for stateful command groups */
 | 
			
		||||
void ga_command_state_init(GAState *s, GACommandState *cs)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue