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 <mpriv...@redhat.com> --- 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 | 29 ++++++++ qga/guest-agent-commands.c | 157 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 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..1371f8b 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -5,6 +5,7 @@ * * Authors: * Michael Roth <mdr...@linux.vnet.ibm.com> + * Michal Privoznik <mpriv...@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,10 +24,15 @@ #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" #include "qemu-queue.h" +#include "host-utils.h" static GAState *ga_state; @@ -583,3 +589,154 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #endif ga_command_state_add(cs, guest_file_init, NULL); } + +/* + * 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]; + + g_debug("guest-network-info 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) { + ifa = ifa->ifa_next; + 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; + + /* Count the number of set bits in netmask. + * 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 = + ctpop32(((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; + + /* Count the number of set bits in netmask. + * 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 = + ctpop32(((uint32_t *) tmp_addr_ptr)[0]) + + ctpop32(((uint32_t *) tmp_addr_ptr)[1]) + + ctpop32(((uint32_t *) tmp_addr_ptr)[2]) + + ctpop32(((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