This fixes https://bugzilla.redhat.com/show_bug.cgi?id=609463 The problem was that, since a bridge always acquires the MAC address of the connected interface with the numerically lowest MAC, as guests are started and stopped, it was possible for the MAC address to change over time, and this change in the network was being detected by Windows 7 (it sees the MAC of the default route change), so on each reboot it would bring up a dialog box asking about this "new network". The solution is to create a dummy tap interface with a MAC guaranteed to be lower than any guest interface's MAC, and attach that tap to the bridge as soon as it's created. Since all guest MAC addresses start with 0xFE, we can just generate a MAC with the standard "0x52, 0x54, 0" prefix, and it's guaranteed to always win (physical interfaces are never connected to these bridges, so we don't need to worry about competing numerically with them). Note that the dummy tap is never set to IFF_UP state - that's not necessary in order for the bridge to take its MAC, and not setting it to UP eliminates the clutter of having an (eg) "virbr0-mac" displayed in the output of the ifconfig command. Problem that needs a solution: ----------------------------- I chose to not auto-generate the MAC address in the network XML parser, as there are likely to be consumers of that API that don't need or want to have a MAC address associated with the bridge. Instead, in bridge_driver.c when the network is being brought up, if there is no MAC, I generate one then. One down side of this is that the MAC is written to the *statedir* xml file (in /var/lib/libvirt/networks) but no to the *config* xml (in /etc/libvirt/qemu/networks). That means that if libvirtd is restarted while the network is down, the next time it's started it will have a different MAC. It looks like the only place the *config* xml is written is in networkDefine or networkCreate, but that's inadequate for this case, as anyone upgrading their libvirt will want this change to take effect for all their already-defined networks. DV suggested that we could add a flag to the parser telling it whether or not to auto-generate a MAC when one wasn't specified. That would require (at least) adding a new arg to: virNetworkDefParseFile virNetworkDefParse virNetworkDefParseNode virNetworkDefParseXML and adding this arg would be embedding details of the XML attributes into the C API arglist, which doesn't seem very clean - this could set a very bad precedent that would lead to argument clutter (one of the things that using XML helps to prevent). Also, it would be one more arg to be potentially set incorrectly by some new user of the parser. Any advice on the cleanest way to solve this problem? --- src/conf/network_conf.c | 21 ++++++++++++++++++- src/conf/network_conf.h | 3 ++ src/libvirt_private.syms | 1 + src/network/bridge_driver.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 28a3ee8..592e38c 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -628,6 +628,19 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0) def->delay = 0; + tmp = virXPathString("string(./bridge[1]/@mac)", ctxt); + if (tmp) { + if (virParseMacAddr(tmp, def->mac) < 0) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("Invalid bridge mac address '%s' in network '%s'"), + tmp, def->name); + VIR_FREE(tmp); + goto error; + } + VIR_FREE(tmp); + def->mac_specified = true; + } + nIps = virXPathNodeSet("./ip", ctxt, &ipNodes); if (nIps > 0) { int ii; @@ -853,9 +866,15 @@ char *virNetworkDefFormat(const virNetworkDefPtr def) virBufferAddLit(&buf, " <bridge"); if (def->bridge) virBufferEscapeString(&buf, " name='%s'", def->bridge); - virBufferVSprintf(&buf, " stp='%s' delay='%ld' />\n", + virBufferVSprintf(&buf, " stp='%s' delay='%ld'", def->stp ? "on" : "off", def->delay); + if (def->mac_specified) { + char macaddr[VIR_MAC_STRING_BUFLEN]; + virFormatMacAddr(def->mac, macaddr); + virBufferVSprintf(&buf, " mac='%s'", macaddr); + } + virBufferAddLit(&buf, " />\n"); if (def->domain) virBufferVSprintf(&buf, " <domain name='%s'/>\n", def->domain); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index fd96c36..1bde4ac 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -31,6 +31,7 @@ # include "internal.h" # include "threads.h" # include "network.h" +# include "util.h" /* 2 possible types of forwarding */ enum virNetworkForwardType { @@ -92,6 +93,8 @@ struct _virNetworkDef { char *domain; unsigned long delay; /* Bridge forward delay (ms) */ unsigned int stp :1; /* Spanning tree protocol */ + unsigned char mac[VIR_MAC_BUFLEN]; /* mac address of bridge device */ + bool mac_specified; int forwardType; /* One of virNetworkForwardType constants */ char *forwardDev; /* Destination device for forwarding */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 88e270c..68e42d5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -874,6 +874,7 @@ virFileWriteStr; virFindFileInPath; virFork; virFormatMacAddr; +virGenerateMacAddr; virGetGroupID; virGetHostname; virGetUserDirectory; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 08aaa36..1952dfd 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1566,6 +1566,7 @@ networkStartNetworkDaemon(struct network_driver *driver, bool v4present = false, v6present = false; virErrorPtr save_err = NULL; virNetworkIpDefPtr ipdef; + char *macTapIfName; if (virNetworkObjIsActive(network)) { networkReportError(VIR_ERR_OPERATION_INVALID, @@ -1585,6 +1586,27 @@ networkStartNetworkDaemon(struct network_driver *driver, return -1; } + virAsprintf(&macTapIfName, "%s-mac", network->def->bridge); + if (!macTapIfName) { + virReportOOMError(); + goto err0; + } + if (!network->def->mac_specified) { + virGenerateMacAddr((unsigned char[]){ 0x52, 0x54, 0 }, + network->def->mac); + network->def->mac_specified = true; + } + if ((err = brAddTap(driver->brctl, network->def->bridge, + &macTapIfName, network->def->mac, 0, false, NULL))) { + virReportSystemError(err, + _("cannot create dummy tap device '%s' to set mac address on bridge '%s'"), + macTapIfName, network->def->bridge); + VIR_FREE(macTapIfName); + goto err0; + } + + VIR_FREE(macTapIfName); + /* Set bridge options */ if ((err = brSetForwardDelay(driver->brctl, network->def->bridge, network->def->delay))) { @@ -1695,6 +1717,17 @@ networkStartNetworkDaemon(struct network_driver *driver, err1: if (!save_err) save_err = virSaveLastError(); + + if ((err = brDeleteTap(driver->brctl, macTapIfName))) { + char ebuf[1024]; + VIR_WARN("Failed to delete dummy tap device '%s' on bridge '%s' : %s", + macTapIfName, network->def->bridge, + virStrerror(err, ebuf, sizeof ebuf)); + } + + err0: + if (!save_err) + save_err = virSaveLastError(); if ((err = brDeleteBridge(driver->brctl, network->def->bridge))) { char ebuf[1024]; VIR_WARN("Failed to delete bridge '%s' : %s", @@ -1714,6 +1747,7 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, { int err; char *stateFile; + char *macTapIfName; VIR_INFO(_("Shutting down network '%s'"), network->def->name); @@ -1744,6 +1778,19 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, kill(network->dnsmasqPid, SIGTERM); char ebuf[1024]; + + virAsprintf(&macTapIfName, "%s-mac", network->def->bridge); + if (!macTapIfName) { + virReportOOMError(); + } else { + if ((err = brDeleteTap(driver->brctl, macTapIfName))) { + VIR_WARN("Failed to delete dummy tap device '%s' on bridge '%s' : %s", + macTapIfName, network->def->bridge, + virStrerror(err, ebuf, sizeof ebuf)); + } + VIR_FREE(macTapIfName); + } + if ((err = brSetInterfaceUp(driver->brctl, network->def->bridge, 0))) { VIR_WARN("Failed to bring down bridge '%s' : %s", network->def->bridge, virStrerror(err, ebuf, sizeof ebuf)); -- 1.7.3.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list