ifaceCtrl;
ifaceGetFlags;
ifaceGetIndex;
+ifaceGetMacaddr;
ifaceGetVlanID;
+ifaceSetMacaddr;
ifaceIsUp;
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);
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);
}
}
#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
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);
}
}
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__ */
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__ */
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:
const unsigned char *vmuuid,
virVirtualPortProfileParamsPtr virtPortProfile,
char **res_ifname,
- enum virVMOperationType vmOp)
+ enum virVMOperationType vmOp,
+ char *stateDir)
{
const char *type = "macvtap";
int c, rc;
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,
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,
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,