1 /* $OpenBSD: addr_range.c,v 1.2 2010/07/01 03:38:17 yasuoka Exp $ */ 2 /*- 3 * Copyright (c) 2009 Internet Initiative Japan Inc. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 /* 28 * addr_range.c - Parser/aggregator for varios address/network expressions. 29 * 30 * Input: 192.168.0.0/24 192.168.1.0/24 31 * Output: 192.168.0.0/23 32 * 33 * Input: 192.168.0.128-192.168.0.255 34 * Output: 192.168.0.128/25 35 * 36 * Notice: 37 * - Byte order of 'addr' and 'mask' (members of struct in_addr_range) 38 * are host byte order. 39 * - Parsing address range like 192.168.0.0-192.168.255.255 costs much of 40 * cpu/memory. 41 * 42 * Example code: 43 * 44 * struct in_addr_range *list = NULL, *cur; 45 * 46 * in_addr_range_list_add(&list, "192.168.0.128-192.168.0.255"); 47 * in_addr_range_list_add(&list, "192.168.1.128-192.168.1.255"); 48 * for (cur = list; cur != NULL; cur = cur->next) { 49 * // do something 50 * struct in_addr a, m; 51 * a.s_addr = htonl(cur->addr); 52 * m.s_addr = htonl(cur->mask); 53 * } 54 * in_addr_range_list_remove_all(&list); 55 * 56 * Author: 57 * Yasuoka Masahiko <yasuoka@iij.ad.jp> 58 * 59 * $Id: addr_range.c,v 1.2 2010/07/01 03:38:17 yasuoka Exp $ 60 */ 61 #ifdef ADDR_RANGE_DEBUG 62 #define IIJDEBUG 63 #endif 64 65 #include <sys/types.h> 66 #include <netinet/in.h> 67 #include <arpa/inet.h> 68 69 #include <errno.h> 70 #include <syslog.h> 71 #include <string.h> 72 #include <stdlib.h> 73 #include <stdio.h> 74 75 #ifdef IIJDEBUG 76 #include <debugutil.h> 77 static int saved_errno; 78 #define INT4_CHARS(x) \ 79 ((x) & 0xff000000) >> 24, ((x) & 0x00ff0000) >> 16, \ 80 ((x) & 0x0000ff00) >> 8, ((x) & 0x000000ff) 81 #endif 82 #include "addr_range.h" 83 84 static void in_addr_range_list_uniq __P((struct in_addr_range **)); 85 static int in_addr_range_list_add0 __P((struct in_addr_range **, u_int32_t, u_int32_t)); 86 static int bitmask2masklen __P((u_int32_t)); 87 88 struct in_addr_range * 89 in_addr_range_create() 90 { 91 struct in_addr_range *_this; 92 if ((_this = (struct in_addr_range *)malloc( 93 sizeof(struct in_addr_range))) == NULL) 94 return NULL; 95 memset(_this, 0xff, sizeof(struct in_addr_range)); 96 _this->next = NULL; 97 return _this; 98 } 99 100 101 void 102 in_addr_range_destroy(struct in_addr_range *_this) 103 { 104 free(_this); 105 } 106 107 void 108 in_addr_range_list_remove_all(struct in_addr_range **list) 109 { 110 struct in_addr_range *cur0, *cur; 111 112 cur = *list; 113 while (cur != NULL) { 114 cur0 = cur; 115 cur = cur->next; 116 in_addr_range_destroy(cur0); 117 } 118 *list = NULL; 119 } 120 121 static void 122 in_addr_range_list_uniq(struct in_addr_range **list) 123 { 124 u_int32_t m0; 125 struct in_addr_range **cur, *a, *b; 126 127 restart: 128 for (cur = list; *cur != NULL; cur = &(*cur)->next) { 129 if ((*cur)->next == NULL) 130 continue; 131 a = *cur; 132 b = (*cur)->next; 133 m0 = 0xffffffff ^ a->mask; 134 if (a->mask == b->mask && ( 135 a->addr - b->addr == m0 + 1 || 136 b->addr - a->addr == m0 + 1)) { 137 m0 = m0 * 2 + 1; 138 m0 ^= 0xffffffff; 139 if ((a->addr & m0) != (b->addr & m0)) 140 continue; 141 if (a->addr > b->addr) { 142 #ifdef IIJDEBUG 143 log_printf(LOG_DL_2, 144 "%d.%d.%d.%d/%d + %d.%d.%d.%d/%d = %d.%d.%d.%d/%d" 145 , INT4_CHARS(a->addr), bitmask2masklen(a->mask) 146 , INT4_CHARS(b->addr), bitmask2masklen(b->mask) 147 , INT4_CHARS(b->addr), bitmask2masklen(m0) 148 ); 149 #endif 150 b->mask = m0; 151 *cur = b; 152 in_addr_range_destroy(a); 153 } else { 154 #ifdef IIJDEBUG 155 log_printf(LOG_DL_2, 156 "==>%d.%d.%d.%d/%d + %d.%d.%d.%d/%d = %d.%d.%d.%d/%d" 157 , INT4_CHARS(a->addr), bitmask2masklen(a->mask) 158 , INT4_CHARS(b->addr), bitmask2masklen(b->mask) 159 , INT4_CHARS(a->addr), bitmask2masklen(m0) 160 ); 161 #endif 162 a->mask = m0; 163 (*cur)->next = b->next; 164 in_addr_range_destroy(b); 165 } 166 goto restart; 167 } 168 } 169 } 170 171 int 172 in_addr_range_list_includes(struct in_addr_range **list, struct in_addr *addr) 173 { 174 struct in_addr_range *cur; 175 u_int32_t addr0 = ntohl(addr->s_addr); 176 177 for (cur = *list; cur != NULL; cur = cur->next) { 178 if ((addr0 & cur->mask) == (cur->addr & cur->mask)) 179 return 1; 180 } 181 return 0; 182 } 183 184 static int 185 in_addr_range_list_add0(struct in_addr_range **list, u_int32_t addr, 186 u_int32_t mask) 187 { 188 struct in_addr_range *ent; 189 struct in_addr_range **cur; 190 191 if ((ent = in_addr_range_create()) == NULL) 192 return -1; 193 ent->addr = addr; 194 ent->mask = mask; 195 196 for (cur = list; *cur != NULL; cur = &(*cur)->next) { 197 if ((ent->addr & (*cur)->mask) == 198 ((*cur)->addr & (*cur)->mask)) { 199 in_addr_range_destroy(ent); 200 in_addr_range_list_uniq(list); 201 return 0; 202 } 203 if ((ent->addr & ent->mask) == ((*cur)->addr & ent->mask)) { 204 ent->next = (*cur)->next; 205 free(*cur); 206 *cur = ent; 207 in_addr_range_list_uniq(list); 208 return 0; 209 } 210 if ((*cur)->addr > ent->addr) 211 break; 212 } 213 if (cur != NULL) { 214 ent->next = *cur; 215 *cur = ent; 216 in_addr_range_list_uniq(list); 217 } 218 return 0; 219 } 220 221 int 222 in_addr_range_list_add(struct in_addr_range **list, const char *str) 223 { 224 int is_range = 0, is_masklen = 0, is_maskaddr = 0, mask; 225 char *p0, *p1; 226 struct in_addr a0, a1; 227 u_int32_t i0, i1; 228 229 if ((p0 = strdup(str)) == NULL) { 230 #ifdef IIJDEBUG 231 saved_errno = errno; 232 log_printf(LOG_DL_1, "malloc() failes: %m"); 233 errno = saved_errno; 234 #endif 235 return -1; 236 } 237 if ((p1 = strchr(p0, '-')) != NULL) { 238 *p1++ = '\0'; 239 is_range = 1; 240 } else if ((p1 = strchr(p0, '/')) != NULL) { 241 *p1++ = '\0'; 242 243 if (sscanf(p1, "%d", &mask) != 1) { 244 #ifdef IIJDEBUG 245 saved_errno = errno; 246 log_printf(LOG_DL_1, "sscanf(%s) failes: %m", 247 p1); 248 errno = saved_errno; 249 #endif 250 free(p0); 251 return -1; 252 } 253 if (mask < 0 || 32 < mask) { 254 #ifdef IIJDEBUG 255 log_printf(LOG_DL_1, "must be 0 <= masklen <= 32: %d", 256 mask); 257 errno = EINVAL; 258 #endif 259 free(p0); 260 return -1; 261 } 262 is_masklen = 1; 263 } else if ((p1 = strchr(p0, ':')) != NULL) { 264 *p1++ = '\0'; 265 is_maskaddr = 1; 266 } 267 268 if (inet_aton(p0, &a0) != 1) { 269 if (errno == 0) 270 errno = EINVAL; 271 #ifdef IIJDEBUG 272 saved_errno = errno; 273 log_printf(LOG_DL_1, "inet_aton(%s) failes: %m", p0); 274 errno = saved_errno; 275 #endif 276 free(p0); 277 return -1; 278 } 279 if ((is_range || is_maskaddr) && inet_aton(p1, &a1) != 1) { 280 if (errno == 0) 281 errno = EINVAL; 282 #ifdef IIJDEBUG 283 saved_errno = errno; 284 log_printf(LOG_DL_1, "inet_aton(%s) failes: %m", p1); 285 errno = saved_errno; 286 #endif 287 free(p0); 288 return -1; 289 } 290 free(p0); 291 if (is_range) { 292 i0 = ntohl(a0.s_addr); 293 i1 = ntohl(a1.s_addr); 294 for (; i0 <= i1; i0++) 295 in_addr_range_list_add0(list, i0, 0xffffffff); 296 } else if (is_masklen) { 297 i0 = ntohl(a0.s_addr); 298 if (mask == 0) 299 i1 = 0x0; 300 else 301 i1 = 0xffffffffL << (32 - mask); 302 if ((i0 & i1) != i0) { 303 #ifdef IIJDEBUG 304 log_printf(LOG_DL_1, "invalid addr/mask pair: " 305 "%d.%d.%d.%d/%d", 306 INT4_CHARS(i0), bitmask2masklen(i1)); 307 #endif 308 errno = EINVAL; 309 return -1; 310 } 311 in_addr_range_list_add0(list, i0, i1); 312 } else if (is_maskaddr) { 313 i0 = ntohl(a0.s_addr); 314 i1 = ntohl(a1.s_addr); 315 if ((i0 & i1) != i0 || bitmask2masklen(i1) < 0) { 316 #ifdef IIJDEBUG 317 log_printf(LOG_DL_1, "invalid addr/mask pair: " 318 "%d.%d.%d.%d/%d", 319 INT4_CHARS(i0), bitmask2masklen(i1)); 320 #endif 321 errno = EINVAL; 322 return -1; 323 } 324 in_addr_range_list_add0(list, i0, i1); 325 } else { 326 i0 = ntohl(a0.s_addr); 327 in_addr_range_list_add0(list, i0, 0xffffffff); 328 } 329 330 return 0; 331 } 332 333 static int bitmask2masklen(mask) 334 u_int32_t mask; 335 { 336 switch(mask) { 337 case 0x00000000: return 0; 338 case 0x80000000: return 1; 339 case 0xC0000000: return 2; 340 case 0xE0000000: return 3; 341 case 0xF0000000: return 4; 342 case 0xF8000000: return 5; 343 case 0xFC000000: return 6; 344 case 0xFE000000: return 7; 345 case 0xFF000000: return 8; 346 case 0xFF800000: return 9; 347 case 0xFFC00000: return 10; 348 case 0xFFE00000: return 11; 349 case 0xFFF00000: return 12; 350 case 0xFFF80000: return 13; 351 case 0xFFFC0000: return 14; 352 case 0xFFFE0000: return 15; 353 case 0xFFFF0000: return 16; 354 case 0xFFFF8000: return 17; 355 case 0xFFFFC000: return 18; 356 case 0xFFFFE000: return 19; 357 case 0xFFFFF000: return 20; 358 case 0xFFFFF800: return 21; 359 case 0xFFFFFC00: return 22; 360 case 0xFFFFFE00: return 23; 361 case 0xFFFFFF00: return 24; 362 case 0xFFFFFF80: return 25; 363 case 0xFFFFFFC0: return 26; 364 case 0xFFFFFFE0: return 27; 365 case 0xFFFFFFF0: return 28; 366 case 0xFFFFFFF8: return 29; 367 case 0xFFFFFFFC: return 30; 368 case 0xFFFFFFFE: return 31; 369 case 0xFFFFFFFF: return 32; 370 } 371 return -1; 372 } 373 374 #ifdef ADDR_RANGE_DEBUG 375 #include <unistd.h> 376 377 static void usage __P ((void)); 378 379 static void 380 usage() 381 { 382 fprintf(stderr, 383 "usage: addr_range [-d] [addr_exp]...\n" 384 "\taddr_exp: 192.168.0.1 (equals 192.168.0.1/32) or \n" 385 "\t 192.168.32.1-192.168.32.255 or \n" 386 "\t 192.168.4.0:255.255.254.0 or \n" 387 "\t 10.0.0.1/24\n" 388 ); 389 } 390 391 int 392 main(int argc, char *argv[]) 393 { 394 int i, ch; 395 struct in_addr_range *list = NULL, *cur; 396 397 debugfp = stderr; 398 debuglevel = 0; 399 400 while ((ch = getopt(argc, argv, "d")) != -1) { 401 switch (ch) { 402 case 'd': 403 debuglevel++; 404 break; 405 default: 406 usage(); 407 exit(1); 408 } 409 } 410 411 argc -= optind; 412 argv += optind; 413 414 if (argc <= 0) { 415 usage(); 416 exit(1); 417 } 418 419 for (i = 0; i < argc; i++) { 420 if (in_addr_range_list_add(&list, argv[i])) { 421 perror(argv[i]); 422 } 423 } 424 for (cur = list; cur != NULL; cur = cur->next) { 425 fprintf(stderr, "%d.%d.%d.%d/%d\n" 426 , INT4_CHARS(cur->addr), bitmask2masklen(cur->mask) 427 ); 428 } 429 in_addr_range_list_remove_all(&list); 430 431 return 0; 432 } 433 #endif 434