diff --git a/THANKS b/THANKS index ae23230..f230fcd 100644 --- a/THANKS +++ b/THANKS @@ -41,3 +41,4 @@ Ivanov Ivan Manuel Hachtkemper Klaus Slott Boris Lytochkin +Jeff Bailey diff --git a/src/dhcpd-pools.h b/src/dhcpd-pools.h index 53df74a..47e246e 100644 --- a/src/dhcpd-pools.h +++ b/src/dhcpd-pools.h @@ -238,6 +238,8 @@ extern int parse_ipaddr_init(const char *restrict src, extern int parse_ipaddr_v4(const char *restrict src, union ipaddr_t *restrict dst); extern int parse_ipaddr_v6(const char *restrict src, union ipaddr_t *restrict dst); +extern void parse_cidr(struct range_t *range_p, const char *word); + extern void (*copy_ipaddr) (union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); extern void copy_ipaddr_init(union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); diff --git a/src/getdata.c b/src/getdata.c index 995e1a9..6f53b1a 100644 --- a/src/getdata.c +++ b/src/getdata.c @@ -339,13 +339,19 @@ void parse_config(int is_include, const char *restrict config_file, /* printf ("range 2nd ip: %s\n", word); */ range_p = ranges + num_ranges; argument = ITS_NOTHING_INTERESTING; - parse_ipaddr(word, &addr); - if (one_ip_range == 1) { + if (strchr(word, '/')) { + parse_cidr(range_p, word); one_ip_range = 0; - copy_ipaddr(&range_p->first_ip, &addr); + } else { + /* not cidr */ + parse_ipaddr(word, &addr); + if (one_ip_range == 1) { + one_ip_range = 0; + copy_ipaddr(&range_p->first_ip, &addr); + } + copy_ipaddr(&range_p->last_ip, &addr); + reorder_last_first(range_p); } - copy_ipaddr(&range_p->last_ip, &addr); - reorder_last_first(range_p); newrange: range_p->count = 0; range_p->touched = 0; diff --git a/src/other.c b/src/other.c index 7ac69c9..4eda1ad 100644 --- a/src/other.c +++ b/src/other.c @@ -52,10 +52,15 @@ #include "error.h" #include "progname.h" #include "quote.h" +#include "xalloc.h" #include "dhcpd-pools.h" #include "defaults.h" +char *(*cidr_last)(union ipaddr_t *restrict addr, const int mask); +static char *cidr_last_v4(union ipaddr_t *restrict addr, const int mask); +static char *cidr_last_v6(union ipaddr_t *restrict addr, const int mask); + /*! \brief Set function pointers depending on IP version. * \param ip IP version. */ @@ -73,6 +78,7 @@ void set_ipv_functions(int version) leasecomp = leasecomp_v4; ntop_ipaddr = ntop_ipaddr_v4; parse_ipaddr = parse_ipaddr_v4; + cidr_last = cidr_last_v4; xstrstr = xstrstr_v4; break; @@ -86,6 +92,7 @@ void set_ipv_functions(int version) leasecomp = leasecomp_v6; ntop_ipaddr = ntop_ipaddr_v6; parse_ipaddr = parse_ipaddr_v6; + cidr_last = cidr_last_v6; xstrstr = xstrstr_v6; break; @@ -99,6 +106,7 @@ void set_ipv_functions(int version) leasecomp = leasecomp_init; ntop_ipaddr = ntop_ipaddr_init; parse_ipaddr = parse_ipaddr_init; + cidr_last = NULL; xstrstr = xstrstr_init; break; @@ -148,6 +156,92 @@ int parse_ipaddr_v6(const char *restrict src, union ipaddr_t *restrict dst) return rv == 1; } +static int strtol_mask(const char *str) +{ + long num; + char *end = NULL; + + errno = 0; + if (str == NULL || *str == '\0') + goto err; + num = strtol(str, &end, 10); + + if (errno || str == end || (end && *end)) + goto err; + if (num < 0 || 128 < num) + goto err; + return (int)num; + err: + return -1; +} + +static char *cidr_last_v4(union ipaddr_t *restrict addr, const int mask) +{ + union ipaddr_t last_ip; + uint32_t netmask; + const char *ip; + + if (mask) + netmask = (1U << (32 - mask)) - 1; + else + netmask = 0; + last_ip.v4 = addr->v4 | netmask; + + ip = ntop_ipaddr(&last_ip); + return xstrdup(ip); +} + +static char *cidr_last_v6(union ipaddr_t *restrict addr, const int mask) +{ + union ipaddr_t bitmask; + int i, j; + char ip[128]; + + memset(&bitmask, 0x0, sizeof(bitmask)); + for (i = mask, j = 0; i > 0; i -= 8, j++) { + if (i >= 8) + bitmask.v6[j] = 0xff; + else + bitmask.v6[j] = (unsigned char)(0xffU << (8 - i)); + } + for (i = 0; i < (int)sizeof(bitmask); i++) + addr->v6[i] |= ~bitmask.v6[i]; + inet_ntop(AF_INET6, addr, ip, sizeof(ip)); + return xstrdup(ip); +} + +void parse_cidr(struct range_t *range_p, const char *word) +{ + char *divider; + int mask; + union ipaddr_t addr; + char *last; + + /* determine cidr */ + divider = strchr(word, '/'); + *divider++ = '\0'; + mask = strtol_mask(divider); + if (mask < 0) + error(EXIT_FAILURE, 0, "cidr %s invalid mask %s", word, + divider); + if (config.ip_version == IPvUNKNOWN) { + if (!strchr(word, ':')) + set_ipv_functions(IPv4); + else + set_ipv_functions(IPv6); + } + + /* start of the range is easy */ + parse_ipaddr(word, &addr); + copy_ipaddr(&range_p->first_ip, &addr); + + /* end of the range depends cidr size */ + last = cidr_last(&addr, mask); + parse_ipaddr(last, &addr); + copy_ipaddr(&range_p->last_ip, &addr); + free(last); +} + /*! \brief Copy IP address to union. * * \param dst Destination for a binary IP address. diff --git a/tests/Makemodule.am b/tests/Makemodule.am index 46bac47..2784298 100644 --- a/tests/Makemodule.am +++ b/tests/Makemodule.am @@ -20,6 +20,8 @@ TESTS = \ tests/leading0 \ tests/one-ip \ tests/one-line \ + tests/range4 \ + tests/range6 \ tests/same-twice \ tests/simple \ tests/sorts \ diff --git a/tests/confs/range4 b/tests/confs/range4 new file mode 100644 index 0000000..6540de7 --- /dev/null +++ b/tests/confs/range4 @@ -0,0 +1,31 @@ +shared-network example1 { + subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + range 10.0.0.0/27; + } + } + subnet 10.1.0.0 netmask 255.255.255.0 { + pool { + range 10.1.0.1/27; + } + } +} + +shared-network example2 { + subnet 10.2.0.0 netmask 255.255.255.0 { + pool { + range 10.2.0.1/28; + } + } + subnet 10.3.0.0 netmask 255.255.255.0 { + pool { + range 10.3.0.1/29; + } + } +} + +subnet 10.4.0.0 netmask 255.255.255.0 { + pool { + range 10.4.0.1/32; + } +} diff --git a/tests/confs/range6 b/tests/confs/range6 new file mode 100644 index 0000000..b15d615 --- /dev/null +++ b/tests/confs/range6 @@ -0,0 +1,4 @@ +subnet6 dead:abba:1000::/56 { + range6 dead:abba:1000::/56; + prefix6 dead:abba:1000:0100:: dead:abba:1000:ff00::/56; +} diff --git a/tests/expected/range4 b/tests/expected/range4 new file mode 100644 index 0000000..f22e11d --- /dev/null +++ b/tests/expected/range4 @@ -0,0 +1,16 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +example1 10.0.0.0 - 10.0.0.31 32 12 37.500 0 12 37.500 +example1 10.1.0.1 - 10.1.0.31 31 10 32.258 0 10 32.258 +example2 10.2.0.1 - 10.2.0.15 15 8 53.333 0 8 53.333 +example2 10.3.0.1 - 10.3.0.7 7 7 100.000 0 7 100.000 +All networks 10.4.0.1 - 10.4.0.1 1 1 100.000 0 1 100.000 + +Shared networks: +name max cur percent touch t+c t+c perc +example1 63 22 34.921 0 22 34.921 +example2 22 15 68.182 0 15 68.182 + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 86 38 44.186 0 38 44.186 diff --git a/tests/expected/range6 b/tests/expected/range6 new file mode 100644 index 0000000..fe06786 --- /dev/null +++ b/tests/expected/range6 @@ -0,0 +1,10 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +All networks dead:abba:1000:: - dead:abba:1000:ff:ffff:ffff:ffff:ffff 4.72237e+21 2 0.000 1 3 0.000 + +Shared networks: +name max cur percent touch t+c t+c perc + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 4.72237e+21 2 0.000 1 3 0.000 diff --git a/tests/leases/range4 b/tests/leases/range4 new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/leases/range4 @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/leases/range6 b/tests/leases/range6 new file mode 120000 index 0000000..82e3978 --- /dev/null +++ b/tests/leases/range6 @@ -0,0 +1 @@ +v6 \ No newline at end of file diff --git a/tests/range4 b/tests/range4 new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/range4 @@ -0,0 +1 @@ +test.sh \ No newline at end of file diff --git a/tests/range6 b/tests/range6 new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/range6 @@ -0,0 +1 @@ +test.sh \ No newline at end of file