[libvirt] [PATCH 4/4] interface: add bond support to udev backend

Laine Stump laine at laine.org
Mon Feb 18 21:11:47 UTC 2013


On 02/17/2013 08:56 PM, Doug Goldstein wrote:
> The udev backend now supports bond interfaces.
> ---
> The result of an iface-dumpxml bond0 is as follows:
>
> <interface type='bond' name='bond0'>
>   <mtu size='1500'/>
>   <bond mode='balance-rr'>
>     <interface type='ethernet' name='eth2'>
>       <mac address='d0:67:e5:fa:88:95'/>
>       <mtu size='1500'/>
>     </interface>
>     <interface type='ethernet' name='eth3'>
>       <mac address='d0:67:e5:fa:88:95'/>
>       <mtu size='1500'/>
>     </interface>
>   </bond>
> </interface>
> ---
>  src/interface/interface_backend_udev.c | 195 +++++++++++++++++++++++++++++++++
>  1 file changed, 195 insertions(+)
>
> diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c
> index 73494a6..bba02d1 100644
> --- a/src/interface/interface_backend_udev.c
> +++ b/src/interface/interface_backend_udev.c
> @@ -494,6 +494,22 @@ err:
>  }
>  
>  /**
> + * 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)
> +{
> +    if (STRPREFIX(entry->d_name, "slave_"))
> +        return 1;

Urg. That *feels* kind of ugly, but if that's what it takes, then that's
what it takes...

> +
> +    return 0;
> +}
> +
> +/**
>   * Helper function for finding bridge members using scandir()
>   *
>   * @param entry - directory entry passed by scandir()
> @@ -522,6 +538,14 @@ udevIfaceFreeIfaceDef(virInterfaceDef *ifacedef)
>      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++) {
> @@ -541,6 +565,168 @@ udevIfaceFreeIfaceDef(virInterfaceDef *ifacedef)
>  }
>  
>  static int
> +udevIfaceGetIfaceDefBond(struct udev *udev ATTRIBUTE_UNUSED,
> +                         struct udev_device *dev ATTRIBUTE_UNUSED,
> +                         const char *name,
> +                         virInterfaceDef *ifacedef)
> +                         ATTRIBUTE_NONNULL(1)
> +                         ATTRIBUTE_NONNULL(2)
> +                         ATTRIBUTE_NONNULL(3)
> +                         ATTRIBUTE_NONNULL(4)
> +                         ATTRIBUTE_RETURN_CHECK;
> +static int
> +udevIfaceGetIfaceDefBond(struct udev *udev ATTRIBUTE_UNUSED,
> +                         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 (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {

How does virStrToLong_i handle a NULL string? (Is it possible that
udev_device_get_sysattr_value() could ever return NULL, e.g. if the
value isn't set?)

> +        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 (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {

Same comment as above.

> +        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 (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 (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {

Same comment as above.

> +        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 (virStrToLong_i(strchr(tmp_str, ' ') + 1, NULL, 10, &tmp_int) < 0) {

Ooh. Are you guaranteed that udev_device_get_sysattr_value will return
non-NULL, and that it will *always* contain a space? If not, the above
line could segfault (especially devious because of the "+1" - even a
function that checked for NULL and special-cased it would fail that
check and try to dereference 0x1. I think you need to 1) check from
tmp_str != NULL, 2) do the strchr() separately and check for NULL, then
call virStrToLong_i().


> +        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 (virStrToLong_i(strchr(tmp_str, ' ') + 1, NULL, 10, &tmp_int) < 0) {

Same comment as above.

> +        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 (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;
> +
> +    ifacedef->data.bond.target =
> +        strdup(udev_device_get_sysattr_value(dev, "bonding/arp_ip_target"));

Same question here as before - is it possible that the above udev
function vould return NULL in some case other than an error? If so,
you'll need to call that in a separate step from strdup().

> +    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);

Need to check for NULL return here and appropriately cleanup if encountered.

> +        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
>  udevIfaceGetIfaceDefBridge(struct udev *udev,
>                             struct udev_device *dev,
>                             const char *name,
> @@ -752,6 +938,11 @@ udevIfaceGetIfaceDef(struct udev *udev, const char *name)
>          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;
> +    }
> +

As with the "special case" check for vlans on kernels older than 3.7,
this clause is checked even if ifacedef->type was already set to
something else. Both this, and the special vlan case that looks for a
"." in the device name, should be in an "else if()" of the original if
clause.

>      switch (ifacedef->type) {
>      case VIR_INTERFACE_TYPE_VLAN:
>          if (udevIfaceGetIfaceDefVlan(udev, dev, name, ifacedef))
> @@ -761,6 +952,10 @@ udevIfaceGetIfaceDef(struct udev *udev, const char *name)
>          if (udevIfaceGetIfaceDefBridge(udev, dev, name, ifacedef))
>              goto cleanup;
>          break;
> +    case VIR_INTERFACE_TYPE_BOND:
> +        if (udevIfaceGetIfaceDefBond(udev, dev, name, ifacedef))
> +            goto cleanup;
> +        break;

Shoot! I didn't notice before that you're doing if(function()) here
rather than the more standard "if (function() < 0)". All of the cases in
this switch should check for < 0 rather than just non-0 to fit with
libvirt coding practices (otherwise I find myself thinking that the
function returns a string :-) That nit retroactively applies to the
first 3 patches.


>      case VIR_INTERFACE_TYPE_ETHERNET:
>          break;
>      }




More information about the libvir-list mailing list