diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8e844e9df4..09b01592ef 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -498,7 +498,9 @@ ifaceCheck; ifaceCtrl; ifaceGetFlags; ifaceGetIndex; +ifaceGetMacaddr; ifaceGetVlanID; +ifaceSetMacaddr; ifaceIsUp; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6346243320..e9ca1c9f93 100644 --- a/src/qemu/qemu_command.c +++ b/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); } } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index e805849760..a7f11ab436 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1610,7 +1610,9 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver, #if WITH_MACVTAP if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) { delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev, - &detach->data.direct.virtPortProfile); + detach->data.direct.mode, + &detach->data.direct.virtPortProfile, + driver->stateDir); VIR_FREE(detach->ifname); } #endif diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 8e09e52fad..b4411376a2 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2876,7 +2876,8 @@ void qemuProcessStop(struct qemud_driver *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); } } diff --git a/src/util/interface.c b/src/util/interface.c index 04a922c939..7651f1d1a6 100644 --- a/src/util/interface.c +++ b/src/util/interface.c @@ -390,3 +390,99 @@ ifaceGetVlanID(const char *vlanifname ATTRIBUTE_UNUSED, 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; + + memset(&ifr, 0, sizeof(struct ifreq)); + if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL) + return EINVAL; + + if (ioctl(fd, SIOCGIFHWADDR, (char *)&ifr) != 0) + return errno; + + memcpy(macaddr, ifr.ifr_ifru.ifru_hwaddr.sa_data, VIR_MAC_BUFLEN); + + return 0; +} + +#else + +int +ifaceGetMacaddr(const char *ifname ATTRIBUTE_UNUSED, + unsigned char *macaddr ATTRIBUTE_UNUSED) +{ + 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; + + 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 ATTRIBUTE_UNUSED, + const unsigned char *macaddr ATTRIBUTE_UNUSED) +{ + return ENOSYS; +} + +#endif /* __linux__ */ diff --git a/src/util/interface.h b/src/util/interface.h index 6470492041..fc0611b4d7 100644 --- a/src/util/interface.h +++ b/src/util/interface.h @@ -32,4 +32,8 @@ int ifaceGetIndex(bool reportError, const char *ifname, int *ifindex); 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__ */ diff --git a/src/util/macvtap.c b/src/util/macvtap.c index 068638e71b..48c7b0ebe7 100644 --- a/src/util/macvtap.c +++ b/src/util/macvtap.c @@ -545,6 +545,103 @@ 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) +{ + 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) { + return errno; + } + + if (virParseMacAddr(macstr, &oldmac[0]) != 0) { + macvtapError(VIR_ERR_INTERNAL_ERROR, + _("Cannot parse MAC address from '%s'"), + oldmacname); + return -1; + } + + /*reset mac and remove file-ignore results*/ + ignore_value(ifaceSetMacaddr(linkdev, oldmac)); + ignore_value(unlink(path)); + VIR_FREE(macstr); + return 0; +} /** * openMacvtapTap: @@ -575,7 +672,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 +687,19 @@ 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) { + return -1; + } + } + if (tgifname) { if(ifaceGetIndex(false, tgifname, &ifindex) == 0) { if (STRPREFIX(tgifname, @@ -684,8 +795,14 @@ 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) { + restoreMacAddress(linkdev, stateDir); + } + if (ifname) { vpDisassociatePortProfileId(ifname, macaddr, linkdev, diff --git a/src/util/macvtap.h b/src/util/macvtap.h index a1383c4545..2843596cd4 100644 --- a/src/util/macvtap.h +++ b/src/util/macvtap.h @@ -83,12 +83,15 @@ int openMacvtapTap(const char *ifname, const unsigned char *vmuuid, virVirtualPortProfileParamsPtr virtPortProfile, char **res_ifname, - enum virVMOperationType vmop); + enum virVMOperationType vmop, + char *stateDir); void delMacvtap(const char *ifname, const unsigned char *macaddress, const char *linkdev, - virVirtualPortProfileParamsPtr virtPortProfile); + int mode, + virVirtualPortProfileParamsPtr virtPortProfile, + char *stateDir); int vpAssociatePortProfileId(const char *macvtap_ifname, const unsigned char *macvtap_macaddr,