diff options
-rw-r--r-- | accel-pppd/iprange.c | 209 | ||||
-rw-r--r-- | accel-pppd/utils.c | 27 | ||||
-rw-r--r-- | accel-pppd/utils.h | 2 |
3 files changed, 171 insertions, 67 deletions
diff --git a/accel-pppd/iprange.c b/accel-pppd/iprange.c index 6ea2c2f9..608574e3 100644 --- a/accel-pppd/iprange.c +++ b/accel-pppd/iprange.c @@ -1,12 +1,16 @@ +#include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <string.h> +#include <arpa/inet.h> +#include <netinet/in.h> #include "triton.h" #include "list.h" #include "log.h" +#include "utils.h" #include "iprange.h" @@ -22,64 +26,138 @@ struct iprange_t static int conf_disable = 0; static LIST_HEAD(client_ranges); -//static LIST_HEAD(tunnel_ranges); -//parses ranges like x.x.x.x/mask -static struct iprange_t *parse1(const char *str) +/* 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) { - int n,f1,f2,f3,f4,m; - struct iprange_t *r; - int mask; - - n = sscanf(str, "%u.%u.%u.%u/%u",&f1, &f2, &f3, &f4, &m); - if (n != 5) - return NULL; - if (f1 > 255) - return NULL; - if (f2 > 255) - return NULL; - if (f3 > 255) - return NULL; - if (f4 > 255) - return NULL; - if (m > 32) - return NULL; - - r = _malloc(sizeof(*r)); - r->begin = (f4 << 24) | (f3 << 16) | (f2 << 8) | f1; - - mask = htonl(~((1 << (32 - m)) - 1)); - r->end = ntohl(r->begin | ~mask); - r->begin = ntohl(r->begin); - - return r; + struct iprange_t *range; + + while (!list_empty(head)) { + range = list_first_entry(head, typeof(*range), entry); + list_del(&range->entry); + _free(range); + } } -//parses ranges like x.x.x.x-y -static struct iprange_t *parse2(const char *str) +static int parse_iprange(const char *str, struct iprange_t **range) { - int n,f1,f2,f3,f4,m; - struct iprange_t *r; + char ipstr[CIDR_MAXLEN + 1] = { 0 }; + struct iprange_t *_range; + struct in_addr addr; + const char *errmsg; + 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; + } + + 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, &errmsg)) { + log_error("iprange: impossible to parse range \"%s\":" + " invalid IPv4 address \"%s\"\n", + str, ipstr); + return -1; + } + ipmin = ntohl(addr.s_addr); + + + /* 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; + 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; + } + + /* Interpret /0 as disable request */ + if (prefix_len == 0) + goto disable; + + mask = UINT32_MAX << (32 - prefix_len); + + if (ipmin != (ipmin & mask)) { + char buf[INET_ADDRSTRLEN] = { 0 }; - n = sscanf(str, "%u.%u.%u.%u-%u",&f1, &f2, &f3, &f4, &m); - if (n != 5) - return NULL; - if (f1 > 255) - return NULL; - if (f2 > 255) - return NULL; - if (f3 > 255) - return NULL; - if (f4 > 255) - return NULL; - if (m < f4 || m > 255) - return NULL; - - r = _malloc(sizeof(*r)); - r->begin = ntohl((f4 << 24) | (f3 << 16) | (f2 << 8) | f1); - r->end = ntohl((m << 24) | (f3 << 16) | (f2 << 8) | f1); - - return r; + 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))); + } + + ipmax = ipmin | ~mask; + } else { + long int max; + + 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; + } + + ipmax = (ipmin & 0xffffff00) | max; + } + + _range = _malloc(sizeof(*_range)); + if (!_range) { + log_error("iprange: impossible to allocate range \"%s\":" + " memory allocation failed\n", str); + return -1; + } + + _range->begin = ipmin; + _range->end = ipmax; + *range = _range; + + return 0; + +disable: + *range = NULL; + + return 0; } static void load_ranges(struct list_head *list, const char *conf_sect) @@ -94,24 +172,22 @@ static void load_ranges(struct list_head *list, const char *conf_sect) } list_for_each_entry(opt, &s->items, entry) { - if (!strcmp(opt->name, "disable")) - goto disable; - r = parse1(opt->name); - if (!r) - r = parse2(opt->name); + /* Ignore parsing errors, parse_iprange() already logs suitable + * error message. + */ + if (parse_iprange(opt->name, &r) < 0) + continue; + if (!r) { - log_emerg("iprange: cann't parse '%s' in '%s'\n", opt->name, conf_sect); - _exit(EXIT_FAILURE); + log_warn("iprange: iprange module disabled, improper IP address assignment may cause kernel soft lockup!\n"); + free_ranges(list); + conf_disable = 1; + + return; } - if (r->begin == r->end) - goto disable; + list_add_tail(&r->entry, list); } - - return; -disable: - conf_disable = 1; - log_emerg("iprange: iprange module disabled so improper ip address assigning may cause kernel soft lockup!\n"); } static int check_range(struct list_head *list, in_addr_t ipaddr) @@ -145,7 +221,6 @@ int __export iprange_tunnel_check(in_addr_t ipaddr) static void iprange_init(void) { load_ranges(&client_ranges, "client-ip-range"); - //load_ranges(&tunnel_ranges, "tunnel-ip-range"); } DEFINE_INIT(10, iprange_init); diff --git a/accel-pppd/utils.c b/accel-pppd/utils.c index 81b4c993..3b87ee16 100644 --- a/accel-pppd/utils.c +++ b/accel-pppd/utils.c @@ -1,7 +1,9 @@ #include <errno.h> +#include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <netinet/in.h> #include "triton.h" #include "utils.h" @@ -35,6 +37,31 @@ int __export u_readlong(long int *dst, const char *src, } } +int __export u_parse_ip4addr(const char *src, struct in_addr *addr, + const char **err_msg) +{ + struct addrinfo hint = { + .ai_flags = AI_NUMERICHOST, + .ai_family = AF_INET, + .ai_socktype = 0, + .ai_protocol = 0, + }; + struct addrinfo *ainfo; + int err; + + err = getaddrinfo(src, NULL, &hint, &ainfo); + if (err) { + *err_msg = gai_strerror(err); + return err; + } + + *addr = ((struct sockaddr_in *)ainfo->ai_addr)->sin_addr; + + freeaddrinfo(ainfo); + + return 0; +} + int __export u_randbuf(void *buf, size_t buf_len, int *err) { uint8_t *u8buf = buf; diff --git a/accel-pppd/utils.h b/accel-pppd/utils.h index be62f6a3..87582648 100644 --- a/accel-pppd/utils.h +++ b/accel-pppd/utils.h @@ -5,6 +5,8 @@ void u_inet_ntoa(in_addr_t, char *str); int u_readlong(long int *dst, const char *src, long int min, long int max); +int u_parse_ip4addr(const char *src, struct in_addr *addr, + const char **err_msg); int u_randbuf(void *buf, size_t buf_len, int *err); #endif |