All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] util: add util_ip_prefix_tohl
@ 2020-10-26 20:35 James Prestwood
  2020-10-26 20:35 ` [PATCH 2/2] unit: add test for util_ip_prefix_tohl James Prestwood
  2020-10-26 21:17 ` [PATCH 1/2] util: add util_ip_prefix_tohl Denis Kenzior
  0 siblings, 2 replies; 3+ messages in thread
From: James Prestwood @ 2020-10-26 20:35 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 3292 bytes --]

Parses an IP prefix notation string into prefix, start, end, and
netmask. All values are returned in host order.
---
 src/util.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util.h |  3 ++
 2 files changed, 93 insertions(+)

 - Moved these two into their own set
 - Changed the loop to only iterate over the maximum IP string size (16).
   This prevents looping through a really long (invalid) IP string 
 - Use l_strlcpy instead of memcpy + NULL terminate
diff --git a/src/util.c b/src/util.c
index 13b01c81..ac22b261 100644
--- a/src/util.c
+++ b/src/util.c
@@ -28,6 +28,8 @@
 #include <stdio.h>
 #include <sys/uio.h>
 #include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <ell/ell.h>
 
@@ -218,3 +220,91 @@ const char *util_get_username(const char *identity)
 
 	return identity;
 }
+
+static bool is_prefix_valid(uint32_t ip, unsigned int prefix)
+{
+	int i;
+
+	for (i = 31 - prefix; i >= 0; i--) {
+		if (ip & (1 << i))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * Parse a prefix notation IP string (e.g. A.B.C.D/E) into an IP range and
+ * netmask. All returned IP addresses/mask will be in host order. The start/end
+ * IP will only include the usable IP range where the last octet is not zero or
+ * 255.
+ */
+bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
+				uint32_t *start_out, uint32_t *end_out,
+				uint32_t *mask_out)
+{
+	struct in_addr ia;
+	int i;
+	unsigned int prefix = 0;
+	char no_prefix[INET_ADDRSTRLEN];
+	char *endp;
+	uint32_t start_ip;
+	uint32_t end_ip;
+	uint32_t netmask = 0xffffffff;
+
+	/*
+	 * Only iterate over the max length of an IP in case of invalid long
+	 * inputs.
+	 */
+	for (i = 0; i < INET_ADDRSTRLEN && ip[i] != '\0'; i++) {
+		/* Found '/', check the next byte exists and parse prefix */
+		if (ip[i] == '/' && ip[i + 1] != '\0') {
+			prefix = strtol(ip + i + 1, &endp, 10);
+			if (*endp != '\0')
+				return false;
+
+			break;
+		}
+	}
+
+	if (prefix < 1 || prefix > 31)
+		return false;
+
+	/* 'i' will be at most INET_ADDRSTRLEN - 1 */
+	l_strlcpy(no_prefix, ip, i + 1);
+
+	/* Check if IP preceeding prefix is valid */
+	if (inet_pton(AF_INET, no_prefix, &ia) != 1 || ia.s_addr == 0)
+		return false;
+
+	start_ip = ntohl(ia.s_addr);
+
+	if (!is_prefix_valid(start_ip, prefix))
+		return false;
+
+	/* Usable range is start + 1 .. end - 1 */
+	start_ip += 1;
+
+	/* Calculate end IP and netmask */
+	end_ip = start_ip;
+	for (i = 31 - prefix; i >= 0; i--) {
+		end_ip |= (1 << i);
+		netmask &= ~(1 << i);
+	}
+
+	end_ip -= 1;
+
+	if (prefix_out)
+		*prefix_out = prefix;
+
+	if (start_out)
+		*start_out = start_ip;
+
+	if (end_out)
+		*end_out = end_ip;
+
+	if (mask_out)
+		*mask_out = netmask;
+
+	return true;
+}
diff --git a/src/util.h b/src/util.h
index 2679c117..e6b4747f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -107,4 +107,7 @@ static inline uint32_t util_secure_fill_with_msb(uint32_t val)
 	return (uint32_t) (val >> (sizeof(val)*8 - 1)) * 0xFFFFFFFF;
 }
 
+bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix, uint32_t *start_out,
+				uint32_t *end_out, uint32_t *mask_out);
+
 #endif /* __UTIL_H */
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] unit: add test for util_ip_prefix_tohl
  2020-10-26 20:35 [PATCH 1/2] util: add util_ip_prefix_tohl James Prestwood
@ 2020-10-26 20:35 ` James Prestwood
  2020-10-26 21:17 ` [PATCH 1/2] util: add util_ip_prefix_tohl Denis Kenzior
  1 sibling, 0 replies; 3+ messages in thread
From: James Prestwood @ 2020-10-26 20:35 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2878 bytes --]

---
 unit/test-util.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

 - Added a very long invalid IP string test
 - Added a valid IP string that is maximum length possible

diff --git a/unit/test-util.c b/unit/test-util.c
index f091f2c2..c3f8c673 100644
--- a/unit/test-util.c
+++ b/unit/test-util.c
@@ -27,6 +27,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <ell/ell.h>
 
 #include "src/util.h"
