[libvirt] [PATCH v3]: set and restore MAC address of a NIC when using PASSTHROUGH mode

Daniel P. Berrange berrange at redhat.com
Tue Jun 21 10:41:41 UTC 2011


On Tue, Jun 21, 2011 at 11:08:38AM +0200, Gerhard Stenzel wrote:
> 
> This is another rework of the patch from Dirk addressing all comments
> received so far.
> 
> The following patch addresses the problem that when a PASSTHROUGH 
> mode DIRECT NIC connection is made the MAC address of the NIC is 
> not automatically set and reset to the configured VM MAC and 
> back again.
> 
> The attached patch fixes this problem by setting and resetting the MAC
> while remembering the previous setting while the VM is running.
> This also works if libvirtd is restarted while the VM is running.
> 
> the patch passes make syntax-check
> 
> Signed-off-by: Dirk Herrendoerfer <d.herrendoerfer at herrendoerfer.name>
> Signed-off-by: Gerhard Stenzel <gerhard.stenzel at de.ibm.com>
> 
> ---
> 
> 
> Index: libvirt/src/qemu/qemu_command.c
> ===================================================================
> --- libvirt.orig/src/qemu/qemu_command.c
> +++ libvirt/src/qemu/qemu_command.c
> @@ -128,7 +128,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def
>      rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
>                          net->data.direct.mode, vnet_hdr, def->uuid,
>                          &net->data.direct.virtPortProfile, &res_ifname,
> -                        vmop);
> +                        vmop, driver->stateDir);
>      if (rc >= 0) {
>          qemuAuditNetDevice(def, net, res_ifname, true);
>          VIR_FREE(net->ifname);
> @@ -149,7 +149,9 @@ qemuPhysIfaceConnect(virDomainDefPtr def
>              if (err) {
>                  VIR_FORCE_CLOSE(rc);
>                  delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
> -                           &net->data.direct.virtPortProfile);
> +                           net->data.direct.mode,
> +                           &net->data.direct.virtPortProfile,
> +                           driver->stateDir);
>                  VIR_FREE(net->ifname);
>              }
>          }
> Index: libvirt/src/qemu/qemu_process.c
> ===================================================================
> --- libvirt.orig/src/qemu/qemu_process.c
> +++ libvirt/src/qemu/qemu_process.c
> @@ -2876,7 +2876,8 @@ void qemuProcessStop(struct qemud_driver
>          virDomainNetDefPtr net = def->nets[i];
>          if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
>              delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
> -                       &net->data.direct.virtPortProfile);
> +                       net->data.direct.mode,
> +                       &net->data.direct.virtPortProfile, driver->stateDir);
>              VIR_FREE(net->ifname);
>          }
>      }
> Index: libvirt/src/util/macvtap.c
> ===================================================================
> --- libvirt.orig/src/util/macvtap.c
> +++ libvirt/src/util/macvtap.c
> @@ -545,6 +545,106 @@ configMacvtapTap(int tapfd, int vnet_hdr
>      return 0;
>  }
>  
> +/**
> + * replaceMacAdress:
> + * @macaddress: new MAC address for interface
> + * @linkdev: name of interface
> + * @stateDir: directory to store old MAC address
> + *
> + * Returns 0 on success, -1 in case of fatal error, error code otherwise.
> + *
> + */
> +static int
> +replaceMacAdress(const unsigned char *macaddress,
> +                 const char *linkdev,
> +                 char *stateDir)
> +{
> +    unsigned char oldmac[6];
> +    int rc;
> +
> +    rc = ifaceGetMacaddr(linkdev, oldmac);
> +
> +    if (rc) {
> +        virReportSystemError(rc,
> +                             _("Getting MAC address from '%s' "
> +                               "to '%02x:%02x:%02x:%02x:%02x:%02x' failed."),
> +                             linkdev,
> +                             oldmac[0], oldmac[1], oldmac[2],
> +                             oldmac[3], oldmac[4], oldmac[5]);
> +    } else {
> +        char *path = NULL;
> +
> +        char macstr[VIR_MAC_STRING_BUFLEN];
> +        if (virAsprintf(&path, "%s/%s",
> +                        stateDir,
> +                        linkdev) < 0) {
> +            virReportOOMError();
> +            return errno;
> +        }
> +        virFormatMacAddr(oldmac, macstr);
> +        if (virFileWriteStr(path, macstr, O_CREAT|O_TRUNC|O_WRONLY) < 0) {
> +            virReportSystemError(errno, _("Unable to preserve mac for %s"),
> +                                 linkdev);
> +            return errno;
> +        }
> +    }
> +
> +    rc = ifaceSetMacaddr(linkdev, macaddress);
> +    if (rc) {
> +        virReportSystemError(errno,
> +                             _("Setting MAC address on  '%s' to "
> +                               "'%02x:%02x:%02x:%02x:%02x:%02x' failed."),
> +                             linkdev,
> +                             macaddress[0], macaddress[1], macaddress[2],
> +                             macaddress[3], macaddress[4], macaddress[5]);
> +    }
> +    return rc;
> +}
> +
> +/**
> + * restoreMacAddress:
> + * @linkdev: name of interface
> + * @stateDir: directory containing old MAC address
> + *
> + * Returns 0 on success, -1 in case of fatal error, error code otherwise.
> + *
> + */
> +static int
> +restoreMacAddress(const char *linkdev,
> +                  char *stateDir)
> +{
> +    int ret;
> +    char *oldmacname = NULL;
> +    char *macstr = NULL;
> +    char *path = NULL;
> +    unsigned char oldmac[6];
> +
> +    if (virAsprintf(&path, "%s/%s",
> +                    stateDir,
> +                    linkdev) < 0) {
> +        virReportOOMError();
> +        return -1;
> +    }
> +
> +    if (virFileReadAll(path, VIR_MAC_STRING_BUFLEN, &macstr) < 0) {
> +        virReportSystemError(errno, _("Unable to preserve mac for %s"),
> +                             linkdev);
> +        return errno;
> +    }

The virFileReadAll method already reports error messages.

> +
> +    if (virParseMacAddr(macstr, &oldmac[0]) != 0) {
> +        virReportSystemError(VIR_ERR_INTERNAL_ERROR,
> +                             _("Cannot parse MAC address from '%s'"),
> +                             oldmacname);
> +        return -1;
> +    }
> +
> +    /*reset mac and remove file-ignore results*/
> +    ret = ifaceSetMacaddr(linkdev, oldmac);
> +    ret = unlink(path);

To ignore results you can instead do

    ignore_value(ifaceSetMacaddr(linkdev, oldmac));
    ignore_value(unlink(path));

> +    VIR_FREE(macstr);
> +    return 0;
> +}
>  
>  /**
>   * openMacvtapTap:
> @@ -575,7 +675,8 @@ openMacvtapTap(const char *tgifname,
>                 const unsigned char *vmuuid,
>                 virVirtualPortProfileParamsPtr virtPortProfile,
>                 char **res_ifname,
> -               enum virVMOperationType vmOp)
> +               enum virVMOperationType vmOp,
> +               char *stateDir)
>  {
>      const char *type = "macvtap";
>      int c, rc;
> @@ -589,6 +690,21 @@ openMacvtapTap(const char *tgifname,
>  
>      VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virVMOperationTypeToString(vmOp));
>  
> +    /** Note: When using PASSTHROUGH mode with MACVTAP devices the link
> +     * device's MAC address must be set to the VMs MAC address. In
> +     * order to not confuse the first switch or bridge in line this MAC
> +     * address must be reset when the VM is shut down.
> +     * This is especially important when using SRIOV capable cards that
> +     * emulate their switch in firmware.
> +     */
> +    if (mode == VIR_DOMAIN_NETDEV_MACVTAP_MODE_PASSTHRU) {
> +        if (replaceMacAdress(macaddress, linkdev, stateDir) != 0) {
> +            virReportSystemError(errno,
> +                                 _("cannot replace MAC address on $s"),
> +                                 linkdev);

Missing a 'return -1' here, and 'replaceMacAddress already reports
an error message, so no need todo so again.

> +        }
> +    }
> +
>      if (tgifname) {
>          if(ifaceGetIndex(false, tgifname, &ifindex) == 0) {
>              if (STRPREFIX(tgifname,
> @@ -684,8 +800,18 @@ void
>  delMacvtap(const char *ifname,
>             const unsigned char *macaddr,
>             const char *linkdev,
> -           virVirtualPortProfileParamsPtr virtPortProfile)
> +           int mode,
> +           virVirtualPortProfileParamsPtr virtPortProfile,
> +           char *stateDir)
>  {
> +    if (mode == VIR_DOMAIN_NETDEV_MACVTAP_MODE_PASSTHRU) {
> +        if (restoreMacAddress(linkdev, stateDir) != 0) {
> +            virReportSystemError(errno,
> +                                 _("cannot restore MAC address on $s"),
> +                                 linkdev);
> +        }
> +    }

Again, restoreMacAddress already reports an error message. No
neeed for a 'return -1' here, since we need to make a best
effort to run the next bit of code too.


> Index: libvirt/src/util/interface.c
> ===================================================================
> --- libvirt.orig/src/util/interface.c
> +++ libvirt/src/util/interface.c
> @@ -390,3 +390,102 @@ ifaceGetVlanID(const char *vlanifname AT
>      return ENOSYS;
>  }
>  #endif /* __linux__ */
> +
> +/**
> + * ifaceGetMacaddr:
> + * @ifname: interface name to set MTU for
> + * @macaddr: MAC address (VIR_MAC_BUFLEN in size)
> + *
> + * This function gets the @macaddr for a given interface @ifname.
> + *
> + * Returns 0 in case of success or an errno code in case of failure.
> + */
> +#ifdef __linux__
> +int
> +ifaceGetMacaddr(const char *ifname,
> +                unsigned char *macaddr)
> +{
> +    struct ifreq ifr;
> +    int fd;
> +
> +    if (!ifname)
> +        return EINVAL;
> +
> +    fd = socket(AF_INET, SOCK_STREAM, 0);
> +    if(fd < 0){
> +        return errno;
> +    }

Normal coding style for libvirt is to have a space
before & after the () on an 'if' condition

> +
> +    memset(&ifr, 0, sizeof(struct ifreq));
> +    if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL)
> +        return EINVAL;
> +
> +    if(ioctl(fd, SIOCGIFHWADDR, (char *)&ifr) != 0){

Again here.

> +        return errno;
> +    }
> +
> +    memcpy(macaddr, ifr.ifr_ifru.ifru_hwaddr.sa_data, VIR_MAC_BUFLEN);
> +
> +    return 0;
> +}
> +
> +#else
> +
> +int
> +ifaceGetMacaddr(const char *ifname,
> +                unsigned char *macaddr)

This needs  ATTRIBUTE_UNUSED on both params to avoid a warning

> +{
> +    return ENOSYS;
> +}
> +
> +#endif /* __linux__ */
> +
> +/**
> + * ifaceSetMacaddr:
> + * @ifname: interface name to set MTU for
> + * @macaddr: MAC address (VIR_MAC_BUFLEN in size)
> + *
> + * This function sets the @macaddr for a given interface @ifname. This
> + * gets rid of the kernel's automatically assigned random MAC.
> + *
> + * Returns 0 in case of success or an errno code in case of failure.
> + */
> +#ifdef __linux__
> +int
> +ifaceSetMacaddr(const char *ifname,
> +                const unsigned char *macaddr)
> +{
> +    struct ifreq ifr;
> +    int fd;
> +
> +    if (!ifname)
> +        return EINVAL;
> +
> +    fd = socket(AF_INET, SOCK_STREAM, 0);
> +    if(fd < 0){
> +        return errno;

Same style item

> +    }
> +
> +    memset(&ifr, 0, sizeof(struct ifreq));
> +    if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL)
> +        return EINVAL;
> +
> +    /* To fill ifr.ifr_hdaddr.sa_family field */
> +    if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
> +        return errno;
> +
> +    memcpy(ifr.ifr_hwaddr.sa_data, macaddr, VIR_MAC_BUFLEN);
> +
> +    return ioctl(fd, SIOCSIFHWADDR, &ifr) == 0 ? 0 : errno;
> +}
> +
> +#else
> +
> +int
> +ifaceSetMacaddr(const char *ifname,
> +                const unsigned char *macaddr)

And 'ATTRIBUTE_UNUSED' on these too

> +{
> +    return ENOSYS;
> +}
> +
> +#endif /* __linux__ */
> Index: libvirt/src/util/interface.h
> ===================================================================
> --- libvirt.orig/src/util/interface.h
> +++ libvirt/src/util/interface.h
> @@ -32,4 +32,8 @@ int ifaceGetIndex(bool reportError, cons
>  
>  int ifaceGetVlanID(const char *vlanifname, int *vlanid);
>  
> +int ifaceSetMacaddr(const char *ifname, const unsigned char *macaddr);
> +
> +int ifaceGetMacaddr(const char *ifname, unsigned char *macaddr);
> +
>  #endif /* __VIR_INTERFACE_H__ */
> Index: libvirt/src/libvirt_private.syms
> ===================================================================
> --- libvirt.orig/src/libvirt_private.syms
> +++ libvirt/src/libvirt_private.syms
> @@ -507,7 +507,9 @@ ifaceCheck;
>  ifaceCtrl;
>  ifaceGetFlags;
>  ifaceGetIndex;
> +ifaceGetMacaddr;
>  ifaceGetVlanID;
> +ifaceSetMacaddr;
>  ifaceIsUp;
>  
> ===================================================================


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list