From: Michal Privoznik <mprivozn@redhat.com>
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 <mprivozn@redhat.com>
---
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.
+ *
+ * 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);
+
+ ret = prefix;
+ cleanup:
+ freeaddrinfo(ai);
+ return ret;
+}
+
/**
* virSocketAddrIsNumericLocalhost:
* @address: address to check
diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h
index c7ad3250e0..dc6373793b 100644
--- a/src/util/virsocketaddr.h
+++ b/src/util/virsocketaddr.h
@@ -135,6 +135,8 @@ bool virSocketAddrIsWildcard(const virSocketAddr *addr);
int virSocketAddrNumericFamily(const char *address);
+int virSocketAddrSubnetToPrefix(const char *subnet);
+
bool virSocketAddrIsNumericLocalhost(const char *addr);
int virSocketAddrPTRDomain(const virSocketAddr *addr,
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;
+ return 0;
+}
+
static int
mymain(void)
{
@@ -352,6 +367,14 @@ mymain(void)
ret = -1; \
} while (0)
+#define DO_TEST_SUBNET_TO_PREFIX(addr, prefix) \
+ do { \
+ struct testSubnetToPrefixData data = { addr, prefix }; \
+ if (virTestRun("Test subnet to prefix " addr, \
+ testSubnetToPrefixHelper, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_UNSPEC, true);
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_INET, true);
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_INET6, false);
@@ -476,6 +499,12 @@ mymain(void)
DO_TEST_LOCALHOST("hello", false);
DO_TEST_LOCALHOST("fe80::1:1", false);
+ DO_TEST_SUBNET_TO_PREFIX("0.0.0.0", 0);
+ DO_TEST_SUBNET_TO_PREFIX("255.0.0.0", 8);
+ DO_TEST_SUBNET_TO_PREFIX("255.255.255.254", 31);
+ DO_TEST_SUBNET_TO_PREFIX("64", 64);
+ DO_TEST_SUBNET_TO_PREFIX("/64", 64);
+
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
2.52.0
On 2/6/26 7:52 AM, Michal Privoznik via Devel wrote:
> From: Michal Privoznik <mprivozn@redhat.com>
>
> 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
Just FYI, this comment doesn't seem to match the implementation or the
tests. You have a test defined for 255.0.0.0 where the expected return
value is 8, which makes sense.
> input string is already a prefix (with optional leading slash
> character) just return that number parsed.
>
> Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
> ---
> 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.
> + *
> + * 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);
> +
> + ret = prefix;
> + cleanup:
> + freeaddrinfo(ai);
> + return ret;
> +}
> +
> /**
> * virSocketAddrIsNumericLocalhost:
> * @address: address to check
> diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h
> index c7ad3250e0..dc6373793b 100644
> --- a/src/util/virsocketaddr.h
> +++ b/src/util/virsocketaddr.h
> @@ -135,6 +135,8 @@ bool virSocketAddrIsWildcard(const virSocketAddr *addr);
>
> int virSocketAddrNumericFamily(const char *address);
>
> +int virSocketAddrSubnetToPrefix(const char *subnet);
> +
> bool virSocketAddrIsNumericLocalhost(const char *addr);
>
> int virSocketAddrPTRDomain(const virSocketAddr *addr,
> 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;
> + return 0;
> +}
> +
> static int
> mymain(void)
> {
> @@ -352,6 +367,14 @@ mymain(void)
> ret = -1; \
> } while (0)
>
> +#define DO_TEST_SUBNET_TO_PREFIX(addr, prefix) \
> + do { \
> + struct testSubnetToPrefixData data = { addr, prefix }; \
> + if (virTestRun("Test subnet to prefix " addr, \
> + testSubnetToPrefixHelper, &data) < 0) \
> + ret = -1; \
> + } while (0)
> +
> DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_UNSPEC, true);
> DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_INET, true);
> DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_INET6, false);
> @@ -476,6 +499,12 @@ mymain(void)
> DO_TEST_LOCALHOST("hello", false);
> DO_TEST_LOCALHOST("fe80::1:1", false);
>
> + DO_TEST_SUBNET_TO_PREFIX("0.0.0.0", 0);
> + DO_TEST_SUBNET_TO_PREFIX("255.0.0.0", 8);
> + DO_TEST_SUBNET_TO_PREFIX("255.255.255.254", 31);
> + DO_TEST_SUBNET_TO_PREFIX("64", 64);
> + DO_TEST_SUBNET_TO_PREFIX("/64", 64);
> +
> return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
> }
>
On 2/6/26 16:03, Jonathon Jongsma wrote: > On 2/6/26 7:52 AM, Michal Privoznik via Devel wrote: >> From: Michal Privoznik <mprivozn@redhat.com> >> >> 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 > > Just FYI, this comment doesn't seem to match the implementation or the > tests. You have a test defined for 255.0.0.0 where the expected return > value is 8, which makes sense. Ooops, yes. A typo. Fixed locally. Thanks! Michal
© 2016 - 2026 Red Hat, Inc.