On Fri, Feb 06, 2026 at 14:52:35 +0100, Michal Privoznik via Devel wrote:
> From: Michal Privoznik <[email protected]>
> 
> The aim of this helper is to convert subnet mask to prefix. For
> instance for input "255.0.0.0" to return 0. Additionally, if the
> input string is already a prefix (with optional leading slash
> character) just return that number parsed.
> 
> Signed-off-by: Michal Privoznik <[email protected]>
> ---
>  src/libvirt_private.syms |  1 +
>  src/util/virsocketaddr.c | 51 ++++++++++++++++++++++++++++++++++++++++
>  src/util/virsocketaddr.h |  2 ++
>  tests/sockettest.c       | 29 +++++++++++++++++++++++
>  4 files changed, 83 insertions(+)
> 
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index d81b30f0b6..035eccf70a 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -3461,6 +3461,7 @@ virSocketAddrSetIPv4AddrNetOrder;
>  virSocketAddrSetIPv6Addr;
>  virSocketAddrSetIPv6AddrNetOrder;
>  virSocketAddrSetPort;
> +virSocketAddrSubnetToPrefix;
>  
>  
>  # util/virstoragefile.h
> diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c
> index 1f203fb50d..89d41d7656 100644
> --- a/src/util/virsocketaddr.c
> +++ b/src/util/virsocketaddr.c
> @@ -21,6 +21,7 @@
>  #include "virsocketaddr.h"
>  #include "virerror.h"
>  #include "virbuffer.h"
> +#include "virstring.h"
>  
>  #define VIR_FROM_THIS VIR_FROM_NONE
>  
> @@ -1263,6 +1264,56 @@ virSocketAddrNumericFamily(const char *address)
>      return family;
>  }
>  
> +/**
> + * virSocketAddrSubnetToPrefix:
> + * @subnet: address to convert
> + *
> + * Converts subnet mask to prefix. If @subnet is of an IPv4
> + * format (NNN.NNN.NNN.NNN) then corresponding prefix length is
> + * returned (i.e. number of leading bits.). If @subnet is just a
> + * number (optionally prefixed with '/') then the number is
> + * parsed and returned.

This doesn't say any caveats if e.g. the IPv4 string you pass in isn't a
subnet mask at all ...

> + * Returns: prefix corresponding to @subnet,
> + *          -1 otherwise.
> + */
> +int
> +virSocketAddrSubnetToPrefix(const char *subnet)
> +{
> +    struct addrinfo *ai = NULL;
> +    unsigned int prefix = 0;
> +    struct sockaddr_in in;
> +    int ret = -1;
> +
> +    if (*subnet == '/') {
> +        /* /NN format */
> +        if (virStrToLong_ui(subnet + 1, NULL, 10, &prefix) < 0)
> +            return -1;
> +        return prefix;
> +    }
> +
> +    if (virStrToLong_ui(subnet, NULL, 10, &prefix) >= 0) {
> +        /* plain NN format */
> +        return prefix;
> +    }
> +
> +    if (virSocketAddrParseInternal(&ai, subnet, AF_INET, AI_NUMERICHOST, 
> false) < 0)
> +        return -1;
> +
> +    if (ai->ai_family != AF_INET) {
> +        /* huh? */
> +        goto cleanup;
> +    }
> +
> +    memcpy(&in, ai->ai_addr, sizeof(in));
> +    prefix = __builtin_popcount(in.sin_addr.s_addr);

... so if you don't pass a mask it gives not really documented answers.

With the impl above the following predicates of the test added later
are also true:

    DO_TEST_SUBNET_TO_PREFIX("192.168.0.1", 6);
    DO_TEST_SUBNET_TO_PREFIX("129.0.0.1", 3);


Depending on the expectations either document the expectation of what
happens argument isn't a subnet or add some checks

E.g. you could use __builtin_ffs to figure out the number of leading
bits and then compare with __builtin_popcount to see if other bits
aren't set.


> diff --git a/tests/sockettest.c b/tests/sockettest.c
> index 5cb8a9fb72..df62dc6f3b 100644
> --- a/tests/sockettest.c
> +++ b/tests/sockettest.c
> @@ -257,6 +257,21 @@ testIsLocalhostHelper(const void *opaque)
>      return 0;
>  }
>  
> +struct testSubnetToPrefixData {
> +    const char *addr;
> +    int prefix;
> +};
> +
> +static int
> +testSubnetToPrefixHelper(const void *opaque)
> +{
> +    const struct testSubnetToPrefixData *data = opaque;
> +
> +    if (virSocketAddrSubnetToPrefix(data->addr) != data->prefix)
> +        return -1;

Consider adding:

diff --git a/tests/sockettest.c b/tests/sockettest.c
index df62dc6f3b..37f654880a 100644
--- a/tests/sockettest.c
+++ b/tests/sockettest.c
@@ -266,9 +266,13 @@ static int
 testSubnetToPrefixHelper(const void *opaque)
 {
     const struct testSubnetToPrefixData *data = opaque;
+    int val = virSocketAddrSubnetToPrefix(data->addr);

-    if (virSocketAddrSubnetToPrefix(data->addr) != data->prefix)
+    if (val != data->prefix) {
+        fprintf(stderr, "actual: '%d', expected: '%d'", val, data->prefix);
         return -1;
+    }
+
     return 0;
 }

for debuggability

Reply via email to