libvirt/src/interface/interface_backend_udev.c
Erik Skultety 5165ff0971 src: More cleanup of some system headers already contained in internal.h
All of the ones being removed are pulled in by internal.h. The only
exception is sanlock which expects the application to include <stdint.h>
before sanlock's headers, because sanlock prototypes use fixed width
int, but they don't include stdint.h themselves, so we have to leave
that one in place.

Signed-off-by: Erik Skultety <eskultet@redhat.com>
Acked-by: Michal Privoznik <mprivozn@redhat.com>
2018-09-20 10:16:39 +02:00

1311 lines
38 KiB
C

/*
* interface_backend_udev.c: udev backend for virInterface
*
* Copyright (C) 2014 Red Hat, Inc.
* Copyright (C) 2012 Doug Goldstein <cardoe@cardoe.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <dirent.h>
#include <libudev.h>
#include "virerror.h"
#include "virfile.h"
#include "c-ctype.h"
#include "datatypes.h"
#include "domain_conf.h"
#include "interface_driver.h"
#include "interface_conf.h"
#include "viralloc.h"
#include "virstring.h"
#include "viraccessapicheck.h"
#include "virinterfaceobj.h"
#include "virnetdev.h"
#define VIR_FROM_THIS VIR_FROM_INTERFACE
struct udev_iface_driver {
struct udev *udev;
bool privileged;
};
typedef enum {
VIR_UDEV_IFACE_ACTIVE,
VIR_UDEV_IFACE_INACTIVE,
VIR_UDEV_IFACE_ALL
} virUdevStatus;
static struct udev_iface_driver *driver;
static virInterfaceDef *udevGetIfaceDef(struct udev *udev, const char *name);
static const char *
virUdevStatusString(virUdevStatus status)
{
switch (status) {
case VIR_UDEV_IFACE_ACTIVE:
return "active";
case VIR_UDEV_IFACE_INACTIVE:
return "inactive";
case VIR_UDEV_IFACE_ALL:
return "all";
}
return "";
}
/*
* Get a minimal virInterfaceDef containing enough metadata
* for access control checks to be performed. Currently
* this implies existence of name and mac address attributes
*/
static virInterfaceDef * ATTRIBUTE_NONNULL(1)
udevGetMinimalDefForDevice(struct udev_device *dev)
{
virInterfaceDef *def;
/* Allocate our interface definition structure */
if (VIR_ALLOC(def) < 0)
return NULL;
if (VIR_STRDUP(def->name, udev_device_get_sysname(dev)) < 0)
goto cleanup;
if (VIR_STRDUP(def->mac, udev_device_get_sysattr_value(dev, "address")) < 0)
goto cleanup;
return def;
cleanup:
virInterfaceDefFree(def);
return NULL;
}
static struct udev_enumerate * ATTRIBUTE_NONNULL(1)
udevGetDevices(struct udev *udev, virUdevStatus status)
{
struct udev_enumerate *enumerate;
/* Create a new enumeration to create a list */
enumerate = udev_enumerate_new(udev);
if (!enumerate)
return NULL;
/* Enumerate all network subsystem devices */
udev_enumerate_add_match_subsystem(enumerate, "net");
/* Ignore devices that are part of a bridge */
udev_enumerate_add_nomatch_sysattr(enumerate, "brport/state", NULL);
/* State of the device */
switch (status) {
case VIR_UDEV_IFACE_ACTIVE:
udev_enumerate_add_match_sysattr(enumerate, "operstate", "up");
break;
case VIR_UDEV_IFACE_INACTIVE:
udev_enumerate_add_match_sysattr(enumerate, "operstate", "down");
break;
case VIR_UDEV_IFACE_ALL:
break;
}
/* We don't want to see the TUN devices that QEMU creates for other guests
* running on this machine. By saying nomatch NULL, we just are getting
* devices without the tun_flags sysattr.
*/
udev_enumerate_add_nomatch_sysattr(enumerate, "tun_flags", NULL);
return enumerate;
}
static int
udevNumOfInterfacesByStatus(virConnectPtr conn, virUdevStatus status,
virInterfaceObjListFilter filter)
{
struct udev *udev = udev_ref(driver->udev);
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *devices;
struct udev_list_entry *dev_entry;
int count = 0;
enumerate = udevGetDevices(udev, status);
if (!enumerate) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to get number of %s interfaces on host"),
virUdevStatusString(status));
count = -1;
goto cleanup;
}
/* Do the scan to load up the enumeration */
udev_enumerate_scan_devices(enumerate);
/* Get a list we can walk */
devices = udev_enumerate_get_list_entry(enumerate);
/* For each item so we can count */
udev_list_entry_foreach(dev_entry, devices) {
struct udev_device *dev;
const char *path;
virInterfaceDefPtr def;
path = udev_list_entry_get_name(dev_entry);
dev = udev_device_new_from_syspath(udev, path);
def = udevGetMinimalDefForDevice(dev);
if (filter(conn, def))
count++;
udev_device_unref(dev);
virInterfaceDefFree(def);
}
cleanup:
if (enumerate)
udev_enumerate_unref(enumerate);
udev_unref(udev);
return count;
}
static int
udevListInterfacesByStatus(virConnectPtr conn,
char **const names,
int names_len,
virUdevStatus status,
virInterfaceObjListFilter filter)
{
struct udev *udev = udev_ref(driver->udev);
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *devices;
struct udev_list_entry *dev_entry;
int count = 0;
enumerate = udevGetDevices(udev, status);
if (!enumerate) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to get list of %s interfaces on host"),
virUdevStatusString(status));
goto error;
}
/* Do the scan to load up the enumeration */
udev_enumerate_scan_devices(enumerate);
/* Get a list we can walk */
devices = udev_enumerate_get_list_entry(enumerate);
/* For each item so we can count */
udev_list_entry_foreach(dev_entry, devices) {
struct udev_device *dev;
const char *path;
virInterfaceDefPtr def;
/* Ensure we won't exceed the size of our array */
if (count > names_len)
break;
path = udev_list_entry_get_name(dev_entry);
dev = udev_device_new_from_syspath(udev, path);
def = udevGetMinimalDefForDevice(dev);
if (filter(conn, def)) {
if (VIR_STRDUP(names[count], udev_device_get_sysname(dev)) < 0) {
udev_device_unref(dev);
virInterfaceDefFree(def);
goto error;
}
count++;
}
udev_device_unref(dev);
virInterfaceDefFree(def);
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
return count;
error:
if (enumerate)
udev_enumerate_unref(enumerate);
udev_unref(udev);
for (names_len = 0; names_len < count; names_len++)
VIR_FREE(names[names_len]);
return -1;
}
static int
udevConnectNumOfInterfaces(virConnectPtr conn)
{
if (virConnectNumOfInterfacesEnsureACL(conn) < 0)
return -1;
return udevNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_ACTIVE,
virConnectNumOfInterfacesCheckACL);
}
static int
udevConnectListInterfaces(virConnectPtr conn,
char **const names,
int names_len)
{
if (virConnectListInterfacesEnsureACL(conn) < 0)
return -1;
return udevListInterfacesByStatus(conn, names, names_len,
VIR_UDEV_IFACE_ACTIVE,
virConnectListInterfacesCheckACL);
}
static int
udevConnectNumOfDefinedInterfaces(virConnectPtr conn)
{
if (virConnectNumOfDefinedInterfacesEnsureACL(conn) < 0)
return -1;
return udevNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_INACTIVE,
virConnectNumOfDefinedInterfacesCheckACL);
}
static int
udevConnectListDefinedInterfaces(virConnectPtr conn,
char **const names,
int names_len)
{
if (virConnectListDefinedInterfacesEnsureACL(conn) < 0)
return -1;
return udevListInterfacesByStatus(conn, names, names_len,
VIR_UDEV_IFACE_INACTIVE,
virConnectListDefinedInterfacesCheckACL);
}
#define MATCH(FLAG) (flags & (FLAG))
static int
udevConnectListAllInterfaces(virConnectPtr conn,
virInterfacePtr **ifaces,
unsigned int flags)
{
struct udev *udev;
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *devices;
struct udev_list_entry *dev_entry;
virInterfacePtr *ifaces_list = NULL;
virInterfacePtr iface_obj;
int tmp_count;
int count = 0;
int status = 0;
int ret;
virCheckFlags(VIR_CONNECT_LIST_INTERFACES_FILTERS_ACTIVE, -1);
if (virConnectListAllInterfacesEnsureACL(conn) < 0)
return -1;
/* Grab a udev reference */
udev = udev_ref(driver->udev);
/* List all interfaces in case we support more filter flags in the future */
enumerate = udevGetDevices(udev, VIR_UDEV_IFACE_ALL);
if (!enumerate) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to get list of %s interfaces on host"),
virUdevStatusString(status));
ret = -1;
goto cleanup;
}
/* Do the scan to load up the enumeration */
udev_enumerate_scan_devices(enumerate);
/* Get a list we can walk */
devices = udev_enumerate_get_list_entry(enumerate);
/* For each item so we can count */
udev_list_entry_foreach(dev_entry, devices) {
count++;
}
/* If we've got nothing, exit out */
if (count == 0) {
ret = 0;
goto cleanup;
}
/* If we're asked for the ifaces then alloc up memory */
if (ifaces && VIR_ALLOC_N(ifaces_list, count + 1) < 0) {
ret = -1;
goto cleanup;
}
/* Get a list we can walk */
devices = udev_enumerate_get_list_entry(enumerate);
/* reset our iterator */
count = 0;
/* Walk through each device */
udev_list_entry_foreach(dev_entry, devices) {
struct udev_device *dev;
const char *path;
const char *name;
const char *macaddr;
virInterfaceDefPtr def;
path = udev_list_entry_get_name(dev_entry);
dev = udev_device_new_from_syspath(udev, path);
name = udev_device_get_sysname(dev);
macaddr = udev_device_get_sysattr_value(dev, "address");
status = STREQ(udev_device_get_sysattr_value(dev, "operstate"), "up");
def = udevGetMinimalDefForDevice(dev);
if (!virConnectListAllInterfacesCheckACL(conn, def)) {
udev_device_unref(dev);
virInterfaceDefFree(def);
continue;
}
virInterfaceDefFree(def);
/* Filter the results */
if (MATCH(VIR_CONNECT_LIST_INTERFACES_FILTERS_ACTIVE) &&
!((MATCH(VIR_CONNECT_LIST_INTERFACES_ACTIVE) && status) ||
(MATCH(VIR_CONNECT_LIST_INTERFACES_INACTIVE) && !status))) {
udev_device_unref(dev);
continue;
}
/* If we matched a filter, then add it */
if (ifaces) {
iface_obj = virGetInterface(conn, name, macaddr);
ifaces_list[count++] = iface_obj;
}
udev_device_unref(dev);
}
/* Drop our refcounts */
udev_enumerate_unref(enumerate);
udev_unref(udev);
/* Trim the array to its final size */
if (ifaces) {
ignore_value(VIR_REALLOC_N(ifaces_list, count + 1));
*ifaces = ifaces_list;
ifaces_list = NULL;
}
return count;
cleanup:
if (enumerate)
udev_enumerate_unref(enumerate);
udev_unref(udev);
if (ifaces) {
for (tmp_count = 0; tmp_count < count; tmp_count++)
virObjectUnref(ifaces_list[tmp_count]);
}
VIR_FREE(ifaces_list);
return ret;
}
static virInterfacePtr
udevInterfaceLookupByName(virConnectPtr conn, const char *name)
{
struct udev *udev = udev_ref(driver->udev);
struct udev_device *dev;
virInterfacePtr ret = NULL;
virInterfaceDefPtr def = NULL;
/* get a device reference based on the device name */
dev = udev_device_new_from_subsystem_sysname(udev, "net", name);
if (!dev) {
virReportError(VIR_ERR_NO_INTERFACE,
_("couldn't find interface named '%s'"),
name);
goto cleanup;
}
if (!(def = udevGetMinimalDefForDevice(dev)))
goto cleanup;
if (virInterfaceLookupByNameEnsureACL(conn, def) < 0)
goto cleanup;
ret = virGetInterface(conn, def->name, def->mac);
udev_device_unref(dev);
cleanup:
udev_unref(udev);
virInterfaceDefFree(def);
return ret;
}
static virInterfacePtr
udevInterfaceLookupByMACString(virConnectPtr conn, const char *macstr)
{
struct udev *udev = udev_ref(driver->udev);
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *dev_entry;
struct udev_device *dev;
virInterfaceDefPtr def = NULL;
virInterfacePtr ret = NULL;
enumerate = udevGetDevices(udev, VIR_UDEV_IFACE_ALL);
if (!enumerate) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to lookup interface with MAC address '%s'"),
macstr);
goto cleanup;
}
/* Match on MAC */
udev_enumerate_add_match_sysattr(enumerate, "address", macstr);
/* Do the scan to load up the enumeration */
udev_enumerate_scan_devices(enumerate);
/* Get a list we can walk */
dev_entry = udev_enumerate_get_list_entry(enumerate);
/* Check that we got something back */
if (!dev_entry) {
virReportError(VIR_ERR_NO_INTERFACE,
_("couldn't find interface with MAC address '%s'"),
macstr);
goto cleanup;
}
/* Check that we didn't get multiple items back */
if (udev_list_entry_get_next(dev_entry)) {
virReportError(VIR_ERR_MULTIPLE_INTERFACES,
_("the MAC address '%s' matches multiple interfaces"),
macstr);
goto cleanup;
}
dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_entry));
if (!(def = udevGetMinimalDefForDevice(dev)))
goto cleanup;
if (virInterfaceLookupByMACStringEnsureACL(conn, def) < 0)
goto cleanup;
ret = virGetInterface(conn, def->name, def->mac);
udev_device_unref(dev);
cleanup:
if (enumerate)
udev_enumerate_unref(enumerate);
udev_unref(udev);
virInterfaceDefFree(def);
return ret;
}
/**
* Helper function for finding bond slaves using scandir()
*
* @param entry - directory entry passed by scandir()
*
* @return 1 if we want to add it to scandir's list, 0 if not.
*/
static int
udevBondScanDirFilter(const struct dirent *entry)
{
/* This is ugly so if anyone has a better suggestion, please improve
* this. Unfortunately the kernel stores everything in the top level
* interface sysfs entry and references the slaves as slave_eth0 for
* example.
*/
if (STRPREFIX(entry->d_name, "slave_"))
return 1;
return 0;
}
/**
* Helper function for finding bridge members using scandir()
*
* @param entry - directory entry passed by scandir()
*
* @return 1 if we want to add it to scandir's list, 0 if not.
*/
static int
udevBridgeScanDirFilter(const struct dirent *entry)
{
if (STREQ(entry->d_name, ".") || STREQ(entry->d_name, ".."))
return 0;
/* Omit the domain interfaces from the list of bridge attached
* devices. All we can do is check for the device name matching
* vnet%d. Improvements to this check are welcome.
*/
if (strlen(entry->d_name) >= 5) {
if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_TAP_PREFIX) &&
c_isdigit(entry->d_name[4]))
return 0;
}
return 1;
}
static int
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK
udevGetIfaceDefBond(struct udev *udev,
struct udev_device *dev,
const char *name,
virInterfaceDef *ifacedef)
{
struct dirent **slave_list = NULL;
int slave_count = 0;
size_t i;
const char *tmp_str;
int tmp_int;
/* Initial defaults */
ifacedef->data.bond.target = NULL;
ifacedef->data.bond.nbItf = 0;
ifacedef->data.bond.itf = NULL;
/* Set the bond specifics */
tmp_str = udev_device_get_sysattr_value(dev, "bonding/downdelay");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/downdelay' for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/downdelay' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.downdelay = tmp_int;
tmp_str = udev_device_get_sysattr_value(dev, "bonding/updelay");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/updelay' for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/updelay' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.updelay = tmp_int;
tmp_str = udev_device_get_sysattr_value(dev, "bonding/miimon");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/miimon' for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/miimon' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.frequency = tmp_int;
tmp_str = udev_device_get_sysattr_value(dev, "bonding/arp_interval");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/arp_interval' for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/arp_interval' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.interval = tmp_int;
/* bonding/mode is in the format: "balance-rr 0" so we find the
* space and increment the pointer to get the number and convert
* it to an interger. libvirt uses 1 through 7 while the raw
* number is 0 through 6 so increment it by 1.
*/
tmp_str = udev_device_get_sysattr_value(dev, "bonding/mode");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/mode' for '%s'"), name);
goto error;
}
tmp_str = strchr(tmp_str, ' ');
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid format for 'bonding/mode' for '%s'"), name);
goto error;
}
if (strlen(tmp_str) < 2) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find correct value in 'bonding/mode' for '%s'"),
name);
goto error;
}
if (virStrToLong_i(tmp_str + 1, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/mode' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.mode = tmp_int + 1;
/* bonding/arp_validate is in the format: "none 0" so we find the
* space and increment the pointer to get the number and convert
* it to an interger.
*/
tmp_str = udev_device_get_sysattr_value(dev, "bonding/arp_validate");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/arp_validate' for '%s'"), name);
goto error;
}
tmp_str = strchr(tmp_str, ' ');
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid format for 'bonding/arp_validate' for '%s'"), name);
goto error;
}
if (strlen(tmp_str) < 2) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find correct value in 'bonding/arp_validate' "
"for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str + 1, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/arp_validate' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.validate = tmp_int;
/* bonding/use_carrier is 0 or 1 and libvirt stores it as 1 or 2. */
tmp_str = udev_device_get_sysattr_value(dev, "bonding/use_carrier");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/use_carrier' for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bonding/use_carrier' '%s' for '%s'"),
tmp_str, name);
goto error;
}
ifacedef->data.bond.carrier = tmp_int + 1;
/* MII or ARP Monitoring is based on arp_interval and miimon.
* if arp_interval > 0 then ARP monitoring is in play, if
* miimon > 0 then MII monitoring is in play.
*/
if (ifacedef->data.bond.interval > 0)
ifacedef->data.bond.monit = VIR_INTERFACE_BOND_MONIT_ARP;
else if (ifacedef->data.bond.frequency > 0)
ifacedef->data.bond.monit = VIR_INTERFACE_BOND_MONIT_MII;
else
ifacedef->data.bond.monit = VIR_INTERFACE_BOND_MONIT_NONE;
tmp_str = udev_device_get_sysattr_value(dev, "bonding/arp_ip_target");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bonding/arp_ip_target' for '%s'"), name);
goto error;
}
if (VIR_STRDUP(ifacedef->data.bond.target, tmp_str) < 0)
goto error;
/* Slaves of the bond */
/* Get each slave in the bond */
slave_count = scandir(udev_device_get_syspath(dev), &slave_list,
udevBondScanDirFilter, alphasort);
if (slave_count < 0) {
virReportSystemError(errno,
_("Could not get slaves of bond '%s'"), name);
goto error;
}
/* Allocate our list of slave devices */
if (VIR_ALLOC_N(ifacedef->data.bond.itf, slave_count) < 0)
goto error;
ifacedef->data.bond.nbItf = slave_count;
for (i = 0; i < slave_count; i++) {
/* Names are slave_interface. e.g. slave_eth0
* so we use the part after the _
*/
tmp_str = strchr(slave_list[i]->d_name, '_');
if (!tmp_str || strlen(tmp_str) < 2) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid enslaved interface name '%s' seen for "
"bond '%s'"), slave_list[i]->d_name, name);
goto error;
}
/* go past the _ */
tmp_str++;
ifacedef->data.bond.itf[i] =
udevGetIfaceDef(udev, tmp_str);
if (!ifacedef->data.bond.itf[i]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get interface information for '%s', which is "
"a enslaved in bond '%s'"), slave_list[i]->d_name, name);
goto error;
}
VIR_FREE(slave_list[i]);
}
VIR_FREE(slave_list);
return 0;
error:
for (i = 0; slave_count != -1 && i < slave_count; i++)
VIR_FREE(slave_list[i]);
VIR_FREE(slave_list);
return -1;
}
static int
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK
udevGetIfaceDefBridge(struct udev *udev,
struct udev_device *dev,
const char *name,
virInterfaceDef *ifacedef)
{
struct dirent **member_list = NULL;
int member_count = 0;
char *member_path;
const char *tmp_str;
int stp;
size_t i;
/* Set our type to Bridge */
ifacedef->type = VIR_INTERFACE_TYPE_BRIDGE;
/* Retrieve the forward delay */
tmp_str = udev_device_get_sysattr_value(dev, "bridge/forward_delay");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bridge/forward_delay' for '%s'"), name);
goto error;
}
if (VIR_STRDUP(ifacedef->data.bridge.delay, tmp_str) < 0)
goto error;
/* Retrieve Spanning Tree State. Valid values = -1, 0, 1 */
tmp_str = udev_device_get_sysattr_value(dev, "bridge/stp_state");
if (!tmp_str) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not retrieve 'bridge/stp_state' for '%s'"), name);
goto error;
}
if (virStrToLong_i(tmp_str, NULL, 10, &stp) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse 'bridge/stp_state' '%s' for '%s'"),
tmp_str, name);
goto error;
}
switch (stp) {
case -1:
case 0:
case 1:
ifacedef->data.bridge.stp = stp;
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid STP state value %d received for '%s'. Must be "
"-1, 0, or 1."), stp, name);
goto error;
}
/* Members of the bridge */
if (virAsprintf(&member_path, "%s/%s",
udev_device_get_syspath(dev), "brif") < 0)
goto error;
/* Get each member of the bridge */
member_count = scandir(member_path, &member_list,
udevBridgeScanDirFilter, alphasort);
/* Don't need the path anymore */
VIR_FREE(member_path);
if (member_count < 0) {
virReportSystemError(errno,
_("Could not get members of bridge '%s'"),
name);
goto error;
}
/* Allocate our list of member devices */
if (VIR_ALLOC_N(ifacedef->data.bridge.itf, member_count) < 0)
goto error;
ifacedef->data.bridge.nbItf = member_count;
/* Get the interface definitions for each member of the bridge */
for (i = 0; i < member_count; i++) {
ifacedef->data.bridge.itf[i] =
udevGetIfaceDef(udev, member_list[i]->d_name);
if (!ifacedef->data.bridge.itf[i]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get interface information for '%s', which is "
"a member of bridge '%s'"), member_list[i]->d_name, name);
goto error;
}
VIR_FREE(member_list[i]);
}
VIR_FREE(member_list);
return 0;
error:
for (i = 0; member_count != -1 && i < member_count; i++)
VIR_FREE(member_list[i]);
VIR_FREE(member_list);
return -1;
}
static int
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK
udevGetIfaceDefVlan(struct udev *udev ATTRIBUTE_UNUSED,
struct udev_device *dev ATTRIBUTE_UNUSED,
const char *name,
virInterfaceDef *ifacedef)
{
char *procpath = NULL;
char *buf = NULL;
char *vid_pos, *dev_pos;
size_t vid_len, dev_len;
const char *vid_prefix = "VID: ";
const char *dev_prefix = "\nDevice: ";
int ret = -1;
if (virAsprintf(&procpath, "/proc/net/vlan/%s", name) < 0)
goto cleanup;
if (virFileReadAll(procpath, BUFSIZ, &buf) < 0)
goto cleanup;
if ((vid_pos = strstr(buf, vid_prefix)) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to find the VID for the VLAN device '%s'"),
name);
goto cleanup;
}
vid_pos += strlen(vid_prefix);
if ((vid_len = strspn(vid_pos, "0123456789")) == 0 ||
!c_isspace(vid_pos[vid_len])) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to find the VID for the VLAN device '%s'"),
name);
goto cleanup;
}
if ((dev_pos = strstr(vid_pos + vid_len, dev_prefix)) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to find the real device for the VLAN device '%s'"),
name);
goto cleanup;
}
dev_pos += strlen(dev_prefix);
if ((dev_len = strcspn(dev_pos, "\n")) == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to find the real device for the VLAN device '%s'"),
name);
goto cleanup;
}
if (VIR_STRNDUP(ifacedef->data.vlan.tag, vid_pos, vid_len) < 0)
goto cleanup;
if (VIR_STRNDUP(ifacedef->data.vlan.dev_name, dev_pos, dev_len) < 0) {
VIR_FREE(ifacedef->data.vlan.tag);
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(procpath);
VIR_FREE(buf);
return ret;
}
static virInterfaceDef * ATTRIBUTE_NONNULL(1)
udevGetIfaceDef(struct udev *udev, const char *name)
{
struct udev_device *dev = NULL;
virInterfaceDef *ifacedef;
unsigned int mtu;
const char *mtu_str;
char *vlan_parent_dev = NULL;
const char *devtype;
/* Allocate our interface definition structure */
if (VIR_ALLOC(ifacedef) < 0)
return NULL;
/* Clear our structure and set safe defaults */
ifacedef->startmode = VIR_INTERFACE_START_UNSPECIFIED;
if (VIR_STRDUP(ifacedef->name, name) < 0)
goto error;
/* Lookup the device we've been asked about */
dev = udev_device_new_from_subsystem_sysname(udev, "net", name);
if (!dev) {
virReportError(VIR_ERR_NO_INTERFACE,
_("couldn't find interface named '%s'"), name);
goto error;
}
/* MAC address */
if (VIR_STRDUP(ifacedef->mac,
udev_device_get_sysattr_value(dev, "address")) < 0)
goto error;
/* Link state and speed */
if (virNetDevGetLinkInfo(ifacedef->name, &ifacedef->lnk) < 0)
goto error;
/* MTU */
mtu_str = udev_device_get_sysattr_value(dev, "mtu");
if (virStrToLong_ui(mtu_str, NULL, 10, &mtu) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse MTU value '%s'"), mtu_str);
goto error;
}
ifacedef->mtu = mtu;
/* Number of IP protocols this interface has assigned */
/* XXX: Do we want a netlink query or a call out to ip or leave it? */
ifacedef->nprotos = 0;
ifacedef->protos = NULL;
/* Check the type of device we are working with based on the devtype */
devtype = udev_device_get_devtype(dev);
/* Set our type to ethernet as the default case */
ifacedef->type = VIR_INTERFACE_TYPE_ETHERNET;
if (STREQ_NULLABLE(devtype, "vlan")) {
/* This only works on modern kernels (3.7 and newer)
* e949b09b71d975a82f13ac88ce4ad338fed213da
*/
ifacedef->type = VIR_INTERFACE_TYPE_VLAN;
} else if (STREQ_NULLABLE(devtype, "bridge")) {
ifacedef->type = VIR_INTERFACE_TYPE_BRIDGE;
} else if (STREQ_NULLABLE(devtype, "bond")) {
/* This only works on modern kernels (3.9 and newer) */
ifacedef->type = VIR_INTERFACE_TYPE_BOND;
}
/* Fallback checks if the devtype check didn't work. */
if (ifacedef->type == VIR_INTERFACE_TYPE_ETHERNET) {
/* First check if its a VLAN based on the name containing a dot,
* to prevent false positives
*/
vlan_parent_dev = strrchr(name, '.');
if (vlan_parent_dev)
ifacedef->type = VIR_INTERFACE_TYPE_VLAN;
/* Fallback check to see if this is a bond device */
if (udev_device_get_sysattr_value(dev, "bonding/mode"))
ifacedef->type = VIR_INTERFACE_TYPE_BOND;
}
switch (ifacedef->type) {
case VIR_INTERFACE_TYPE_VLAN:
if (udevGetIfaceDefVlan(udev, dev, name, ifacedef) < 0)
goto error;
break;
case VIR_INTERFACE_TYPE_BRIDGE:
if (udevGetIfaceDefBridge(udev, dev, name, ifacedef) < 0)
goto error;
break;
case VIR_INTERFACE_TYPE_BOND:
if (udevGetIfaceDefBond(udev, dev, name, ifacedef) < 0)
goto error;
break;
case VIR_INTERFACE_TYPE_ETHERNET:
break;
}
udev_device_unref(dev);
return ifacedef;
error:
udev_device_unref(dev);
virInterfaceDefFree(ifacedef);
return NULL;
}
static char *
udevInterfaceGetXMLDesc(virInterfacePtr ifinfo,
unsigned int flags)
{
struct udev *udev = udev_ref(driver->udev);
virInterfaceDef *ifacedef;
char *xmlstr = NULL;
virCheckFlags(VIR_INTERFACE_XML_INACTIVE, NULL);
/* Recursively build up the interface XML based on the requested
* interface name
*/
ifacedef = udevGetIfaceDef(udev, ifinfo->name);
if (!ifacedef)
goto cleanup;
if (virInterfaceGetXMLDescEnsureACL(ifinfo->conn, ifacedef) < 0)
goto cleanup;
xmlstr = virInterfaceDefFormat(ifacedef);
virInterfaceDefFree(ifacedef);
cleanup:
/* decrement our udev ptr */
udev_unref(udev);
return xmlstr;
}
static int
udevInterfaceIsActive(virInterfacePtr ifinfo)
{
struct udev *udev = udev_ref(driver->udev);
struct udev_device *dev;
virInterfaceDefPtr def = NULL;
int status = -1;
dev = udev_device_new_from_subsystem_sysname(udev, "net",
ifinfo->name);
if (!dev) {
virReportError(VIR_ERR_NO_INTERFACE,
_("couldn't find interface named '%s'"),
ifinfo->name);
goto cleanup;
}
if (!(def = udevGetMinimalDefForDevice(dev)))
goto cleanup;
if (virInterfaceIsActiveEnsureACL(ifinfo->conn, def) < 0)
goto cleanup;
/* Check if it's active or not */
status = STREQ(udev_device_get_sysattr_value(dev, "operstate"), "up");
udev_device_unref(dev);
cleanup:
udev_unref(udev);
virInterfaceDefFree(def);
return status;
}
static int
udevStateInitialize(bool privileged,
virStateInhibitCallback callback ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
if (VIR_ALLOC(driver) < 0)
goto cleanup;
driver->udev = udev_new();
if (!driver->udev) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to create udev context"));
goto cleanup;
}
driver->privileged = privileged;
ret = 0;
cleanup:
return ret;
}
static int
udevStateCleanup(void)
{
if (!driver)
return -1;
udev_unref(driver->udev);
VIR_FREE(driver);
return 0;
}
static virDrvOpenStatus
udevConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
virConfPtr conf ATTRIBUTE_UNUSED,
unsigned int flags)
{
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (driver == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("interface state driver is not active"));
return VIR_DRV_OPEN_ERROR;
}
if (driver->privileged) {
if (STRNEQ(conn->uri->path, "/system")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected interface URI path '%s', try interface:///system"),
conn->uri->path);
return VIR_DRV_OPEN_ERROR;
}
} else {
if (STRNEQ(conn->uri->path, "/session")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected interface URI path '%s', try interface:///session"),
conn->uri->path);
return VIR_DRV_OPEN_ERROR;
}
}
if (virConnectOpenEnsureACL(conn) < 0)
return VIR_DRV_OPEN_ERROR;
return VIR_DRV_OPEN_SUCCESS;
}
static int udevConnectClose(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 0;
}
static int udevConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED)
{
/* Trivially secure, since always inside the daemon */
return 1;
}
static int udevConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED)
{
/* Not encrypted, but remote driver takes care of that */
return 0;
}
static int udevConnectIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 1;
}
static virInterfaceDriver udevIfaceDriver = {
.name = "udev",
.connectNumOfInterfaces = udevConnectNumOfInterfaces, /* 1.0.0 */
.connectListInterfaces = udevConnectListInterfaces, /* 1.0.0 */
.connectNumOfDefinedInterfaces = udevConnectNumOfDefinedInterfaces, /* 1.0.0 */
.connectListDefinedInterfaces = udevConnectListDefinedInterfaces, /* 1.0.0 */
.connectListAllInterfaces = udevConnectListAllInterfaces, /* 1.0.0 */
.interfaceLookupByName = udevInterfaceLookupByName, /* 1.0.0 */
.interfaceLookupByMACString = udevInterfaceLookupByMACString, /* 1.0.0 */
.interfaceIsActive = udevInterfaceIsActive, /* 1.0.0 */
.interfaceGetXMLDesc = udevInterfaceGetXMLDesc, /* 1.0.0 */
};
static virHypervisorDriver udevHypervisorDriver = {
.name = "interface",
.connectOpen = udevConnectOpen, /* 4.1.0 */
.connectClose = udevConnectClose, /* 4.1.0 */
.connectIsEncrypted = udevConnectIsEncrypted, /* 4.1.0 */
.connectIsSecure = udevConnectIsSecure, /* 4.1.0 */
.connectIsAlive = udevConnectIsAlive, /* 4.1.0 */
};
static virConnectDriver udevConnectDriver = {
.localOnly = true,
.uriSchemes = (const char *[]){ "interface", NULL },
.hypervisorDriver = &udevHypervisorDriver,
.interfaceDriver = &udevIfaceDriver,
};
static virStateDriver interfaceStateDriver = {
.name = "udev",
.stateInitialize = udevStateInitialize,
.stateCleanup = udevStateCleanup,
};
int
udevIfaceRegister(void)
{
if (virRegisterConnectDriver(&udevConnectDriver, false) < 0)
return -1;
if (virSetSharedInterfaceDriver(&udevIfaceDriver) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to register udev interface driver"));
return -1;
}
if (virRegisterStateDriver(&interfaceStateDriver) < 0)
return -1;
return 0;
}