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