]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
virsocketaddr: Introduce virSocketAddrSubnetToPrefix()
authorMichal Privoznik <mprivozn@redhat.com>
Fri, 6 Feb 2026 11:52:40 +0000 (12:52 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Thu, 12 Feb 2026 14:55:30 +0000 (15:55 +0100)
The aim of this helper is to convert subnet mask to prefix. For
instance for input "255.0.0.0" to return 8. 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>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
src/libvirt_private.syms
src/util/virsocketaddr.c
src/util/virsocketaddr.h
tests/sockettest.c

index e84b2d0e595001a53e43a9ce66ee580616220cf0..f8bc7c1a1449d9f1f71a48be4bf49f43c11c4145 100644 (file)
@@ -3461,6 +3461,7 @@ virSocketAddrSetIPv4AddrNetOrder;
 virSocketAddrSetIPv6Addr;
 virSocketAddrSetIPv6AddrNetOrder;
 virSocketAddrSetPort;
+virSocketAddrSubnetToPrefix;
 
 
 # util/virstoragefile.h
index 1f203fb50d704876af3f87134e7d765aa6a7e65b..25dad516f6c73626c420cc1dcf080a183ef47719 100644 (file)
@@ -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,58 @@ 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. There is a corner case: if @subnet is
+ * valid IPv4 address but not valid subnet mask then a positive
+ * value is returned, but obviously it is not valid prefix.
+ *
+ * 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
index c7ad3250e0103e96a9536ccacc6d2b5279dda0cc..dc6373793b0e71d4a9789635965db589f9e272ee 100644 (file)
@@ -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,
index 5cb8a9fb72ba3bdc8a3867f1e8e15f1ca385c895..a8621a2234d6a44243f7810ce8f1651d64a5b130 100644 (file)
@@ -257,6 +257,25 @@ testIsLocalhostHelper(const void *opaque)
     return 0;
 }
 
+struct testSubnetToPrefixData {
+    const char *addr;
+    int prefix;
+};
+
+static int
+testSubnetToPrefixHelper(const void *opaque)
+{
+    const struct testSubnetToPrefixData *data = opaque;
+    int val = virSocketAddrSubnetToPrefix(data->addr);
+
+    if (val != data->prefix) {
+        fprintf(stderr, "actual: '%d', expected: '%d'", val, data->prefix);
+        return -1;
+    }
+
+    return 0;
+}
+
 static int
 mymain(void)
 {
@@ -352,6 +371,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 +503,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;
 }