aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2017-10-16 14:27:32 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2017-10-16 17:45:40 +0200
commit2e7b9ff891a0d4c84a4be3fffbac1b9502fe267c (patch)
tree0ee3abe0431822780117efbf9994dee62adabec3
parent361cb9e910e77f657ad39e0be4684b8028711a0c (diff)
lib/in46a: Introduce in46a_netmasklen API
-rw-r--r--lib/in46_addr.c58
-rw-r--r--lib/in46_addr.h1
-rw-r--r--tests/lib/in46a_test.c59
-rw-r--r--tests/lib/in46a_test.ok2
4 files changed, 120 insertions, 0 deletions
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
index 32e0f8d..36ad6af 100644
--- a/lib/in46_addr.c
+++ b/lib/in46_addr.c
@@ -195,6 +195,64 @@ int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
}
}
+static unsigned int ipv4_netmasklen(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;
+}
+
+static unsigned int ipv6_netmasklen(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;
+}
+
+/*! Convert netmask to prefix length representation
+ * \param[in] netmask in46_addr 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)
+ */
+unsigned int in46a_netmasklen(const struct in46_addr *netmask)
+{
+ switch (netmask->len) {
+ case 4:
+ return ipv4_netmasklen(&netmask->v4);
+ case 16:
+ return ipv6_netmasklen(&netmask->v6);
+ default:
+ OSMO_ASSERT(0);
+ return 0;
+ }
+}
+
/*! Convert given PDP End User Address to in46_addr
* \returns 0 on success; negative on error */
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
diff --git a/lib/in46_addr.h b/lib/in46_addr.h
index ce2df14..ff26521 100644
--- a/lib/in46_addr.h
+++ b/lib/in46_addr.h
@@ -27,6 +27,7 @@ extern const char *in46p_ntoa(const struct in46_prefix *in46p);
extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b);
extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
+unsigned int in46a_netmasklen(const struct in46_addr *netmask);
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua);
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
diff --git a/tests/lib/in46a_test.c b/tests/lib/in46a_test.c
index d6215e7..42a1768 100644
--- a/tests/lib/in46a_test.c
+++ b/tests/lib/in46a_test.c
@@ -246,6 +246,64 @@ static void test_in46a_from_eua(void)
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
}
+static void test_in46a_netmasklen(void)
+{
+ struct in46_addr netmask;
+ unsigned int len;
+
+ printf("Testing in46a_netmasklen() with IPv4 addresses\n");
+ netmask.len = 4;
+
+ netmask.v4.s_addr = 0xffffffff;
+ len = in46a_netmasklen(&netmask);
+ OSMO_ASSERT(len == 32);
+
+ netmask.v4.s_addr = 0x00ffffff;
+ len = in46a_netmasklen(&netmask);
+ OSMO_ASSERT(len == 24);
+
+ netmask.v4.s_addr = 0x00f0ffff;
+ len = in46a_netmasklen(&netmask);
+ OSMO_ASSERT(len == 20);
+
+ netmask.v4.s_addr = 0x000000fe;
+ len = in46a_netmasklen(&netmask);
+ OSMO_ASSERT(len == 7);
+
+ netmask.v4.s_addr = 0x00000000;
+ len = in46a_netmasklen(&netmask);
+ OSMO_ASSERT(len == 0);
+
+ printf("Testing in46a_netmasklen() with IPv6 addresses\n");
+ const struct in46_addr netmaskA = {
+ .len = 16,
+ .v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
+ };
+ len = in46a_netmasklen(&netmaskA);
+ OSMO_ASSERT(len == 128);
+
+ const struct in46_addr netmaskB = {
+ .len = 16,
+ .v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00},
+ };
+ len = in46a_netmasklen(&netmaskB);
+ OSMO_ASSERT(len == 104);
+
+ const struct in46_addr netmaskC = {
+ .len = 16,
+ .v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00},
+ };
+ len = in46a_netmasklen(&netmaskC);
+ OSMO_ASSERT(len == 103);
+
+ const struct in46_addr netmaskD = {
+ .len = 16,
+ .v6.s6_addr = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ };
+ len = in46a_netmasklen(&netmaskD);
+ OSMO_ASSERT(len == 0);
+}
+
int main(int argc, char **argv)
{
osmo_init_logging(&log_info);
@@ -262,4 +320,5 @@ int main(int argc, char **argv)
test_in46a_within_mask();
test_in46a_to_eua();
test_in46a_from_eua();
+ test_in46a_netmasklen();
}
diff --git a/tests/lib/in46a_test.ok b/tests/lib/in46a_test.ok
index b115444..9a0ff7a 100644
--- a/tests/lib/in46a_test.ok
+++ b/tests/lib/in46a_test.ok
@@ -15,3 +15,5 @@ in46a_within_mask(10.11.12.14, 10.11.12.13, 32) = 0
in46a_within_mask(10.11.12.14, 10.11.12.12, 30) = 1
testing in46a_to_eua()
Testing in46a_from_eua()
+Testing in46a_netmasklen() with IPv4 addresses
+Testing in46a_netmasklen() with IPv6 addresses