set and restore MAC address of a NIC when using PASSTHROUGH mode

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
This commit is contained in:
Dirk Herrendoerfer 2011-06-21 11:49:40 -04:00 committed by Stefan Berger
parent 7268cb9274
commit cbd8227ee0
8 changed files with 235 additions and 8 deletions

View File

@ -498,7 +498,9 @@ ifaceCheck;
ifaceCtrl;
ifaceGetFlags;
ifaceGetIndex;
ifaceGetMacaddr;
ifaceGetVlanID;
ifaceSetMacaddr;
ifaceIsUp;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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,

View File

@ -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,