libvirt/src/util/virnetdevbridge.h
Laine Stump 13ec827052 util: set bridge device MAC address explicitly during virNetDevBridgeCreate
When libvirt first implemented a stable and configurable MAC address
for the bridges created for libvirt virtual networks (commit
5754dbd56d, in libvirt v0.8.8) most distro stable releases didn't
support explicitly setting the MAC address of a bridge; the bridge
just always assumed the lowest numbered MAC of all attached
interfaces. Because of this, we stabilized the bridge MAC address by
creating a "dummy" tap interface with a MAC address guaranteed to be
lower than any of the guest tap devices' MACs (which all started with
0xFE, so it's not difficult to do) and attached it to the bridge -
this was the inception of the "virbr0-nic" device that has confused so
many people over the years.

Even though the linux kernel had recently gained support for
explicitly setting a bridge MAC, we deemed it unnecessary to set the
MAC that way, because the other (indirect) method worked everywhere.

But recently there have been reports that the bridge MAC address was
not following the setting in the network config, and mismatched the
MAC of the dummy tap device (which was still correct). It turns out
that this is due to a change in systemd-242 that persists whatever MAC
address is set for a bridge when it's initially started. According to
the systemd NEWS file entry for version 242
(https://github.com/systemd/systemd/blob/master/NEWS):

  "if a bridge interface is created without any slaves, and gains
   a slave later, then now the bridge does not inherit slave's MAC."

This change was the result of:

  https://github.com/systemd/systemd/issues/3374

(apparently if there is no MAC saved for a bridge by the name of a
bridge being created, the random MAC generated during creation is
saved, and then that same MAC is used to explicitly set the MAC each
time it is created). Once a bridge has an explicitly set MAC, the "use
the lowest numbered MAC of attached devices" rule is ignored, so our
dummy tap device is like the goggles - it does nothing! (well, almost).

We could whine about changes in default behavior, etc. etc., but
because the change was in response to actual user problems, that seems
likely a fruitless task. Fortunately, time has marched on, and even
distro releases that are old enough that they are no longer supported
by upstream libvirt (e.g. RHEL6) have support for explicitly setting a
bridge device MAC address, either during creation or with a separate
ioctl after creation, so we can now do that.

To enable explicitly setting the mac during bridge creation, we add a
mac arg to virNetDevBridgeCreate().  In the case of platforms where
the bridge is created with a netlink RTM_NEWLINK message, we just add
that mac to the message. For platforms that still use an ioctl (either
SIOCBRADDBR or SIOCIFCREATE2), we make a separate call to
virNetDevSetMAC() after creating the bridge.

(NB: I was unable to test the calling of virNetDevSetMAC() from the
SIOCIFCREATE2 (BSD) version of virNetDevBridgeCreate(); even though I
managed to get a FreeBSD system setup and libvirt built there, when I
tried to start the default network the SIOCIFCREATE2 ioctl itself
failed, so it never even got to the virNetDevSetMAC(). That leaves the
FreeBSD implementation untested.)

This makes the dummy tap pointless for purposes of setting the MAC
address, but it is still useful for IPv6 DAD initialization (which
apparently requires at least one interface to be attached to the
bridge and online), as well as for setting an initial MTU for the
bridge, so it hasn't been removed.

(NB: we can safely *always* call virNetDevBridgeCreate() with
&def->mac from the network driver because, in spite of the existence
of a "mac_specified" bool in the config suggesting that it may not
always be present, in reality a mac address will always be added to
any network that doesn't have one - this is guaranteed in all cases by
commit a47ae7c004)

https://bugzilla.redhat.com/show_bug.cgi?id=1760851
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
2019-11-07 22:09:19 -05:00

92 lines
3.9 KiB
C

/*
* Copyright (C) 2007-2012, 2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "internal.h"
#include "virmacaddr.h"
int virNetDevBridgeCreate(const char *brname,
const virMacAddr *mac)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeDelete(const char *brname)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeAddPort(const char *brname,
const char *ifname)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeRemovePort(const char *brname,
const char *ifname)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeSetSTPDelay(const char *brname,
int delay)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeGetSTPDelay(const char *brname,
int *delay)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeSetSTP(const char *brname,
bool enable)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeGetSTP(const char *brname,
bool *enable)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeSetVlanFiltering(const char *brname,
bool enable)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeGetVlanFiltering(const char *brname,
bool *enable)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgePortGetLearning(const char *brname,
const char *ifname,
bool *enable)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgePortSetLearning(const char *brname,
const char *ifname,
bool enable)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgePortGetUnicastFlood(const char *brname,
const char *ifname,
bool *enable)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgePortSetUnicastFlood(const char *brname,
const char *ifname,
bool enable)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
typedef enum {
VIR_NETDEVBRIDGE_FDB_FLAG_ROUTER = (1 << 0),
VIR_NETDEVBRIDGE_FDB_FLAG_SELF = (1 << 1),
VIR_NETDEVBRIDGE_FDB_FLAG_MASTER = (1 << 2),
VIR_NETDEVBRIDGE_FDB_FLAG_PERMANENT = (1 << 3),
VIR_NETDEVBRIDGE_FDB_FLAG_TEMP = (1 << 4),
} virNetDevBridgeFDBFlags;
int virNetDevBridgeFDBAdd(const virMacAddr *mac, const char *ifname,
unsigned int flags)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virNetDevBridgeFDBDel(const virMacAddr *mac, const char *ifname,
unsigned int flags)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;