[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