pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/30986 )
Change subject: socket.h: Introduce API osmo_sockaddr_netmask_to_prefixlen() ......................................................................
socket.h: Introduce API osmo_sockaddr_netmask_to_prefixlen()
Implementation is imported from osmo-ggsn.git 97f60e3dca581797007524e0006ca9fafad59713 in46a_netmasklen() and adapter to work with an osmo_sockaddr.
This will be used by osmocom-bb's "modem" app.
Change-Id: I75e75e251c6776801fffdde745aebedf21c68799 --- M TODO-RELEASE M include/osmocom/core/socket.h M src/socket.c M tests/socket/socket_test.c 4 files changed, 118 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/86/30986/1
diff --git a/TODO-RELEASE b/TODO-RELEASE index 6ac8db8..86363e1 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -7,5 +7,5 @@ # If any interfaces have been added since the last public release: c:r:a + 1. # If any interfaces have been removed or changed since the last public release: c:r:0. #library what description / commit summary line -libosmocore new API osmo_sockaddr_is_any() +libosmocore new API osmo_sockaddr_is_any(), osmo_sockaddr_netmask_to_prefixlen() libosmocore ABI breakage OSMO_NUM_DLIB change affecting internal_cat[] diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index 06db5d0..3ed9cc6 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -50,6 +50,8 @@ int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os); int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len);
+int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *addr); + const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr); char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr); diff --git a/src/socket.c b/src/socket.c index 3d945e7..9ce3d1e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1291,6 +1291,63 @@ } }
+static unsigned int in6_addr_netmask_to_prefixlen(const struct in6_addr *netmask) +{ + #if defined(__linux__) + #define ADDRFIELD(i) s6_addr32[i] + #else + #define ADDRFIELD(i) __u6_addr.__u6_addr32[i] + #endif + + unsigned int i, j, prefix = 0; + + for (j = 0; j < 4; j++) { + uint32_t bits = netmask->ADDRFIELD(j); + uint8_t *b = (uint8_t*) &bits; + for (i = 0; i < 4; i++) { + while (b[i] & 0x80) { + prefix++; + b[i] = b[i] << 1; + } + } + } + + #undef ADDRFIELD + + return prefix; +} + +static unsigned int in_addr_netmask_to_prefixlen(const struct in_addr *netmask) +{ + uint32_t bits = netmask->s_addr; + uint8_t *b = (uint8_t*) &bits; + unsigned int i, prefix = 0; + + for (i = 0; i < 4; i++) { + while (b[i] & 0x80) { + prefix++; + b[i] = b[i] << 1; + } + } + return prefix; +} + +/*! Convert netmask to prefix length representation + * \param[in] netmask sockaddr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit) + * \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask), negative on error. + */ +int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *netmask) +{ + switch (netmask->u.sa.sa_family) { + case AF_INET6: + return in6_addr_netmask_to_prefixlen(&netmask->u.sin6.sin6_addr); + case AF_INET: + return in_addr_netmask_to_prefixlen(&netmask->u.sin.sin_addr); + default: + return -ENOTSUP; + } +} + /*! Initialize a unix domain socket (including bind/connect) * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP diff --git a/tests/socket/socket_test.c b/tests/socket/socket_test.c index d090fba..62a5e28 100644 --- a/tests/socket/socket_test.c +++ b/tests/socket/socket_test.c @@ -385,6 +385,63 @@ OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf))); }
+static void test_osa_netmask_prefixlen(void) +{ + struct osmo_sockaddr ipv4; + struct osmo_sockaddr ipv6; + int rc; + + ipv4.u.sin = (struct sockaddr_in){ + .sin_family = AF_INET, + }; + + ipv4.u.sin.sin_addr.s_addr = inet_addr("0.0.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 0); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.0.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 8); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 16); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 24); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.255"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 32); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("0.255.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + /* FIXME: This shows the implementation is not that robust chcking validity of input netmask: */ + OSMO_ASSERT(rc == 8); + + ipv6.u.sin6 = (struct sockaddr_in6){ + .sin6_family = AF_INET6, + }; + + inet_pton(AF_INET6, "fe::", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + OSMO_ASSERT(rc == 7); + + inet_pton(AF_INET6, "ff::", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + OSMO_ASSERT(rc == 8); + + inet_pton(AF_INET6, "ff:ff::", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + OSMO_ASSERT(rc == 16); + + inet_pton(AF_INET6, "ff:ff::ff", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + /* FIXME: This shows the implementation is not that robust chcking validity of input netmask: */ + OSMO_ASSERT(rc == 24); +} + const struct log_info_cat default_categories[] = { };
@@ -407,6 +464,7 @@ test_get_ip_and_port(); test_sockinit_osa(); test_osa_str(); + test_osa_netmask_prefixlen();
return EXIT_SUCCESS; }