From 42605c9e76a0ffb876afc00f64210a407bf463f3 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 26 Oct 2020 13:35:13 -0700 Subject: [PATCH] util: add util_ip_prefix_tohl 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(+) diff --git a/src/util.c b/src/util.c index 13b01c81..009596ba 100644 --- a/src/util.c +++ b/src/util.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include @@ -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 = strtoul(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 */