summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accel-pppd/iprange.c158
1 files changed, 63 insertions, 95 deletions
diff --git a/accel-pppd/iprange.c b/accel-pppd/iprange.c
index 40c938e..58321bd 100644
--- a/accel-pppd/iprange.c
+++ b/accel-pppd/iprange.c
@@ -29,12 +29,6 @@ static pthread_mutex_t iprange_lock = PTHREAD_MUTEX_INITIALIZER;
static bool conf_disable = false;
static LIST_HEAD(client_ranges);
-/* Maximum IPv4 address length with CIDR notation but no extra 0,
- * e.g. "0xff.0xff.0xff.0xff/32".
- */
-#define CIDR_MAXLEN 22
-
-
static void free_ranges(struct list_head *head)
{
struct iprange_t *range;
@@ -46,115 +40,89 @@ static void free_ranges(struct list_head *head)
}
}
+/* Parse a [client-ip-iprange] configuration entry.
+ * Ranges can be defined in CIDR notation ("192.0.2.0/24") or by specifying an
+ * upper bound for the last IPv4 byte, after a '-' character ("192.0.2.0-255").
+ * For simplicity, only mention the CIDR notation in error messages.
+ */
static int parse_iprange(const char *str, struct iprange_t **range)
{
- char ipstr[CIDR_MAXLEN + 1] = { 0 };
- struct iprange_t *_range;
- struct in_addr addr;
- char *suffix_str;
- uint32_t ipmin;
- uint32_t ipmax;
- bool is_cidr;
-
- /* Extra spaces and comments must have already been removed */
- if (strpbrk(str, " \t#")) {
- log_error("iprange: impossible to parse range \"%s\":"
- " invalid space or comment character found\n",
- str);
- return -1;
- }
+ struct iprange_t *new_range;
+ struct in_addr base_addr;
+ const char *ptr;
+ uint32_t ip_min;
+ uint32_t ip_max;
+ uint8_t suffix;
+ size_t len;
if (!strcmp(str, "disable"))
goto disable;
- strncpy(ipstr, str, CIDR_MAXLEN + 1);
- if (ipstr[CIDR_MAXLEN] != '\0') {
- log_error("iprange: impossible to parse range \"%s\":"
- " line too long\n",
- str);
- return -1;
- }
-
- suffix_str = strpbrk(ipstr, "-/");
- if (!suffix_str) {
- log_error("iprange: impossible to parse range \"%s\":"
- " unrecognised range format\n",
- str);
- return -1;
- }
-
- is_cidr = *suffix_str == '/';
- *suffix_str = '\0';
- ++suffix_str;
-
- if (!u_parse_ip4addr(ipstr, &addr)) {
- log_error("iprange: impossible to parse range \"%s\":"
- " invalid IPv4 address \"%s\"\n",
- str, ipstr);
- return -1;
- }
- ipmin = ntohl(addr.s_addr);
+ ptr = str;
-
- /* If is_cidr is set, range is given with CIDR notation,
- * e.g. "192.0.2.0/24".
- * If unset, range is an IP address where the last octet is replaced by
- * an octet range, e.g. "192.0.2.0-255".
- */
- if (is_cidr) {
- long int prefix_len;
+ /* Try IPv4 CIDR notation first */
+ len = u_parse_ip4cidr(ptr, &base_addr, &suffix);
+ if (len) {
+ uint32_t addr_hbo;
uint32_t mask;
- if (u_readlong(&prefix_len, suffix_str, 0, 32)) {
- log_error("iprange: impossible to parse range \"%s\":"
- " invalid CIDR prefix length \"/%s\"\n",
- str, suffix_str);
- return -1;
- }
+ /* Cast to uint64_t to avoid undefined 32 bits shift on 32 bits
+ * integer if 'suffix' is 0.
+ */
+ mask = (uint64_t)0xffffffff << (32 - suffix);
+ addr_hbo = ntohl(base_addr.s_addr);
+ ip_min = addr_hbo & mask;
+ ip_max = addr_hbo | ~mask;
+
+ if (ip_min != addr_hbo) {
+ struct in_addr min_addr = { .s_addr = htonl(ip_min) };
+ char ipbuf[INET_ADDRSTRLEN];
- /* Interpret /0 as disable request */
- if (prefix_len == 0) {
- if (ipmin != INADDR_ANY)
- log_warn("iprange: %s is equivalent to 0.0.0.0/0 and disables the iprange module\n",
- str);
- goto disable;
+ log_warn("iprange: network %s is equivalent to %s/%hhu\n",
+ str, u_ip4str(&min_addr, ipbuf), suffix);
}
+ goto addrange;
+ }
- mask = INADDR_BROADCAST << (32 - prefix_len);
- if (ipmin != (ipmin & mask)) {
- char buf[INET_ADDRSTRLEN] = { 0 };
+ /* Not an IPv4 CIDR, try the IPv4 range notation */
+ len = u_parse_ip4range(ptr, &base_addr, &suffix);
+ if (len) {
+ ip_min = ntohl(base_addr.s_addr);
+ ip_max = (ip_min & 0xffffff00) | suffix;
+ goto addrange;
+ }
- ipmin &= mask;
- addr.s_addr = htonl(ipmin);
- log_warn("iprange: first IP of range %s will be %s\n",
- str, inet_ntop(AF_INET, &addr, buf,
- sizeof(buf)));
- }
+ log_error("iprange: parsing range \"%s\" failed:"
+ " expecting an IPv4 network prefix in CIDR notation\n",
+ str);
- ipmax = ipmin | ~mask;
- } else {
- long int max;
+ return -1;
- if (u_readlong(&max, suffix_str, ipmin & 0xff, 255)) {
- log_error("iprange: impossible to parse range \"%s\":"
- " invalid upper bound \"-%s\"\n",
- str, suffix_str);
- return -1;
- }
+addrange:
+ ptr += len;
- ipmax = (ipmin & 0xffffff00) | max;
+ if (!u_parse_endstr(ptr)) {
+ log_error("iprange: parsing range \"%s\" failed:"
+ " unexpected data at \"%s\"\n",
+ str, ptr);
+ return -1;
}
- _range = _malloc(sizeof(*_range));
- if (!_range) {
- log_error("iprange: impossible to allocate range \"%s\":"
- " memory allocation failed\n", str);
+ if (ip_min == INADDR_ANY && ip_max == INADDR_BROADCAST)
+ goto disable;
+
+ new_range = _malloc(sizeof(*new_range));
+ if (!new_range) {
+ log_error("iprange: impossible to load range \"%s\":"
+ " memory allocation failed\n",
+ str);
return -1;
}
- _range->begin = ipmin;
- _range->end = ipmax;
- *range = _range;
+ new_range->begin = ip_min;
+ new_range->end = ip_max;
+
+ *range = new_range;
return 0;
@@ -175,7 +143,7 @@ static bool load_ranges(struct list_head *list, const char *conf_sect)
list_for_each_entry(opt, &s->items, entry) {
/* Ignore parsing errors, parse_iprange() already logs suitable
- * error message.
+ * error messages.
*/
if (parse_iprange(opt->name, &r) < 0)
continue;