#ifdef ADDR_RANGE_DEBUG
#define IIJDEBUG
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef IIJDEBUG
#include <debugutil.h>
static int saved_errno;
#define INT4_CHARS(x) \
((x) & 0xff000000) >> 24, ((x) & 0x00ff0000) >> 16, \
((x) & 0x0000ff00) >> 8, ((x) & 0x000000ff)
#endif
#include "addr_range.h"
static void in_addr_range_list_uniq(struct in_addr_range **);
static int in_addr_range_list_add0(struct in_addr_range **, u_int32_t, u_int32_t);
static int bitmask2masklen(u_int32_t);
struct in_addr_range *
in_addr_range_create()
{
struct in_addr_range *_this;
if ((_this = malloc(sizeof(struct in_addr_range))) == NULL)
return NULL;
memset(_this, 0xff, sizeof(struct in_addr_range));
_this->next = NULL;
return _this;
}
void
in_addr_range_destroy(struct in_addr_range *_this)
{
free(_this);
}
void
in_addr_range_list_remove_all(struct in_addr_range **list)
{
struct in_addr_range *cur0, *cur;
cur = *list;
while (cur != NULL) {
cur0 = cur;
cur = cur->next;
in_addr_range_destroy(cur0);
}
*list = NULL;
}
static void
in_addr_range_list_uniq(struct in_addr_range **list)
{
u_int32_t m0;
struct in_addr_range **cur, *a, *b;
restart:
for (cur = list; *cur != NULL; cur = &(*cur)->next) {
if ((*cur)->next == NULL)
continue;
a = *cur;
b = (*cur)->next;
m0 = 0xffffffff ^ a->mask;
if (a->mask == b->mask && (
a->addr - b->addr == m0 + 1 ||
b->addr - a->addr == m0 + 1)) {
m0 = m0 * 2 + 1;
m0 ^= 0xffffffff;
if ((a->addr & m0) != (b->addr & m0))
continue;
if (a->addr > b->addr) {
#ifdef IIJDEBUG
log_printf(LOG_DL_2,
"%d.%d.%d.%d/%d + %d.%d.%d.%d/%d = %d.%d.%d.%d/%d"
, INT4_CHARS(a->addr), bitmask2masklen(a->mask)
, INT4_CHARS(b->addr), bitmask2masklen(b->mask)
, INT4_CHARS(b->addr), bitmask2masklen(m0)
);
#endif
b->mask = m0;
*cur = b;
in_addr_range_destroy(a);
} else {
#ifdef IIJDEBUG
log_printf(LOG_DL_2,
"==>%d.%d.%d.%d/%d + %d.%d.%d.%d/%d = %d.%d.%d.%d/%d"
, INT4_CHARS(a->addr), bitmask2masklen(a->mask)
, INT4_CHARS(b->addr), bitmask2masklen(b->mask)
, INT4_CHARS(a->addr), bitmask2masklen(m0)
);
#endif
a->mask = m0;
(*cur)->next = b->next;
in_addr_range_destroy(b);
}
goto restart;
}
}
}
int
in_addr_range_list_includes(struct in_addr_range **list, struct in_addr *addr)
{
struct in_addr_range *cur;
u_int32_t addr0 = ntohl(addr->s_addr);
for (cur = *list; cur != NULL; cur = cur->next) {
if ((addr0 & cur->mask) == (cur->addr & cur->mask))
return 1;
}
return 0;
}
static int
in_addr_range_list_add0(struct in_addr_range **list, u_int32_t addr,
u_int32_t mask)
{
struct in_addr_range *ent;
struct in_addr_range **cur;
if ((ent = in_addr_range_create()) == NULL)
return -1;
ent->addr = addr;
ent->mask = mask;
for (cur = list; *cur != NULL; cur = &(*cur)->next) {
if ((ent->addr & (*cur)->mask) ==
((*cur)->addr & (*cur)->mask)) {
in_addr_range_destroy(ent);
in_addr_range_list_uniq(list);
return 0;
}
if ((ent->addr & ent->mask) == ((*cur)->addr & ent->mask)) {
ent->next = (*cur)->next;
free(*cur);
*cur = ent;
in_addr_range_list_uniq(list);
return 0;
}
if ((*cur)->addr > ent->addr)
break;
}
if (cur != NULL) {
ent->next = *cur;
*cur = ent;
in_addr_range_list_uniq(list);
}
return 0;
}
int
in_addr_range_list_add(struct in_addr_range **list, const char *str)
{
int is_range = 0, is_masklen = 0, is_maskaddr = 0, mask;
char *p0, *p1;
struct in_addr a0, a1;
u_int32_t i0, i1;
if ((p0 = strdup(str)) == NULL) {
#ifdef IIJDEBUG
saved_errno = errno;
log_printf(LOG_DL_1, "malloc() failed: %m");
errno = saved_errno;
#endif
return -1;
}
if ((p1 = strchr(p0, '-')) != NULL) {
*p1++ = '\0';
is_range = 1;
} else if ((p1 = strchr(p0, '/')) != NULL) {
*p1++ = '\0';
if (sscanf(p1, "%d", &mask) != 1) {
#ifdef IIJDEBUG
saved_errno = errno;
log_printf(LOG_DL_1, "sscanf(%s) failed: %m",
p1);
errno = saved_errno;
#endif
free(p0);
return -1;
}
if (mask < 0 || 32 < mask) {
#ifdef IIJDEBUG
log_printf(LOG_DL_1, "must be 0 <= masklen <= 32: %d",
mask);
errno = EINVAL;
#endif
free(p0);
return -1;
}
is_masklen = 1;
} else if ((p1 = strchr(p0, ':')) != NULL) {
*p1++ = '\0';
is_maskaddr = 1;
}
if (inet_pton(AF_INET, p0, &a0) != 1) {
if (errno == 0)
errno = EINVAL;
#ifdef IIJDEBUG
saved_errno = errno;
log_printf(LOG_DL_1, "inet_pton(%s) failed: %m", p0);
errno = saved_errno;
#endif
free(p0);
return -1;
}
if ((is_range || is_maskaddr) && inet_pton(AF_INET, p1, &a1) != 1) {
if (errno == 0)
errno = EINVAL;
#ifdef IIJDEBUG
saved_errno = errno;
log_printf(LOG_DL_1, "inet_pton(%s) failed: %m", p1);
errno = saved_errno;
#endif
free(p0);
return -1;
}
free(p0);
if (is_range) {
i0 = ntohl(a0.s_addr);
i1 = ntohl(a1.s_addr);
for (; i0 <= i1; i0++)
in_addr_range_list_add0(list, i0, 0xffffffff);
} else if (is_masklen) {
i0 = ntohl(a0.s_addr);
if (mask == 0)
i1 = 0x0;
else
i1 = 0xffffffffL << (32 - mask);
if ((i0 & i1) != i0) {
#ifdef IIJDEBUG
log_printf(LOG_DL_1, "invalid addr/mask pair: "
"%d.%d.%d.%d/%d",
INT4_CHARS(i0), bitmask2masklen(i1));
#endif
errno = EINVAL;
return -1;
}
in_addr_range_list_add0(list, i0, i1);
} else if (is_maskaddr) {
i0 = ntohl(a0.s_addr);
i1 = ntohl(a1.s_addr);
if ((i0 & i1) != i0 || bitmask2masklen(i1) < 0) {
#ifdef IIJDEBUG
log_printf(LOG_DL_1, "invalid addr/mask pair: "
"%d.%d.%d.%d/%d",
INT4_CHARS(i0), bitmask2masklen(i1));
#endif
errno = EINVAL;
return -1;
}
in_addr_range_list_add0(list, i0, i1);
} else {
i0 = ntohl(a0.s_addr);
in_addr_range_list_add0(list, i0, 0xffffffff);
}
return 0;
}
static int bitmask2masklen(u_int32_t mask)
{
switch(mask) {
case 0x00000000: return 0;
case 0x80000000: return 1;
case 0xC0000000: return 2;
case 0xE0000000: return 3;
case 0xF0000000: return 4;
case 0xF8000000: return 5;
case 0xFC000000: return 6;
case 0xFE000000: return 7;
case 0xFF000000: return 8;
case 0xFF800000: return 9;
case 0xFFC00000: return 10;
case 0xFFE00000: return 11;
case 0xFFF00000: return 12;
case 0xFFF80000: return 13;
case 0xFFFC0000: return 14;
case 0xFFFE0000: return 15;
case 0xFFFF0000: return 16;
case 0xFFFF8000: return 17;
case 0xFFFFC000: return 18;
case 0xFFFFE000: return 19;
case 0xFFFFF000: return 20;
case 0xFFFFF800: return 21;
case 0xFFFFFC00: return 22;
case 0xFFFFFE00: return 23;
case 0xFFFFFF00: return 24;
case 0xFFFFFF80: return 25;
case 0xFFFFFFC0: return 26;
case 0xFFFFFFE0: return 27;
case 0xFFFFFFF0: return 28;
case 0xFFFFFFF8: return 29;
case 0xFFFFFFFC: return 30;
case 0xFFFFFFFE: return 31;
case 0xFFFFFFFF: return 32;
}
return -1;
}
#ifdef ADDR_RANGE_DEBUG
#include <unistd.h>
static void usage(void);
static void
usage()
{
fprintf(stderr,
"usage: addr_range [-d] [addr_exp]...\n"
"\taddr_exp: 192.168.0.1 (equals 192.168.0.1/32) or \n"
"\t 192.168.32.1-192.168.32.255 or \n"
"\t 192.168.4.0:255.255.254.0 or \n"
"\t 10.0.0.1/24\n"
);
}
int
main(int argc, char *argv[])
{
int i, ch;
struct in_addr_range *list = NULL, *cur;
debugfp = stderr;
debuglevel = 0;
while ((ch = getopt(argc, argv, "d")) != -1) {
switch (ch) {
case 'd':
debuglevel++;
break;
default:
usage();
exit(1);
}
}
argc -= optind;
argv += optind;
if (argc <= 0) {
usage();
exit(1);
}
for (i = 0; i < argc; i++) {
if (in_addr_range_list_add(&list, argv[i])) {
perror(argv[i]);
}
}
for (cur = list; cur != NULL; cur = cur->next) {
fprintf(stderr, "%d.%d.%d.%d/%d\n"
, INT4_CHARS(cur->addr), bitmask2masklen(cur->mask)
);
}
in_addr_range_list_remove_all(&list);
return 0;
}
#endif