/* * interface_backend_udev.c: udev backend for virInterface * * Copyright (C) 2012 Doug Goldstein * * 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 * . */ #include #include #include #include #include "virerror.h" #include "datatypes.h" #include "interface_driver.h" #include "interface_conf.h" #include "viralloc.h" #define VIR_FROM_THIS VIR_FROM_INTERFACE struct udev_iface_driver { struct udev *udev; }; typedef enum { VIR_UDEV_IFACE_ACTIVE, VIR_UDEV_IFACE_INACTIVE, VIR_UDEV_IFACE_ALL } virUdevStatus ; static virInterfaceDef *udevIfaceGetIfaceDef(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 ""; } static struct udev_enumerate * ATTRIBUTE_NONNULL(1) udevIfaceGetDevices(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 virDrvOpenStatus udevIfaceOpenInterface(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, unsigned int flags) { struct udev_iface_driver *driverState = NULL; virCheckFlags(0, VIR_DRV_OPEN_ERROR); if (VIR_ALLOC(driverState) < 0) { virReportOOMError(); goto err; } driverState->udev = udev_new(); if (!driverState->udev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create udev context")); goto err; } conn->interfacePrivateData = driverState; return VIR_DRV_OPEN_SUCCESS; err: VIR_FREE(driverState); return VIR_DRV_OPEN_ERROR; } static int udevIfaceCloseInterface(virConnectPtr conn) { struct udev_iface_driver *driverState; if (conn->interfacePrivateData != NULL) { driverState = conn->interfacePrivateData; udev_unref(driverState->udev); VIR_FREE(driverState); } conn->interfacePrivateData = NULL; return 0; } static int udevIfaceNumOfInterfacesByStatus(virConnectPtr conn, virUdevStatus status) { struct udev_iface_driver *driverState = conn->interfacePrivateData; struct udev *udev = udev_ref(driverState->udev); struct udev_enumerate *enumerate = NULL; struct udev_list_entry *devices; struct udev_list_entry *dev_entry; int count = 0; enumerate = udevIfaceGetDevices(udev, status); if (!enumerate) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to get number of %s interfaces on host"), virUdevStatusString(status)); count = -1; goto err; } /* 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++; } err: if (enumerate) udev_enumerate_unref(enumerate); udev_unref(udev); return count; } static int udevIfaceListInterfacesByStatus(virConnectPtr conn, char **const names, int names_len, virUdevStatus status) { struct udev_iface_driver *driverState = conn->interfacePrivateData; struct udev *udev = udev_ref(driverState->udev); struct udev_enumerate *enumerate = NULL; struct udev_list_entry *devices; struct udev_list_entry *dev_entry; int count = 0; enumerate = udevIfaceGetDevices(udev, status); if (!enumerate) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to get list of %s interfaces on host"), virUdevStatusString(status)); goto err; } /* 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; /* 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); names[count] = strdup(udev_device_get_sysname(dev)); udev_device_unref(dev); /* If strdup() failed, we are out of memory */ if (!names[count]) { virReportOOMError(); goto err; } count++; } udev_enumerate_unref(enumerate); udev_unref(udev); return count; err: 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 udevIfaceNumOfInterfaces(virConnectPtr conn) { return udevIfaceNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_ACTIVE); } static int udevIfaceListInterfaces(virConnectPtr conn, char **const names, int names_len) { return udevIfaceListInterfacesByStatus(conn, names, names_len, VIR_UDEV_IFACE_ACTIVE); } static int udevIfaceNumOfDefinedInterfaces(virConnectPtr conn) { return udevIfaceNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_INACTIVE); } static int udevIfaceListDefinedInterfaces(virConnectPtr conn, char **const names, int names_len) { return udevIfaceListInterfacesByStatus(conn, names, names_len, VIR_UDEV_IFACE_INACTIVE); } static int udevIfaceListAllInterfaces(virConnectPtr conn, virInterfacePtr **ifaces, unsigned int flags) { struct udev_iface_driver *driverState = conn->interfacePrivateData; 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_ACTIVE | VIR_CONNECT_LIST_INTERFACES_INACTIVE, -1); /* Grab a udev reference */ udev = udev_ref(driverState->udev); /* List all interfaces in case we support more filter flags in the future */ enumerate = udevIfaceGetDevices(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) { if (VIR_ALLOC_N(ifaces_list, count + 1) < 0) { virReportOOMError(); 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; int add_to_list = 0; 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"); /* Filter the results */ if (status && (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE)) add_to_list = 1; else if (!status && (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE)) add_to_list = 1; /* If we matched a filter, then add it */ if (add_to_list) { if (ifaces) { iface_obj = virGetInterface(conn, name, macaddr); ifaces_list[count] = iface_obj; } count++; } 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; } return count; cleanup: if (enumerate) udev_enumerate_unref(enumerate); udev_unref(udev); if (ifaces) { for (tmp_count = 0; tmp_count < count; tmp_count++) virInterfaceFree(ifaces_list[tmp_count]); } VIR_FREE(ifaces_list); return ret; } static virInterfacePtr udevIfaceLookupByName(virConnectPtr conn, const char *name) { struct udev_iface_driver *driverState = conn->interfacePrivateData; struct udev *udev = udev_ref(driverState->udev); struct udev_device *dev; const char *macaddr; virInterfacePtr ret = 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 err; } macaddr = udev_device_get_sysattr_value(dev, "address"); ret = virGetInterface(conn, name, macaddr); udev_device_unref(dev); err: udev_unref(udev); return ret; } static virInterfacePtr udevIfaceLookupByMACString(virConnectPtr conn, const char *macstr) { struct udev_iface_driver *driverState = conn->interfacePrivateData; struct udev *udev = udev_ref(driverState->udev); struct udev_enumerate *enumerate = NULL; struct udev_list_entry *dev_entry; struct udev_device *dev; const char *name; virInterfacePtr ret = NULL; enumerate = udevIfaceGetDevices(udev, VIR_UDEV_IFACE_ALL); if (!enumerate) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to lookup interface with MAC address '%s'"), macstr); goto err; } /* 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 err; } /* 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 err; } dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_entry)); name = udev_device_get_sysname(dev); ret = virGetInterface(conn, name, macstr); udev_device_unref(dev); err: if (enumerate) udev_enumerate_unref(enumerate); udev_unref(udev); 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 udevIfaceBondScanDirFilter(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 udevIfaceBridgeScanDirFilter(const struct dirent *entry) { if (STREQ(entry->d_name, ".") || STREQ(entry->d_name, "..")) return 0; return 1; } /** * Frees any memory allocated by udevIfaceGetIfaceDef() * * @param ifacedef - interface to free and cleanup */ static void udevIfaceFreeIfaceDef(virInterfaceDef *ifacedef) { int i; if (!ifacedef) return; if (ifacedef->type == VIR_INTERFACE_TYPE_BOND) { VIR_FREE(ifacedef->data.bond.target); for (i = 0; i < ifacedef->data.bond.nbItf; i++) { udevIfaceFreeIfaceDef(ifacedef->data.bond.itf[i]); } VIR_FREE(ifacedef->data.bond.itf); } if (ifacedef->type == VIR_INTERFACE_TYPE_BRIDGE) { VIR_FREE(ifacedef->data.bridge.delay); for (i = 0; i < ifacedef->data.bridge.nbItf; i++) { udevIfaceFreeIfaceDef(ifacedef->data.bridge.itf[i]); } VIR_FREE(ifacedef->data.bridge.itf); } if (ifacedef->type == VIR_INTERFACE_TYPE_VLAN) { VIR_FREE(ifacedef->data.vlan.devname); } VIR_FREE(ifacedef->mac); VIR_FREE(ifacedef->name); VIR_FREE(ifacedef); } static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK udevIfaceGetIfaceDefBond(struct udev *udev, struct udev_device *dev, const char *name, virInterfaceDef *ifacedef) { struct dirent **slave_list = NULL; int slave_count = 0; int 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } tmp_str = strchr(tmp_str, ' '); if (!tmp_str) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid format for 'bonding/mode' for '%s'"), name); goto cleanup; } if (strlen(tmp_str) < 2) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to find correct value in 'bonding/mode' for '%s'"), name); goto cleanup; } 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 cleanup; } 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 cleanup; } tmp_str = strchr(tmp_str, ' '); if (!tmp_str) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid format for 'bonding/arp_validate' for '%s'"), name); goto cleanup; } if (strlen(tmp_str) < 2) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to find correct value in 'bonding/arp_validate' " "for '%s'"), name); goto cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } 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 cleanup; } ifacedef->data.bond.target = strdup(tmp_str); if (!ifacedef->data.bond.target) { virReportOOMError(); goto cleanup; } /* Slaves of the bond */ /* Get each slave in the bond */ slave_count = scandir(udev_device_get_syspath(dev), &slave_list, udevIfaceBondScanDirFilter, alphasort); if (slave_count < 0) { virReportSystemError(errno, _("Could not get slaves of bond '%s'"), name); goto cleanup; } /* Allocate our list of slave devices */ if (VIR_ALLOC_N(ifacedef->data.bond.itf, slave_count) < 0) { virReportOOMError(); goto cleanup; } 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, '_'); tmp_str++; ifacedef->data.bond.itf[i] = udevIfaceGetIfaceDef(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 cleanup; } VIR_FREE(slave_list[i]); } VIR_FREE(slave_list); return 0; cleanup: for (i = 0; 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 udevIfaceGetIfaceDefBridge(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; int 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; } ifacedef->data.bridge.delay = strdup(tmp_str); if (!ifacedef->data.bridge.delay) { virReportOOMError(); 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) { virReportOOMError(); goto error; } /* Get each member of the bridge */ member_count = scandir(member_path, &member_list, udevIfaceBridgeScanDirFilter, 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) { virReportOOMError(); goto error; } ifacedef->data.bridge.nbItf = member_count; /* Get the interface defintions for each member of the bridge */ for (i = 0; i < member_count; i++) { ifacedef->data.bridge.itf[i] = udevIfaceGetIfaceDef(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; 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 udevIfaceGetIfaceDefVlan(struct udev *udev ATTRIBUTE_UNUSED, struct udev_device *dev ATTRIBUTE_UNUSED, const char *name, virInterfaceDef *ifacedef) { char *vid; char *vlan_parent_dev = NULL; vlan_parent_dev = strdup(name); if (!vlan_parent_dev) { virReportOOMError(); goto cleanup; } /* Find the DEVICE.VID again */ vid = strrchr(vlan_parent_dev, '.'); if (!vid) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to find the VID for the VLAN device '%s'"), name); goto cleanup; } /* Replace the dot with a NULL so we can have the device and VID */ vid[0] = '\0'; vid++; /* Set the VLAN specifics */ ifacedef->data.vlan.tag = vid; ifacedef->data.vlan.devname = vlan_parent_dev; return 0; cleanup: VIR_FREE(vlan_parent_dev); return -1; } static virInterfaceDef * ATTRIBUTE_NONNULL(1) udevIfaceGetIfaceDef(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) { virReportOOMError(); return NULL; } /* Clear our structure and set safe defaults */ ifacedef->startmode = VIR_INTERFACE_START_UNSPECIFIED; ifacedef->name = strdup(name); if (!ifacedef->name) { virReportOOMError(); goto cleanup; } /* 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 cleanup; } /* MAC address */ ifacedef->mac = strdup(udev_device_get_sysattr_value(dev, "address")); if (!ifacedef->mac) { virReportOOMError(); goto cleanup; } /* 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 cleanup; } 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 (udevIfaceGetIfaceDefVlan(udev, dev, name, ifacedef) < 0) goto cleanup; break; case VIR_INTERFACE_TYPE_BRIDGE: if (udevIfaceGetIfaceDefBridge(udev, dev, name, ifacedef) < 0) goto cleanup; break; case VIR_INTERFACE_TYPE_BOND: if (udevIfaceGetIfaceDefBond(udev, dev, name, ifacedef) < 0) goto cleanup; break; case VIR_INTERFACE_TYPE_ETHERNET: break; } udev_device_unref(dev); return ifacedef; cleanup: udev_device_unref(dev); udevIfaceFreeIfaceDef(ifacedef); return NULL; } static char * udevIfaceGetXMLDesc(virInterfacePtr ifinfo, unsigned int flags) { struct udev_iface_driver *driverState = ifinfo->conn->interfacePrivateData; struct udev *udev = udev_ref(driverState->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 = udevIfaceGetIfaceDef(udev, ifinfo->name); /* We've already printed by it happened */ if (!ifacedef) goto err; /* Convert our interface to XML */ xmlstr = virInterfaceDefFormat(ifacedef); /* Recursively free our interface structures and free the children too */ udevIfaceFreeIfaceDef(ifacedef); err: /* decrement our udev ptr */ udev_unref(udev); return xmlstr; } static int udevIfaceIsActive(virInterfacePtr ifinfo) { struct udev_iface_driver *driverState = ifinfo->conn->interfacePrivateData; struct udev *udev = udev_ref(driverState->udev); struct udev_device *dev; int status; 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); status = -1; 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); return status; } static virInterfaceDriver udevIfaceDriver = { "udev", .open = udevIfaceOpenInterface, /* 1.0.0 */ .close = udevIfaceCloseInterface, /* 1.0.0 */ .numOfInterfaces = udevIfaceNumOfInterfaces, /* 1.0.0 */ .listInterfaces = udevIfaceListInterfaces, /* 1.0.0 */ .numOfDefinedInterfaces = udevIfaceNumOfDefinedInterfaces, /* 1.0.0 */ .listDefinedInterfaces = udevIfaceListDefinedInterfaces, /* 1.0.0 */ .listAllInterfaces = udevIfaceListAllInterfaces, /* 1.0.0 */ .interfaceLookupByName = udevIfaceLookupByName, /* 1.0.0 */ .interfaceLookupByMACString = udevIfaceLookupByMACString, /* 1.0.0 */ .interfaceIsActive = udevIfaceIsActive, /* 1.0.0 */ .interfaceGetXMLDesc = udevIfaceGetXMLDesc, /* 1.0.0 */ }; int udevIfaceRegister(void) { if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to register udev interface driver")); return -1; } return 0; }