On 2/10/26 18:40, Peter Krempa wrote:
> On Fri, Feb 06, 2026 at 14:52:37 +0100, Michal Privoznik via Devel wrote:
>> From: Michal Privoznik <[email protected]>
>>
>> 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 <[email protected]>
>> ---
>>
>> Admittedly, the 'virsh domifaddr' output is misaligned after this, but
>> I'll post a separate patch for that.
> 
> Disclaimer: I have absolutely no idea about the hyperv specific bits.
> 
> 
>>
>> 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);
> 
> 'query' is unused after this.

It's not.

> 
> 
>> +
>> +    if (hypervGetWmiClass(Msvm_GuestNetworkAdapterConfiguration, &aConfig) 
>> < 0)

This ^^ is actually a macro:

#define hypervGetWmiClass(type, class) \
    hypervGetWmiClassList(priv, type ## _WmiInfo, &query, (hypervObject
**)class)


>> +        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 this fails ....
> 
>> +
>> +        if (oneIface)
>> +            VIR_APPEND_ELEMENT(*ifaces, nifaces, oneIface);
> 
> 
> ... after this was partially filled, the caller has no way of figuring
> out how many entries to free. This function should cleanup after itself
> internally.
> 

Ah, okay. Will post v2.

> 
>> +
>> +        entry = entry->next;
>> +    }
>> +
>> +    return nifaces;
>> +}
>> +
>> +

Michal

Reply via email to