From: Michal Privoznik <mprivozn@redhat.com>
The virDomainInterfaceAddresses() API accepts @source argument,
but since this is hyperv, we can't really use _SRC_LEASE (we
didn't spawn any dnsmasq there), not _SRC_ARP. The only source
that's more or less usable is _SRC_AGENT. Okay, there's no QEMU
guest agent running, but hyperv has its own guest agent. In my
testing (with Linux guest) I had to install 'hyperv' package and
then enable 'hv_kvp_daemon.service'. After that,
Msvm_GuestNetworkAdapterConfiguration struct [1] contained guest
IP addresses.
There's one caveat though: the interface name
(virDomainInterface::name). We don't fetch that one even for
hypervDomainGetXMLDesc() case. And there's no <target dev=''/>
either nor device alias (v12.0.0-43-g4009126f17). So just put
InstanceID there for now, which is this long path, with some
UUIDs, e.g.:
Microsoft:5C58E5F2-946E-490F-B81D-6E2A7328640D\C85554E0-2B3B-487C-A557-D230BFF5F9E6\
But hey, at least it's unique.
1: https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-guestnetworkadapterconfiguration
Resolves: https://issues.redhat.com/browse/RHEL-145306
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
---
Admittedly, the 'virsh domifaddr' output is misaligned after this, but
I'll post a separate patch for that.
src/hyperv/hyperv_driver.c | 169 ++++++++++++++++++++++++++
src/hyperv/hyperv_wmi_generator.input | 12 ++
2 files changed, 181 insertions(+)
diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c
index fbc76544df..c25cb91c13 100644
--- a/src/hyperv/hyperv_driver.c
+++ b/src/hyperv/hyperv_driver.c
@@ -3654,6 +3654,174 @@ hypervDomainSendKey(virDomainPtr domain, unsigned int codeset,
}
+static int
+hypervDomainInterfaceAddressesParseOne(hypervPrivate *priv,
+ Msvm_EthernetPortAllocationSettingData *net,
+ virDomainInterfacePtr *oneIfaceRet)
+{
+ g_autoptr(virDomainInterface) iface = NULL;
+ g_autoptr(Msvm_SyntheticEthernetPortSettingData) sepsd = NULL;
+ g_autoptr(Msvm_GuestNetworkAdapterConfiguration) aConfig = NULL;
+ g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
+ virMacAddr macAddr = { 0 };
+ char macAddrStr[VIR_MAC_STRING_BUFLEN] = { 0 };
+
+ VIR_DEBUG("Parsing ethernet adapter '%s'", net->data->InstanceID);
+
+ iface = g_new0(virDomainInterface, 1);
+ iface->name = g_strdup(net->data->InstanceID);
+
+ if (hypervDomainDefParseEthernetAdapterMAC(priv, net, &macAddr) < 0)
+ return -1;
+
+ iface->hwaddr = g_strdup(virMacAddrFormat(&macAddr, macAddrStr));
+
+ virBufferAsprintf(&query,
+ "ASSOCIATORS OF {%s} "
+ "WHERE AssocClass=Msvm_SettingDataComponent "
+ "ResultClass=Msvm_GuestNetworkAdapterConfiguration",
+ net->data->Parent);
+
+ if (hypervGetWmiClass(Msvm_GuestNetworkAdapterConfiguration, &aConfig) < 0)
+ return -1;
+
+ if (aConfig) {
+ size_t nAddr = aConfig->data->IPAddresses.count;
+ size_t i;
+
+ if (aConfig->data->Subnets.count != nAddr) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("the number of IP addresses (%1$zu) does not match the number of subnets (%2$d)"),
+ nAddr, aConfig->data->Subnets.count);
+ return -1;
+ }
+
+ iface->addrs = g_new0(virDomainIPAddress, nAddr);
+ iface->naddrs = nAddr;
+ for (i = 0; i < nAddr; i++) {
+ const char *ipAddrStr = ((const char **) aConfig->data->IPAddresses.data)[i];
+ const char *subnetAddrStr = ((const char **) aConfig->data->Subnets.data)[i];
+ virDomainIPAddressPtr ip = &iface->addrs[i];
+ int family;
+ int prefix;
+
+ VIR_DEBUG("ipAddrStr='%s' subnetAddrStr='%s'",
+ ipAddrStr, subnetAddrStr);
+
+ ip->addr = g_strdup(ipAddrStr);
+ family = virSocketAddrNumericFamily(ipAddrStr);
+ if (family == AF_INET6) {
+ ip->type = VIR_IP_ADDR_TYPE_IPV6;
+ } else if (family == AF_INET) {
+ ip->type = VIR_IP_ADDR_TYPE_IPV4;
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown IP address family of '%1$s'"),
+ ipAddrStr);
+ return -1;
+ }
+
+ prefix = virSocketAddrSubnetToPrefix(subnetAddrStr);
+ if (prefix < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected subnet mask '%1$s'"),
+ subnetAddrStr);
+ return -1;
+ }
+ ip->prefix = prefix;
+ }
+ }
+
+ *oneIfaceRet = g_steal_pointer(&iface);
+ return 0;
+}
+
+
+static ssize_t
+hypervDomainInterfaceAddressesParseList(hypervPrivate *priv,
+ Msvm_EthernetPortAllocationSettingData *nets,
+ virDomainInterfacePtr **ifaces)
+{
+ Msvm_EthernetPortAllocationSettingData *entry = nets;
+ size_t nifaces = 0;
+
+ while (entry) {
+ virDomainInterfacePtr oneIface = NULL;
+
+ if (hypervDomainInterfaceAddressesParseOne(priv, entry, &oneIface) < 0)
+ return -1;
+
+ if (oneIface)
+ VIR_APPEND_ELEMENT(*ifaces, nifaces, oneIface);
+
+ entry = entry->next;
+ }
+
+ return nifaces;
+}
+
+
+static int
+hypervDomainInterfaceAddresses(virDomainPtr dom,
+ virDomainInterfacePtr **ifaces,
+ unsigned int source,
+ unsigned int flags)
+{
+ hypervPrivate *priv = NULL;
+ char uuid_string[VIR_UUID_STRING_BUFLEN];
+ g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
+ g_autoptr(Msvm_VirtualSystemSettingData) virtualSystemSettingData = NULL;
+ g_autoptr(Msvm_EthernetPortAllocationSettingData) nets = NULL;
+ virDomainInterfacePtr *ifacesRet = NULL;
+ ssize_t ifacesRetCount = 0;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (source != VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
+ _("Unknown IP address data source %1$d"),
+ source);
+ return -1;
+ }
+
+ if (hypervMsvmComputerSystemFromDomain(dom, &computerSystem) < 0)
+ return -1;
+
+ priv = dom->conn->privateData;
+ virUUIDFormat(dom->uuid, uuid_string);
+
+ if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv,
+ uuid_string,
+ &virtualSystemSettingData) < 0) {
+ return -1;
+ }
+
+ if (hypervGetEthernetPortAllocationSD(priv,
+ virtualSystemSettingData->data->InstanceID,
+ &nets) < 0) {
+ return -1;
+ }
+
+ ifacesRetCount = hypervDomainInterfaceAddressesParseList(priv, nets, &ifacesRet);
+ if (ifacesRetCount < 0)
+ goto cleanup;
+
+ *ifaces = g_steal_pointer(&ifacesRet);
+ ret = ifacesRetCount;
+
+ cleanup:
+ if (ret < 0) {
+ while (ifacesRetCount > 0) {
+ virDomainInterfaceFree(ifacesRet[--ifacesRetCount]);
+ }
+ VIR_FREE(ifacesRet);
+ }
+
+ return ret;
+}
+
+
static virHypervisorDriver hypervHypervisorDriver = {
.name = "Hyper-V",
.connectOpen = hypervConnectOpen, /* 0.9.5 */
@@ -3717,6 +3885,7 @@ static virHypervisorDriver hypervHypervisorDriver = {
.domainManagedSaveRemove = hypervDomainManagedSaveRemove, /* 0.9.5 */
.domainSendKey = hypervDomainSendKey, /* 3.6.0 */
.connectIsAlive = hypervConnectIsAlive, /* 0.9.8 */
+ .domainInterfaceAddresses = hypervDomainInterfaceAddresses, /* 12.1.0 */
};
diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input
index 0b342cbfa6..e6053267f8 100644
--- a/src/hyperv/hyperv_wmi_generator.input
+++ b/src/hyperv/hyperv_wmi_generator.input
@@ -866,6 +866,18 @@ class Msvm_SyntheticEthernetPortSettingData
end
+class Msvm_GuestNetworkAdapterConfiguration
+ string InstanceID
+ uint16 ProtocolIFType
+ boolean DHCPEnabled
+ string IPAddresses[]
+ string Subnets[]
+ string DefaultGateways[]
+ string DNSServers[]
+ uint16 IPAddressOrigins[]
+end
+
+
class Msvm_EthernetPortAllocationSettingData
string InstanceID
string Caption
--
2.52.0
© 2016 - 2026 Red Hat, Inc.