@@ -115,6 +117,68 @@ static void get_username_test(const void *data)
 	assert(strcmp(test, "username") == 0);
 }
 
+static void ip_prefix_test(const void *data)
+{
+	unsigned int i;
+	char *invalid[] = {
+		"192.168.0.0", /* Not prefix notation */
+		"192.168./22", /* Incomplete notation */
+		"192.168.0.1/255", /* Too long prefix */
+		"192.168.0.1/0", /* Too short prefix */
+		"192.168.0.1/16", /* Invalid prefix */
+		"192.168.1.2.3/24", /* IP too long */
+		"192.168.111.222.333.444/20", /* IP way too long */
+	};
+
+	struct {
+		char *ip_prefix;
+		uint8_t prefix;
+		char *start;
+		char *end;
+		char *mask;
+	} valid[] = {
+		{"192.168.80.0/22", 22, "192.168.80.1",
+				"192.168.83.254", "255.255.252.0"},
+		{"192.168.128.0/20", 20, "192.168.128.1",
+				"192.168.143.254", "255.255.240.0"},
+		{"192.168.0.0/25", 25, "192.168.0.1",
+				"192.168.0.126", "255.255.255.128"},
+		{"192.168.0.0/29", 29, "192.168.0.1",
+				"192.168.0.6", "255.255.255.248"},
+		{"192.168.0.128/25", 25, "192.168.0.129",
+				"192.168.0.254", "255.255.255.128"},
+		/* Valid notation which is maximum length */
+		{"192.168.111.108/30", 30, "192.168.111.109",
+				"192.168.111.110", "255.255.255.252"},
+	};
+
+	for (i = 0; i < L_ARRAY_SIZE(invalid); i++)
+		assert(!util_ip_prefix_tohl(invalid[i], NULL, NULL,
+						NULL, NULL));
+
+	for (i = 0; i < L_ARRAY_SIZE(valid); i++) {
+		uint8_t prefix;
+		uint32_t start;
+		uint32_t end;
+		uint32_t mask;
+		struct in_addr ia;
+
+		assert(util_ip_prefix_tohl(valid[i].ip_prefix,
+						&prefix, &start, &end, &mask));
+
+		assert(valid[i].prefix == prefix);
+
+		ia.s_addr = htonl(start);
+		assert(strcmp(inet_ntoa(ia), valid[i].start) == 0);
+
+		ia.s_addr = htonl(end);
+		assert(strcmp(inet_ntoa(ia), valid[i].end) == 0);
+
+		ia.s_addr = htonl(mask);
+		assert(strcmp(inet_ntoa(ia), valid[i].mask) == 0);
+	}
+}
+
 int main(int argc, char *argv[])
 {
 	l_test_init(&argc, &argv);
@@ -122,6 +186,7 @@ int main(int argc, char *argv[])
 	l_test_add("/util/ssid_to_utf8/", ssid_to_utf8, ssid_samples);
 	l_test_add("/util/get_domain/", get_domain_test, NULL);
 	l_test_add("/util/get_username/", get_username_test, NULL);
+	l_test_add("/util/ip_prefix/", ip_prefix_test, NULL);
 
 	return l_test_run();
 }
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 1/2] util: add util_ip_prefix_tohl
  2020-10-26 20:35 [PATCH 1/2] util: add util_ip_prefix_tohl James Prestwood
  2020-10-26 20:35 ` [PATCH 2/2] unit: add test for util_ip_prefix_tohl James Prestwood
@ 2020-10-26 21:17 ` Denis Kenzior
  1 sibling, 0 replies; 3+ messages in thread
From: Denis Kenzior @ 2020-10-26 21:17 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1060 bytes --]

Hi James,

On 10/26/20 3:35 PM, James Prestwood wrote:
> Parses an IP prefix notation string into prefix, start, end, and
> netmask. All values are returned in host order.
> ---
>   src/util.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   src/util.h |  3 ++
>   2 files changed, 93 insertions(+)
> 
>   - Moved these two into their own set
>   - Changed the loop to only iterate over the maximum IP string size (16).
>     This prevents looping through a really long (invalid) IP string
>   - Use l_strlcpy instead of memcpy + NULL terminate

<snip>

> +	/*
> +	 * Only iterate over the max length of an IP in case of invalid long
> +	 * inputs.
> +	 */
> +	for (i = 0; i < INET_ADDRSTRLEN && ip[i] != '\0'; i++) {
> +		/* Found '/', check the next byte exists and parse prefix */
> +		if (ip[i] == '/' && ip[i + 1] != '\0') {
> +			prefix = strtol(ip + i + 1, &endp, 10);

I changed this to strtoul and..

> +			if (*endp != '\0')
> +				return false;
> +
> +			break;
> +		}
> +	}

Both applied, thanks.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-10-26 21:17 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-26 20:35 [PATCH 1/2] util: add util_ip_prefix_tohl James Prestwood
2020-10-26 20:35 ` [PATCH 2/2] unit: add test for util_ip_prefix_tohl James Prestwood
2020-10-26 21:17 ` [PATCH 1/2] util: add util_ip_prefix_tohl Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.