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; }