From: Michal Privoznik <mprivozn@redhat.com>
If a guest changes MAC address on its vNIC, then QEMU emits
NIC_RX_FILTER_CHANGED event (the event is emitted in other cases
too, but that's not important right now). Now, domain XML allows
users to chose whether to trust these events or not:
<interface trustGuestRxFilters='yes|no'/>
For the 'no' case no action is performed and the event is
ignored. But for the 'yes' case, some host side features of
corresponding vNIC (well tap/macvtap device) are tweaked to
reflect changed MAC address. But what is missing is reflecting
this new MAC address in domain XML.
Basically, what happens is: the host sees traffic with new MAC
address, all tools inside the guest see the new MAC address
(including 'virsh domifaddr --source agent') which makes it
harder to match device in the guest with the one in the domain
XML.
Therefore, report this new MAC address as another attribute of
the <mac/> element:
<mac address="52:54:00:a4:6f:91" guestAddress="00:11:22:33:44:55"/>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
---
docs/formatdomain.rst | 5 +++++
src/conf/domain_conf.c | 6 ++++++
src/conf/domain_conf.h | 3 +++
src/conf/schemas/domaincommon.rng | 5 +++++
src/qemu/qemu_domain.c | 32 +++++++++++++++++++++++++++++++
src/qemu/qemu_driver.c | 2 +-
6 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 4bc6a318f5..c82d158922 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -4966,6 +4966,11 @@ when it's in the reserved VMware range by adding a ``type="static"`` attribute
to the ``<mac/>`` element. Note that this attribute is useless if the provided
MAC address is outside of the reserved VMWare ranges.
+:since:`Since 11.2.0`, the ``<mac/>`` element can optionally contain
+``guestAddress`` attribute (output only), which contains new MAC address if the
+guest changed it. This is currently implemented only for QEMU/KVM and requires
+setting ``trustGuestRxFilters`` to ``yes``.
+
:since:`Since 7.3.0`, one can set the ACPI index against network interfaces.
With some operating systems (eg Linux with systemd), the ACPI index is used
to provide network interface device naming, that is stable across changes
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index dedcf76511..7c68fd6f2a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2840,6 +2840,7 @@ virDomainNetDefFree(virDomainNetDef *def)
if (!def)
return;
+ g_free(def->guestAddress);
g_free(def->modelstr);
switch (def->type) {
@@ -24586,6 +24587,11 @@ virDomainNetDefFormat(virBuffer *buf,
virBufferAsprintf(&macAttrBuf, " type='%s'", virDomainNetMacTypeTypeToString(def->mac_type));
if (def->mac_check != VIR_TRISTATE_BOOL_ABSENT)
virBufferAsprintf(&macAttrBuf, " check='%s'", virTristateBoolTypeToString(def->mac_check));
+ if (def->guestAddress &&
+ !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) {
+ virBufferAsprintf(&macAttrBuf, " guestAddress='%s'",
+ virMacAddrFormat(def->guestAddress, macstr));
+ }
virXMLFormatElement(buf, "mac", &macAttrBuf, NULL);
if (publicActual) {
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 3a97fd866c..3368921d6d 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1078,6 +1078,9 @@ struct _virDomainNetDef {
bool mac_generated; /* true if mac was *just now* auto-generated by libvirt */
virDomainNetMacType mac_type;
virTristateBool mac_check;
+ virMacAddr *guestAddress; /* MAC address from query-rx-filter (as reported
+ by guest). Not parsed from domain XML. Output
+ only. */
int model; /* virDomainNetModelType */
char *modelstr;
union {
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
index 3276569325..ad6b889a93 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -3837,6 +3837,11 @@
<ref name="virYesNo"/>
</attribute>
</optional>
+ <optional>
+ <attribute name="guestAddress">
+ <ref name="uniMacAddr"/>
+ </attribute>
+ </optional>
<empty/>
</element>
</optional>
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 29fac0034e..47ae59d408 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -11002,6 +11002,19 @@ syncNicRxFilterMulticast(char *ifname,
}
+/**
+ * qemuDomainSyncRxFilter:
+ * @vm: domain object
+ * @def: domain interface definition
+ * @asyncJob: async job type
+ *
+ * Fetch new state of RX Filter and set host side of the interface
+ * accordingly (e.g. reflect MAC address change on macvtap).
+ *
+ * Reflect changed MAC address in the domain definition.
+ *
+ * Returns: 0 on success, -1 on error.
+ */
int
qemuDomainSyncRxFilter(virDomainObj *vm,
virDomainNetDef *def,
@@ -11010,6 +11023,7 @@ qemuDomainSyncRxFilter(virDomainObj *vm,
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virNetDevRxFilter) guestFilter = NULL;
g_autoptr(virNetDevRxFilter) hostFilter = NULL;
+ virMacAddr *oldMac = NULL;
int rc;
if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
@@ -11055,6 +11069,24 @@ qemuDomainSyncRxFilter(virDomainObj *vm,
return -1;
}
+ if (def->guestAddress)
+ oldMac = def->guestAddress;
+ else
+ oldMac = &def->mac;
+
+ if (virMacAddrCmp(oldMac, &guestFilter->mac)) {
+ /* Reflect changed MAC address in the domain XML. */
+ if (virMacAddrCmp(&def->mac, &guestFilter->mac)) {
+ if (!def->guestAddress) {
+ def->guestAddress = g_new0(virMacAddr, 1);
+ }
+
+ virMacAddrSet(def->guestAddress, &guestFilter->mac);
+ } else {
+ VIR_FREE(def->guestAddress);
+ }
+ }
+
return 0;
}
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f0e9681161..a4866450fc 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3677,7 +3677,7 @@ processNicRxFilterChangedEvent(virDomainObj *vm,
"from domain %p %s",
devAlias, vm, vm->def->name);
- if (virDomainObjBeginJob(vm, VIR_JOB_QUERY) < 0)
+ if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
return;
if (!virDomainObjIsActive(vm)) {
--
2.48.1
On Tue, Mar 18, 2025 at 02:58:18PM +0100, Michal Privoznik via Devel wrote: > From: Michal Privoznik <mprivozn@redhat.com> > > If a guest changes MAC address on its vNIC, then QEMU emits > NIC_RX_FILTER_CHANGED event (the event is emitted in other cases > too, but that's not important right now). Now, domain XML allows > users to chose whether to trust these events or not: > > <interface trustGuestRxFilters='yes|no'/> > > For the 'no' case no action is performed and the event is > ignored. But for the 'yes' case, some host side features of > corresponding vNIC (well tap/macvtap device) are tweaked to > reflect changed MAC address. But what is missing is reflecting > this new MAC address in domain XML. > > Basically, what happens is: the host sees traffic with new MAC > address, all tools inside the guest see the new MAC address > (including 'virsh domifaddr --source agent') which makes it > harder to match device in the guest with the one in the domain > XML. > > Therefore, report this new MAC address as another attribute of > the <mac/> element: > > <mac address="52:54:00:a4:6f:91" guestAddress="00:11:22:33:44:55"/> IIUC, the "guestAddress" also influences things on the host - the macvtap device seems to get re-programmed with the MAC, so perhaps this is a slightly misleading name. How about 'activeAddress' / 'liveAddress' / 'currentAddress' With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
On Tue, Mar 18, 2025 at 02:58:18PM +0100, Michal Privoznik via Devel wrote:
> From: Michal Privoznik <mprivozn@redhat.com>
>
> If a guest changes MAC address on its vNIC, then QEMU emits
> NIC_RX_FILTER_CHANGED event (the event is emitted in other cases
> too, but that's not important right now). Now, domain XML allows
> users to chose whether to trust these events or not:
>
> <interface trustGuestRxFilters='yes|no'/>
>
> For the 'no' case no action is performed and the event is
> ignored. But for the 'yes' case, some host side features of
> corresponding vNIC (well tap/macvtap device) are tweaked to
> reflect changed MAC address. But what is missing is reflecting
> this new MAC address in domain XML.
>
> Basically, what happens is: the host sees traffic with new MAC
> address, all tools inside the guest see the new MAC address
> (including 'virsh domifaddr --source agent') which makes it
> harder to match device in the guest with the one in the domain
> XML.
>
> Therefore, report this new MAC address as another attribute of
> the <mac/> element:
>
> <mac address="52:54:00:a4:6f:91" guestAddress="00:11:22:33:44:55"/>
What happens when the guest OS reboots, or rather the
machine is reset ? Will the virtio-net device revert
back to its original configured MAC, or if the guest
MAC change persistent until QEMU is shut off.
If the former, we would need to be clearly guestAddress
at reset time.
I wonder a little whether 'address' has any purpose
at all if the guest MAC is changed ? ie should we
just be updating 'address' in-place, and letting
apps request "inactive" XML if they want the original
configured MAC ?
The 'address' is used by the NW filter code to apply
rules tied to guest MAC, which presumably need updating
if the guest changes its MAC
>
> Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
> Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
> ---
> docs/formatdomain.rst | 5 +++++
> src/conf/domain_conf.c | 6 ++++++
> src/conf/domain_conf.h | 3 +++
> src/conf/schemas/domaincommon.rng | 5 +++++
> src/qemu/qemu_domain.c | 32 +++++++++++++++++++++++++++++++
> src/qemu/qemu_driver.c | 2 +-
> 6 files changed, 52 insertions(+), 1 deletion(-)
>
> diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
> index 4bc6a318f5..c82d158922 100644
> --- a/docs/formatdomain.rst
> +++ b/docs/formatdomain.rst
> @@ -4966,6 +4966,11 @@ when it's in the reserved VMware range by adding a ``type="static"`` attribute
> to the ``<mac/>`` element. Note that this attribute is useless if the provided
> MAC address is outside of the reserved VMWare ranges.
>
> +:since:`Since 11.2.0`, the ``<mac/>`` element can optionally contain
> +``guestAddress`` attribute (output only), which contains new MAC address if the
> +guest changed it. This is currently implemented only for QEMU/KVM and requires
> +setting ``trustGuestRxFilters`` to ``yes``.
> +
> :since:`Since 7.3.0`, one can set the ACPI index against network interfaces.
> With some operating systems (eg Linux with systemd), the ACPI index is used
> to provide network interface device naming, that is stable across changes
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index dedcf76511..7c68fd6f2a 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -2840,6 +2840,7 @@ virDomainNetDefFree(virDomainNetDef *def)
> if (!def)
> return;
>
> + g_free(def->guestAddress);
> g_free(def->modelstr);
>
> switch (def->type) {
> @@ -24586,6 +24587,11 @@ virDomainNetDefFormat(virBuffer *buf,
> virBufferAsprintf(&macAttrBuf, " type='%s'", virDomainNetMacTypeTypeToString(def->mac_type));
> if (def->mac_check != VIR_TRISTATE_BOOL_ABSENT)
> virBufferAsprintf(&macAttrBuf, " check='%s'", virTristateBoolTypeToString(def->mac_check));
> + if (def->guestAddress &&
> + !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) {
> + virBufferAsprintf(&macAttrBuf, " guestAddress='%s'",
> + virMacAddrFormat(def->guestAddress, macstr));
> + }
> virXMLFormatElement(buf, "mac", &macAttrBuf, NULL);
>
> if (publicActual) {
> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
> index 3a97fd866c..3368921d6d 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -1078,6 +1078,9 @@ struct _virDomainNetDef {
> bool mac_generated; /* true if mac was *just now* auto-generated by libvirt */
> virDomainNetMacType mac_type;
> virTristateBool mac_check;
> + virMacAddr *guestAddress; /* MAC address from query-rx-filter (as reported
> + by guest). Not parsed from domain XML. Output
> + only. */
> int model; /* virDomainNetModelType */
> char *modelstr;
> union {
> diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
> index 3276569325..ad6b889a93 100644
> --- a/src/conf/schemas/domaincommon.rng
> +++ b/src/conf/schemas/domaincommon.rng
> @@ -3837,6 +3837,11 @@
> <ref name="virYesNo"/>
> </attribute>
> </optional>
> + <optional>
> + <attribute name="guestAddress">
> + <ref name="uniMacAddr"/>
> + </attribute>
> + </optional>
> <empty/>
> </element>
> </optional>
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index 29fac0034e..47ae59d408 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -11002,6 +11002,19 @@ syncNicRxFilterMulticast(char *ifname,
> }
>
>
> +/**
> + * qemuDomainSyncRxFilter:
> + * @vm: domain object
> + * @def: domain interface definition
> + * @asyncJob: async job type
> + *
> + * Fetch new state of RX Filter and set host side of the interface
> + * accordingly (e.g. reflect MAC address change on macvtap).
> + *
> + * Reflect changed MAC address in the domain definition.
> + *
> + * Returns: 0 on success, -1 on error.
> + */
> int
> qemuDomainSyncRxFilter(virDomainObj *vm,
> virDomainNetDef *def,
> @@ -11010,6 +11023,7 @@ qemuDomainSyncRxFilter(virDomainObj *vm,
> qemuDomainObjPrivate *priv = vm->privateData;
> g_autoptr(virNetDevRxFilter) guestFilter = NULL;
> g_autoptr(virNetDevRxFilter) hostFilter = NULL;
> + virMacAddr *oldMac = NULL;
> int rc;
>
> if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
> @@ -11055,6 +11069,24 @@ qemuDomainSyncRxFilter(virDomainObj *vm,
> return -1;
> }
>
> + if (def->guestAddress)
> + oldMac = def->guestAddress;
> + else
> + oldMac = &def->mac;
> +
> + if (virMacAddrCmp(oldMac, &guestFilter->mac)) {
> + /* Reflect changed MAC address in the domain XML. */
> + if (virMacAddrCmp(&def->mac, &guestFilter->mac)) {
> + if (!def->guestAddress) {
> + def->guestAddress = g_new0(virMacAddr, 1);
> + }
> +
> + virMacAddrSet(def->guestAddress, &guestFilter->mac);
> + } else {
> + VIR_FREE(def->guestAddress);
> + }
> + }
> +
> return 0;
> }
>
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index f0e9681161..a4866450fc 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -3677,7 +3677,7 @@ processNicRxFilterChangedEvent(virDomainObj *vm,
> "from domain %p %s",
> devAlias, vm, vm->def->name);
>
> - if (virDomainObjBeginJob(vm, VIR_JOB_QUERY) < 0)
> + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
> return;
>
> if (!virDomainObjIsActive(vm)) {
> --
> 2.48.1
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
On 3/18/25 15:10, Daniel P. Berrangé wrote: > On Tue, Mar 18, 2025 at 02:58:18PM +0100, Michal Privoznik via Devel wrote: >> From: Michal Privoznik <mprivozn@redhat.com> >> >> If a guest changes MAC address on its vNIC, then QEMU emits >> NIC_RX_FILTER_CHANGED event (the event is emitted in other cases >> too, but that's not important right now). Now, domain XML allows >> users to chose whether to trust these events or not: >> >> <interface trustGuestRxFilters='yes|no'/> >> >> For the 'no' case no action is performed and the event is >> ignored. But for the 'yes' case, some host side features of >> corresponding vNIC (well tap/macvtap device) are tweaked to >> reflect changed MAC address. But what is missing is reflecting >> this new MAC address in domain XML. >> >> Basically, what happens is: the host sees traffic with new MAC >> address, all tools inside the guest see the new MAC address >> (including 'virsh domifaddr --source agent') which makes it >> harder to match device in the guest with the one in the domain >> XML. >> >> Therefore, report this new MAC address as another attribute of >> the <mac/> element: >> >> <mac address="52:54:00:a4:6f:91" guestAddress="00:11:22:33:44:55"/> > > What happens when the guest OS reboots, or rather the > machine is reset ? Will the virtio-net device revert > back to its original configured MAC, or if the guest > MAC change persistent until QEMU is shut off. In my testing, when the domain is reset then the MAC address is changed back. > > If the former, we would need to be clearly guestAddress > at reset time. Yes, and this is handled properly, because as of v8.9.0-rc1~27 the MAC address is refreshed more often. > > I wonder a little whether 'address' has any purpose > at all if the guest MAC is changed ? ie should we > just be updating 'address' in-place, and letting > apps request "inactive" XML if they want the original > configured MAC ? > > The 'address' is used by the NW filter code to apply > rules tied to guest MAC, which presumably need updating > if the guest changes its MAC And so does TC rules when QoS is in place. Updating MAC address in place is what I initially had in my RFC patches but Marting proposed adding new attribute instead. https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/U274MIGIYRGFTPCAPG237JH3P2EGCWIG/#IWFCKHLWHAOIJPJGYKJE7X666FODUM53 Michal
On Tue, Mar 18, 2025 at 03:31:30PM +0100, Michal Prívozník wrote: > On 3/18/25 15:10, Daniel P. Berrangé wrote: > > On Tue, Mar 18, 2025 at 02:58:18PM +0100, Michal Privoznik via Devel wrote: > >> From: Michal Privoznik <mprivozn@redhat.com> > >> > >> If a guest changes MAC address on its vNIC, then QEMU emits > >> NIC_RX_FILTER_CHANGED event (the event is emitted in other cases > >> too, but that's not important right now). Now, domain XML allows > >> users to chose whether to trust these events or not: > >> > >> <interface trustGuestRxFilters='yes|no'/> > >> > >> For the 'no' case no action is performed and the event is > >> ignored. But for the 'yes' case, some host side features of > >> corresponding vNIC (well tap/macvtap device) are tweaked to > >> reflect changed MAC address. But what is missing is reflecting > >> this new MAC address in domain XML. > >> > >> Basically, what happens is: the host sees traffic with new MAC > >> address, all tools inside the guest see the new MAC address > >> (including 'virsh domifaddr --source agent') which makes it > >> harder to match device in the guest with the one in the domain > >> XML. > >> > >> Therefore, report this new MAC address as another attribute of > >> the <mac/> element: > >> > >> <mac address="52:54:00:a4:6f:91" guestAddress="00:11:22:33:44:55"/> > > > > What happens when the guest OS reboots, or rather the > > machine is reset ? Will the virtio-net device revert > > back to its original configured MAC, or if the guest > > MAC change persistent until QEMU is shut off. > > In my testing, when the domain is reset then the MAC address is changed > back. > > > > > If the former, we would need to be clearly guestAddress > > at reset time. > > Yes, and this is handled properly, because as of v8.9.0-rc1~27 the MAC > address is refreshed more often. > > > > > I wonder a little whether 'address' has any purpose > > at all if the guest MAC is changed ? ie should we > > just be updating 'address' in-place, and letting > > apps request "inactive" XML if they want the original > > configured MAC ? > > > > The 'address' is used by the NW filter code to apply > > rules tied to guest MAC, which presumably need updating > > if the guest changes its MAC > > And so does TC rules when QoS is in place. > Updating MAC address in place is what I initially had in my RFC patches > but Marting proposed adding new attribute instead. Hmmm, so we avoid confusing existing apps that don't expect MAC to ever change, which I guess I can see as a use case. It also gives apps an indication of what the MAC will revert back to on machine reset, which is NOT guaranteed the same as what the inactive config contains. I find the latter compelling as a reason for reporting both > https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/U274MIGIYRGFTPCAPG237JH3P2EGCWIG/#IWFCKHLWHAOIJPJGYKJE7X666FODUM53 Heh, 2 years old, no wonder I couldn't quickly find it :-) With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
From: Michal Privoznik <mprivozn@redhat.com>
The aim off this event is to notify management application that
guest changed MAC address on one of its vNICs so the app can
update its internal records, e.g. for finding match between
guest/host view of vNICs.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
---
examples/c/misc/event-test.c | 14 +++++
include/libvirt/libvirt-domain.h | 28 +++++++++
src/conf/domain_event.c | 93 +++++++++++++++++++++++++++++
src/conf/domain_event.h | 12 ++++
src/libvirt_private.syms | 2 +
src/remote/remote_daemon_dispatch.c | 32 ++++++++++
src/remote/remote_driver.c | 34 +++++++++++
src/remote/remote_protocol.x | 17 +++++-
src/remote_protocol-structs | 8 +++
tools/virsh-domain-event.c | 20 +++++++
10 files changed, 259 insertions(+), 1 deletion(-)
diff --git a/examples/c/misc/event-test.c b/examples/c/misc/event-test.c
index 88d99dff56..a61dbf4529 100644
--- a/examples/c/misc/event-test.c
+++ b/examples/c/misc/event-test.c
@@ -1102,6 +1102,19 @@ myNetworkEventMetadataChangeCallback(virConnectPtr conn G_GNUC_UNUSED,
}
+static int
+myDomainEventNICMACChangeCallback(virConnectPtr conn G_GNUC_UNUSED,
+ virDomainPtr dom,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC,
+ void *opaque G_GNUC_UNUSED)
+{
+ printf("%s EVENT: Domain %s(%d) NIC MAC changed: alias: '%s' oldMAC: '%s' newMAC: '%s'\n",
+ __func__, virDomainGetName(dom), virDomainGetID(dom), alias, oldMAC, newMAC);
+ return 0;
+}
+
static void
myFreeFunc(void *opaque)
@@ -1160,6 +1173,7 @@ struct domainEventData domainEvents[] = {
DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback),
DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE, myDomainEventMemoryFailureCallback),
DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MEMORY_DEVICE_SIZE_CHANGE, myDomainEventMemoryDeviceSizeChangeCallback),
+ DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE, myDomainEventNICMACChangeCallback),
};
struct storagePoolEventData {
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 8c86bd8f94..c4f8baea77 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -6979,6 +6979,33 @@ typedef void (*virConnectDomainEventMemoryDeviceSizeChangeCallback)(virConnectPt
void *opaque);
+/**
+ * virConnectDomainEventNICMACChangeCallback:
+ * @conn: connection object
+ * @dom: domain on which the event occurred
+ * @alias: network interface device alias
+ * @oldMAC: the old value of network interface MAC address
+ * @newMAC: the new value of network interface MAC address
+ * @opaque: application specified data
+ *
+ * The callback occurs when the guest changes MAC address on one of
+ * its virtual network interfaces, for QEMU domains this is emitted
+ * only for vNICs of model virtio. The event is not emitted for
+ * other types (e.g. PCI device passthrough).
+ *
+ * The callback signature to use when registering for an event of
+ * type VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE with
+ * virConnectDomainEventRegisterAny().
+ *
+ * Since: 11.2.0
+ */
+typedef void (*virConnectDomainEventNICMACChangeCallback)(virConnectPtr conn,
+ virDomainPtr dom,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC,
+ void *opaque);
+
/**
* VIR_DOMAIN_EVENT_CALLBACK:
*
@@ -7027,6 +7054,7 @@ typedef enum {
VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD = 24, /* virConnectDomainEventBlockThresholdCallback (Since: 3.2.0) */
VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE = 25, /* virConnectDomainEventMemoryFailureCallback (Since: 6.9.0) */
VIR_DOMAIN_EVENT_ID_MEMORY_DEVICE_SIZE_CHANGE = 26, /* virConnectDomainEventMemoryDeviceSizeChangeCallback (Since: 7.9.0) */
+ VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE = 27, /* virConnectDomainEventNICMACChangeCallback (Since: 11.2.0) */
# ifdef VIR_ENUM_SENTINELS
VIR_DOMAIN_EVENT_ID_LAST
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c
index 09f3368064..88087bad4f 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -57,6 +57,7 @@ static virClass *virDomainEventMetadataChangeClass;
static virClass *virDomainEventBlockThresholdClass;
static virClass *virDomainEventMemoryFailureClass;
static virClass *virDomainEventMemoryDeviceSizeChangeClass;
+static virClass *virDomainEventNICMACChangeClass;
static void virDomainEventDispose(void *obj);
static void virDomainEventLifecycleDispose(void *obj);
@@ -81,6 +82,7 @@ static void virDomainEventMetadataChangeDispose(void *obj);
static void virDomainEventBlockThresholdDispose(void *obj);
static void virDomainEventMemoryFailureDispose(void *obj);
static void virDomainEventMemoryDeviceSizeChangeDispose(void *obj);
+static void virDomainEventNICMACChangeDispose(void *obj);
static void
virDomainEventDispatchDefaultFunc(virConnectPtr conn,
@@ -285,6 +287,15 @@ struct _virDomainEventMemoryDeviceSizeChange {
};
typedef struct _virDomainEventMemoryDeviceSizeChange virDomainEventMemoryDeviceSizeChange;
+struct _virDomainEventNICMACChange {
+ virDomainEvent parent;
+
+ char *alias;
+ char *oldMAC;
+ char *newMAC;
+};
+typedef struct _virDomainEventNICMACChange virDomainEventNICMACChange;
+
static int
virDomainEventsOnceInit(void)
{
@@ -334,6 +345,8 @@ virDomainEventsOnceInit(void)
return -1;
if (!VIR_CLASS_NEW(virDomainEventMemoryDeviceSizeChange, virDomainEventClass))
return -1;
+ if (!VIR_CLASS_NEW(virDomainEventNICMACChange, virDomainEventClass))
+ return -1;
return 0;
}
@@ -559,6 +572,16 @@ virDomainEventMemoryDeviceSizeChangeDispose(void *obj)
g_free(event->alias);
}
+static void
+virDomainEventNICMACChangeDispose(void *obj)
+{
+ virDomainEventNICMACChange *event = obj;
+
+ g_free(event->alias);
+ g_free(event->oldMAC);
+ g_free(event->newMAC);
+}
+
static void *
virDomainEventNew(virClass *klass,
int eventID,
@@ -1733,6 +1756,62 @@ virDomainEventMemoryDeviceSizeChangeNewFromDom(virDomainPtr dom,
}
+static virObjectEvent *
+virDomainEventNICMACChangeNew(int id,
+ const char *name,
+ unsigned char *uuid,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC)
+
+{
+ virDomainEventNICMACChange *ev;
+
+ if (virDomainEventsInitialize() < 0)
+ return NULL;
+
+ if (!(ev = virDomainEventNew(virDomainEventNICMACChangeClass,
+ VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE,
+ id, name, uuid)))
+ return NULL;
+
+ ev->alias = g_strdup(alias);
+ ev->oldMAC = g_strdup(oldMAC);
+ ev->newMAC = g_strdup(newMAC);
+
+ return (virObjectEvent *)ev;
+}
+
+
+virObjectEvent *
+virDomainEventNICMACChangeNewFromObj(virDomainObj *obj,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC)
+{
+ return virDomainEventNICMACChangeNew(obj->def->id,
+ obj->def->name,
+ obj->def->uuid,
+ alias,
+ oldMAC,
+ newMAC);
+}
+
+virObjectEvent *
+virDomainEventNICMACChangeNewFromDom(virDomainPtr dom,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC)
+{
+ return virDomainEventNICMACChangeNew(dom->id,
+ dom->name,
+ dom->uuid,
+ alias,
+ oldMAC,
+ newMAC);
+
+}
+
static void
virDomainEventDispatchDefaultFunc(virConnectPtr conn,
virObjectEvent *event,
@@ -2041,6 +2120,20 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn,
goto cleanup;
}
+ case VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE:
+ {
+ virDomainEventNICMACChange *nicMacChangeEvent;
+
+ nicMacChangeEvent = (virDomainEventNICMACChange *)event;
+ ((virConnectDomainEventNICMACChangeCallback)cb)(conn, dom,
+ nicMacChangeEvent->alias,
+ nicMacChangeEvent->oldMAC,
+ nicMacChangeEvent->newMAC,
+ cbopaque);
+
+ goto cleanup;
+ }
+
case VIR_DOMAIN_EVENT_ID_LAST:
break;
}
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h
index f4016dc1e9..f31cfb9e42 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -277,6 +277,18 @@ virDomainEventMemoryDeviceSizeChangeNewFromDom(virDomainPtr dom,
const char *alias,
unsigned long long size);
+virObjectEvent *
+virDomainEventNICMACChangeNewFromObj(virDomainObj *obj,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC);
+
+virObjectEvent *
+virDomainEventNICMACChangeNewFromDom(virDomainPtr dom,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC);
+
int
virDomainEventStateRegister(virConnectPtr conn,
virObjectEventState *state,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e78abdad15..10e7346b94 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -771,6 +771,8 @@ virDomainEventMetadataChangeNewFromDom;
virDomainEventMetadataChangeNewFromObj;
virDomainEventMigrationIterationNewFromDom;
virDomainEventMigrationIterationNewFromObj;
+virDomainEventNICMACChangeNewFromDom;
+virDomainEventNICMACChangeNewFromObj;
virDomainEventPMSuspendDiskNewFromDom;
virDomainEventPMSuspendDiskNewFromObj;
virDomainEventPMSuspendNewFromDom;
diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c
index e812f5c3e9..5dfed7ceef 100644
--- a/src/remote/remote_daemon_dispatch.c
+++ b/src/remote/remote_daemon_dispatch.c
@@ -1323,6 +1323,37 @@ remoteRelayDomainEventMemoryDeviceSizeChange(virConnectPtr conn,
}
+static int
+remoteRelayDomainEventNICMACChange(virConnectPtr conn,
+ virDomainPtr dom,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC,
+ void *opaque)
+{
+ daemonClientEventCallback *callback = opaque;
+ remote_domain_event_nic_mac_change_msg data;
+
+ if (callback->callbackID < 0 ||
+ !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+ return -1;
+
+ /* build return data */
+ memset(&data, 0, sizeof(data));
+ data.callbackID = callback->callbackID;
+ data.alias = g_strdup(alias);
+ data.oldMAC = g_strdup(oldMAC);
+ data.newMAC = g_strdup(newMAC);
+ make_nonnull_domain(&data.dom, dom);
+
+ remoteDispatchObjectEventSend(callback->client, remoteProgram,
+ REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE,
+ (xdrproc_t)xdr_remote_domain_event_nic_mac_change_msg,
+ &data);
+ return 0;
+}
+
+
static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot),
@@ -1351,6 +1382,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMemoryFailure),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMemoryDeviceSizeChange),
+ VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventNICMACChange),
};
G_STATIC_ASSERT(G_N_ELEMENTS(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 307f9ca945..90545c0cc1 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -432,6 +432,11 @@ remoteConnectNotifyEventConnectionClosed(virNetClientProgram *prog G_GNUC_UNUSED
virNetClient *client G_GNUC_UNUSED,
void *evdata, void *opaque);
+static void
+remoteDomainBuildEventNICMACChange(virNetClientProgram *prog,
+ virNetClient *client,
+ void *evdata, void *opaque);
+
static virNetClientProgramEvent remoteEvents[] = {
{ REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE,
remoteDomainBuildEventLifecycle,
@@ -650,6 +655,10 @@ static virNetClientProgramEvent remoteEvents[] = {
remoteDomainBuildEventMemoryDeviceSizeChange,
sizeof(remote_domain_event_memory_device_size_change_msg),
(xdrproc_t)xdr_remote_domain_event_memory_device_size_change_msg },
+ { REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE,
+ remoteDomainBuildEventNICMACChange,
+ sizeof(remote_domain_event_nic_mac_change_msg),
+ (xdrproc_t)xdr_remote_domain_event_nic_mac_change_msg },
};
static void
@@ -5068,6 +5077,31 @@ remoteDomainBuildEventMemoryDeviceSizeChange(virNetClientProgram *prog G_GNUC_UN
}
+static void
+remoteDomainBuildEventNICMACChange(virNetClientProgram *prog G_GNUC_UNUSED,
+ virNetClient *client G_GNUC_UNUSED,
+ void *evdata, void *opaque)
+{
+ virConnectPtr conn = opaque;
+ remote_domain_event_nic_mac_change_msg *msg = evdata;
+ struct private_data *priv = conn->privateData;
+ virDomainPtr dom;
+ virObjectEvent *event = NULL;
+
+ if (!(dom = get_nonnull_domain(conn, msg->dom)))
+ return;
+
+ event = virDomainEventNICMACChangeNewFromDom(dom,
+ msg->alias,
+ msg->oldMAC,
+ msg->newMAC);
+
+ virObjectUnref(dom);
+
+ virObjectEventStateQueueRemote(priv->eventState, event, msg->callbackID);
+}
+
+
static int
remoteStreamSend(virStreamPtr st,
const char *data,
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 41c045ff78..b18ac43f7f 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3973,6 +3973,15 @@ struct remote_domain_fd_associate_args {
remote_nonnull_string name;
unsigned int flags;
};
+
+struct remote_domain_event_nic_mac_change_msg {
+ int callbackID;
+ remote_nonnull_domain dom;
+ remote_nonnull_string alias;
+ remote_nonnull_string oldMAC;
+ remote_nonnull_string newMAC;
+};
+
/*----- Protocol. -----*/
/* Define the program number, protocol version and procedure numbers here. */
@@ -7048,5 +7057,11 @@ enum remote_procedure {
* @generate: both
* @acl: domain:write
*/
- REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448
+ REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448,
+
+ /**
+ * @generate: both
+ * @acl: none
+ */
+ REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 449
};
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index ed7e2fbcb0..52fa4c4567 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -3306,6 +3306,13 @@ struct remote_domain_fd_associate_args {
remote_nonnull_string name;
u_int flags;
};
+struct remote_domain_event_nic_mac_change_msg {
+ int callbackID;
+ remote_nonnull_domain dom;
+ remote_nonnull_string alias;
+ remote_nonnull_string oldMAC;
+ remote_nonnull_string newMAC;
+};
enum remote_procedure {
REMOTE_PROC_CONNECT_OPEN = 1,
REMOTE_PROC_CONNECT_CLOSE = 2,
@@ -3755,4 +3762,5 @@ enum remote_procedure {
REMOTE_PROC_NETWORK_EVENT_CALLBACK_METADATA_CHANGE = 446,
REMOTE_PROC_NODE_DEVICE_UPDATE = 447,
REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448,
+ REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 449,
};
diff --git a/tools/virsh-domain-event.c b/tools/virsh-domain-event.c
index cd33d4d938..69a68d857d 100644
--- a/tools/virsh-domain-event.c
+++ b/tools/virsh-domain-event.c
@@ -783,6 +783,24 @@ virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED,
}
+static void
+virshEventNICMACChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+ virDomainPtr dom,
+ const char *alias,
+ const char *oldMAC,
+ const char *newMAC,
+ void *opaque)
+{
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+ virBufferAsprintf(&buf,
+ _("event 'nic-mac-change' for domain '%1$s':\nalias: %2$s\noldMAC: %3$s\nnewMAC: %4$s\n"),
+ virDomainGetName(dom), alias, oldMAC, newMAC);
+
+ virshEventPrint(opaque, &buf);
+}
+
+
virshDomainEventCallback virshDomainEventCallbacks[] = {
{ "lifecycle",
VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
@@ -836,6 +854,8 @@ virshDomainEventCallback virshDomainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), },
{ "memory-device-size-change",
VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), },
+ { "nic-mac-change",
+ VIR_DOMAIN_EVENT_CALLBACK(virshEventNICMACChangePrint), },
};
G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks));
--
2.48.1
From: Michal Privoznik <mprivozn@redhat.com>
So far, we only process NIC_RX_FILTER_CHANGED event when the
corresponding device has 'trustGuestRxFilters' enabled. And the
event is emitted only for virtio model. IOW, this is fairly
limited situation and other scenarios don't emit any event (e.g.
change of MAC address on a PCI passthrough device).
Resolves: https://issues.redhat.com/browse/RHEL-7035
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
---
src/qemu/qemu_domain.c | 16 +++++++++++++++-
src/qemu/qemu_domain.h | 3 ++-
src/qemu/qemu_driver.c | 9 ++++++---
src/qemu/qemu_process.c | 2 +-
4 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 47ae59d408..e3a5d08f4b 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -11018,7 +11018,8 @@ syncNicRxFilterMulticast(char *ifname,
int
qemuDomainSyncRxFilter(virDomainObj *vm,
virDomainNetDef *def,
- virDomainAsyncJob asyncJob)
+ virDomainAsyncJob asyncJob,
+ virObjectEvent **event)
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virNetDevRxFilter) guestFilter = NULL;
@@ -11075,6 +11076,19 @@ qemuDomainSyncRxFilter(virDomainObj *vm,
oldMac = &def->mac;
if (virMacAddrCmp(oldMac, &guestFilter->mac)) {
+ if (event) {
+ char oldMACStr[VIR_MAC_STRING_BUFLEN] = { 0 };
+ char newMACStr[VIR_MAC_STRING_BUFLEN] = { 0 };
+
+ virMacAddrFormat(oldMac, oldMACStr);
+ virMacAddrFormat(&guestFilter->mac, newMACStr);
+
+ *event = virDomainEventNICMACChangeNewFromObj(vm,
+ def->info.alias,
+ oldMACStr,
+ newMACStr);
+ }
+
/* Reflect changed MAC address in the domain XML. */
if (virMacAddrCmp(&def->mac, &guestFilter->mac)) {
if (!def->guestAddress) {
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 8e53a270a7..f3cff49e96 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -1128,7 +1128,8 @@ qemuDomainRefreshStatsSchema(virDomainObj *dom);
int
qemuDomainSyncRxFilter(virDomainObj *vm,
virDomainNetDef *def,
- virDomainAsyncJob asyncJob);
+ virDomainAsyncJob asyncJob,
+ virObjectEvent **event);
int
qemuDomainSchedCoreStart(virQEMUDriverConfig *cfg,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index a4866450fc..ab1e63a46a 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3667,9 +3667,11 @@ processNetdevStreamDisconnectedEvent(virDomainObj *vm,
static void
-processNicRxFilterChangedEvent(virDomainObj *vm,
+processNicRxFilterChangedEvent(virQEMUDriver *driver,
+ virDomainObj *vm,
const char *devAlias)
{
+ virObjectEvent *event = NULL;
virDomainDeviceDef dev;
virDomainNetDef *def;
@@ -3714,11 +3716,12 @@ processNicRxFilterChangedEvent(virDomainObj *vm,
VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network "
"device %s in domain %s", def->info.alias, vm->def->name);
- if (qemuDomainSyncRxFilter(vm, def, VIR_ASYNC_JOB_NONE) < 0)
+ if (qemuDomainSyncRxFilter(vm, def, VIR_ASYNC_JOB_NONE, &event) < 0)
goto endjob;
endjob:
virDomainObjEndJob(vm);
+ virObjectEventStateQueue(driver->domainEventState, event);
}
@@ -4062,7 +4065,7 @@ static void qemuProcessEventHandler(void *data, void *opaque)
processNetdevStreamDisconnectedEvent(vm, processEvent->data);
break;
case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED:
- processNicRxFilterChangedEvent(vm, processEvent->data);
+ processNicRxFilterChangedEvent(driver, vm, processEvent->data);
break;
case QEMU_PROCESS_EVENT_SERIAL_CHANGED:
processSerialChangedEvent(driver, vm, processEvent->data,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 0173fbe3be..1d7579509a 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -8199,7 +8199,7 @@ qemuProcessRefreshRxFilters(virDomainObj *vm,
continue;
}
- if (qemuDomainSyncRxFilter(vm, def, asyncJob) < 0)
+ if (qemuDomainSyncRxFilter(vm, def, asyncJob, NULL) < 0)
return -1;
}
--
2.48.1
© 2016 - 2025 Red Hat, Inc.