├── README ├── cidr-convert.c └── cidr-convert.go /README: -------------------------------------------------------------------------------- 1 | CIDR block calculator. 2 | 3 | Takes a set of dotted-quad IP addresses, ranges, or CIDR blocks, on 4 | stdin; at EOF, prints a minimal set of CIDR ranges to stdout. 5 | 6 | Input consists of a stream of dotted-quads, pairs of dotted-quads 7 | separated by a dash, or dotted-quads with /number widths after 8 | them. Dash-separated ranges refer to all addresses between the 9 | two, inclusive; an address with a /number after it refers to a 10 | CIDR-style block. It is not an error for an address to be 11 | specified in the input more than once. Whitespace may appear 12 | anywhere except within a dotted-quad or CIDR width. Characters 13 | other than digits, dots, dashes, and whitespace are errors. If a 14 | number in a dotted-quad is greater than 255, or a CIDR width is 15 | greater than 32, or other syntax errors occur (such as too many 16 | dots without whitespace, dash, or slash) a complaint is printed and 17 | the dotted-quad, range, or block in which it appears is skipped. 18 | 19 | Output consists of zero or more lines, each a dotted-quad/width CIDR 20 | net-with-mask, including all and only the addresses in the input. 21 | It will be a minimal set, in that no two blocks in the output can 22 | be collapsed without resorting to noncontiguous netmasks. 23 | 24 | Compile-time options: 25 | 26 | -DNO_PROGNAME 27 | Provide __progname, for systems that don't have it. If 28 | it shows up undefined at link time, try compiling with 29 | this turned on. 30 | 31 | This file is in the public domain. 32 | 33 | Originally obtained from: http://www.spamshield.org/cidr-convert.c 34 | 35 | Added to github on 02-01-2012 initially to fix a simple display bug for initial 36 | quads over 127. The goal is to allow for collaborative tweaking and development 37 | of this useful tool. 38 | 39 | As of 05-27-2015, I have ported the tool to Go as well. Mostly as a learning 40 | exercise. Build with 'go build cidr-convert.go' to prevent the compiler from 41 | getting confused. 42 | -------------------------------------------------------------------------------- /cidr-convert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CIDR block calculator. 3 | * 4 | * Takes a set of dotted-quad IP addresses, ranges, or CIDR blocks, on 5 | * stdin; at EOF, prints a minimal set of CIDR ranges to stdout. 6 | * 7 | * Input consists of a stream of dotted-quads, pairs of dotted-quads 8 | * separated by a dash, or dotted-quads with /number widths after 9 | * them. Dash-separated ranges refer to all addresses between the 10 | * two, inclusive; an address with a /number after it refers to a 11 | * CIDR-style block. It is not an error for an address to be 12 | * specified in the input more than once. Whitespace may appear 13 | * anywhere except within a dotted-quad or CIDR width. Characters 14 | * other than digits, dots, dashes, and whitespace are errors. If a 15 | * number in a dotted-quad is greater than 255, or a CIDR width is 16 | * greater than 32, or other syntax errors occur (such as too many 17 | * dots without whitespace, dash, or slash) a complaint is printed and 18 | * the dotted-quad, range, or block in which it appears is skipped. 19 | * 20 | * Output consists of zero or more lines, each a dotted-quad/width CIDR 21 | * net-with-mask, including all and only the addresses in the input. 22 | * It will be a minimal set, in that no two blocks in the output can 23 | * be collapsed without resorting to noncontiguous netmasks. 24 | * 25 | * Compile-time options: 26 | * 27 | * -DNO_PROGNAME 28 | * Provide __progname, for systems that don't have it. If 29 | * it shows up undefined at link time, try compiling with 30 | * this turned on. 31 | * 32 | * This file is in the public domain. 33 | */ 34 | 35 | #include 36 | #include 37 | 38 | extern const char *__progname; 39 | #ifdef NO_PROGNAME 40 | const char *__progname; 41 | int main(int, char **); 42 | int main_(int, char **); 43 | int main(int ac, char **av) { __progname = av[0]; return(main_()); } 44 | #define main main_ 45 | #endif 46 | 47 | /* 48 | * We store the input as a binary tree. Conceptually, the tree is a 49 | * fully-populated depth-32 binary tree, with each leaf marked as 50 | * either present or absent in the input. Of course, that's a totally 51 | * impractical representation. What we actually do is to store that 52 | * tree, but whenever a subtree has all its leaves absent, the pointer 53 | * that would normally point to it is replaced with a NONE pointer; if 54 | * a subtree has all its leaves present, an ALL pointer. (Leaf 55 | * pointers are always either NONE or ALL, according as the leaf in 56 | * question is absent or present.) 57 | * 58 | * This could probably be stored more efficiently by allowing a single 59 | * C structure to represent multiple levels in the tree when there's 60 | * only one non-NONE path down through those levels, but the 61 | * additional code complexity isn't worth it. 62 | * 63 | * Since we don't have to store "up" pointers, we don't, and a node 64 | * consists of nothing but two child pointers. We use an array[2] 65 | * rather than two separate struct elements because at one place it's 66 | * convenient to use a computed index, which would otherwise need to 67 | * be a ? : expression. 68 | * 69 | * The reason for choosing this data structure is that it makes 70 | * extracting CIDR netblocks - our desired output - trivial. All we 71 | * have to do is collapse every node with two ALL children into an ALL 72 | * node itself; when this process can go no farther, an optimal CIDR 73 | * set consists of the address/mask values corresponding to the ALL 74 | * nodes. (We actually do the collapsing as we build the tree, rather 75 | * than deferring it until everything's done.) 76 | * 77 | * We could use a nil pointer for NONE or ALL, but don't, if only for 78 | * error checking. 79 | */ 80 | 81 | typedef struct node NODE; 82 | 83 | struct node { 84 | NODE *sub[2]; 85 | } ; 86 | 87 | /* The root of the tree. */ 88 | static NODE *root; 89 | 90 | /* 91 | * The NONE and ALL pointers. I know of no portable way of generating 92 | * distinctive NODE * values without actually allocating NODEs for 93 | * them to point to, except for the nil pointer. Fortunately, NODEs 94 | * are small enough that statically allocating two isn't a big deal. 95 | */ 96 | static NODE none; 97 | #define NONE (&none) 98 | static NODE all; 99 | #define ALL (&all) 100 | 101 | /* 102 | * Free a node and all nodes under it. Useful when we're setting ALL 103 | * at a point relatively far up in the tree (which happens if a range 104 | * or block subsumes some already-entered individual addresses). 105 | */ 106 | static void free_tree(NODE *n) 107 | { 108 | if ((n == NONE) || (n == ALL)) return; 109 | free_tree(n->sub[0]); 110 | free_tree(n->sub[1]); 111 | free(n); 112 | } 113 | 114 | /* 115 | * Add an address to a node. Conceptually, you pass a node to this 116 | * routine. But since it may want to replace the node with ALL, it 117 | * needs to actually be passed an additional level of pointer, NODE ** 118 | * instead of NODE *. This has the convenient property that this 119 | * routine can also handle replacing NONE nodes with real nodes. a is 120 | * the address being added. bit says how far down in the tree this 121 | * node is, or more accurately how far up; 31 corresponds to the root, 122 | * 0 to the last level of internal nodes, and -1 to leaves. end is a 123 | * value which describes how large a block is being added; it is -1 to 124 | * add a single leaf (a /32), 0 to add a pair of addresses (a /31), 125 | * etc. 126 | * 127 | * Algorithm: Recursive. If the node is already ALL, everything we 128 | * want to add is already present, so do nothing. Otherwise, if we've 129 | * reached the level at which we want to operate (bit <= end), free 130 | * the subtree if it's not NONE (it can't be ALL; we already checked) 131 | * and replace it with ALL, and we're done. Otherwise, we have to 132 | * walk down either the 0 link or the 1 link. If this node is 133 | * presently NONE, we have to create a real node; then we recurse down 134 | * whichever branch of the tree corresponds to the appropriate bit of 135 | * a. After adding, we check, and if both our subtrees are ALL, we 136 | * collapse this node into an ALL. (If further collapsing is possible 137 | * at the next level up, our caller will take care of it.) 138 | */ 139 | static void add_to_node(NODE **np, unsigned long int a, int bit, int end) 140 | { 141 | NODE *n; 142 | 143 | n = *np; 144 | if (n == ALL) return; 145 | if (bit <= end) 146 | { if (n != NONE) free_tree(n); 147 | *np = ALL; 148 | return; 149 | } 150 | if (n == NONE) 151 | { n = malloc(sizeof(NODE)); 152 | n->sub[0] = NONE; 153 | n->sub[1] = NONE; 154 | *np = n; 155 | } 156 | add_to_node(&n->sub[(a>>bit)&1],a,bit-1,end); 157 | if ((n->sub[0] == ALL) && (n->sub[1] == ALL)) 158 | { free(n); 159 | *np = ALL; 160 | } 161 | } 162 | 163 | /* 164 | * Dump output. This dumps out whatever output is appropriate for a 165 | * given NODE. If the node is NONE, there's nothing under it, so 166 | * don't do anything. If it's ALL, we've found a CIDR block; print it 167 | * and return. Otherwise, we recurse, first down the 0 branch, then 168 | * the 1 branch. v is the address-so-far, maintained as part of the 169 | * recursive calls. 170 | * 171 | * The abort() is a can't-happen; it indicates that we have a node 172 | * that's not NONE or ALL at the bottom level of the tree, which is 173 | * supposed to hold only leaves. 174 | */ 175 | static void dump_tree(NODE *n, unsigned long int v, int bit) 176 | { 177 | if (n == NONE) return; 178 | if (n == ALL) 179 | { printf("%lu.%lu.%lu.%lu/%d\n",v>>24&0xff,(v>>16)&0xff,(v>>8)&0xff,v&0xff,31-bit); 180 | return; 181 | } 182 | if (bit < 0) abort(); 183 | dump_tree(n->sub[0],v,bit-1); 184 | dump_tree(n->sub[1],v|(1< a2) 212 | { fprintf(stderr,"%s: invalid range (ends reversed)\n",__progname); 213 | return; 214 | } 215 | while (a1 <= a2) 216 | { m = (a1 - 1) & ~a1; 217 | while (a1+m > a2) m >>= 1; 218 | for (bit=-1,t=m;t;bit++,t>>=1) ; 219 | add_to_node(&root,a1,31,bit); 220 | a1 += m+1; 221 | } 222 | } 223 | 224 | /* 225 | * Add a CIDR-style block. This matches our storage method so well 226 | * it's just a single call to add_to_node. The reason for the ?: 227 | * operator is that C doesn't promise that << by 32 actually shifts; 228 | * 32-bit machines often use only the low five bits of the shift 229 | * count. 230 | */ 231 | static void save_cidr(unsigned long int a, int n) 232 | { 233 | add_to_node(&root,n?a&0xffffffff&(0xffffffff<<(32-n)):0,31,31-n); 234 | } 235 | 236 | /* 237 | * Read input. Implementation is a simple state machine. 238 | * 239 | * State values for the various input syntaxes (a=10, b=11, etc): 240 | * 241 | * input 1 2 3 . 4 5 . 6 7 . 8 9 1 2 3 . 4 5 ... 242 | * state 1 1 2 2 2 3 4 4 5 6 6 7 8 8 9 9 9 2 2 2 3 4 4 ... 243 | * 244 | * input 1 2 3 . 4 5 . 6 7 . 8 9 - 1 1 . 2 2 . 3 3 . 4 4 ... 245 | * state 1 1 2 2 2 3 4 4 5 6 6 7 8 8 9 a a b b c d d e f f g h h 1 1 ... 246 | * 247 | * input 1 2 3 . 4 5 . 6 7 . 8 9 / 1 5 ... 248 | * state 1 1 2 2 2 3 4 4 5 6 6 7 8 8 9 i i j j 1 1 ... 249 | * 250 | * a holds the address being constructed (or, for states 9, i, j, the 251 | * address just constructed); n holds the number being accumulated. 252 | * a1 is used to hold the first address when a range is being read 253 | * (the second address is accumulated into a). 254 | * 255 | * If an error occurs, n is set to -1, and further errors are 256 | * suppressed; we stay this way until we begin a new dotted-quad, by 257 | * entering state 2 from state 9 or by entering state 1 upon seeing 258 | * whitespace in most other states. 259 | * 260 | * The "default: abort();" cases are can't-happen firewalls. 261 | */ 262 | static void read_input(void) 263 | { 264 | unsigned long int a1; 265 | unsigned long int a; 266 | int line; 267 | int n; 268 | int c; 269 | int state; 270 | 271 | state = 1; 272 | line = 1; 273 | n = 0; 274 | while (1) 275 | { c = getchar(); 276 | if (c == EOF) break; 277 | switch (c) 278 | { case '0': case '1': case '2': case '3': case '4': 279 | case '5': case '6': case '7': case '8': case '9': 280 | switch (state) 281 | { default: 282 | abort(); 283 | break; 284 | case 1: 285 | n = c - '0'; 286 | state = 2; 287 | break; 288 | case 3: 289 | case 5: 290 | case 7: 291 | case 12: 292 | case 14: 293 | case 16: 294 | state ++; 295 | case 2: 296 | case 4: 297 | case 6: 298 | case 8: 299 | case 11: 300 | case 13: 301 | case 15: 302 | case 17: 303 | if (n < 0) break; 304 | n = (n * 10) + (c - '0'); 305 | if (n > 255) 306 | { fprintf(stderr,"%s: line %d: out-of-range number in input\n",__progname,line); 307 | n = -1; 308 | } 309 | break; 310 | case 9: 311 | if (n >= 0) 312 | { save_one_addr(a); 313 | n = c - '0'; 314 | } 315 | state = 2; 316 | break; 317 | case 10: 318 | case 18: 319 | if (n >= 0) n = c - '0'; 320 | state ++; 321 | break; 322 | case 19: 323 | if (n < 0) break; 324 | n = (n * 10) + (c - '0'); 325 | if (n > 32) 326 | { fprintf(stderr,"%s: line %d: out-of-range width in input\n",__progname,line); 327 | n = -1; 328 | } 329 | break; 330 | } 331 | break; 332 | case '.': 333 | switch (state) 334 | { default: 335 | abort(); 336 | break; 337 | case 1: 338 | case 3: 339 | case 5: 340 | case 7 ... 8: 341 | case 10: 342 | case 12: 343 | case 14: 344 | case 16 ... 19: 345 | if (n >= 0) fprintf(stderr,"%s: line %d: . at an inappropriate place\n",__progname,line); 346 | n = -1; 347 | break; 348 | case 2: 349 | case 11: 350 | a = 0; 351 | case 4: 352 | case 6: 353 | case 13: 354 | case 15: 355 | if (n >= 0) 356 | { a = (a << 8) | n; 357 | n = 0; 358 | } 359 | state ++; 360 | break; 361 | case 9: 362 | if (n >= 0) 363 | { save_one_addr(a); 364 | fprintf(stderr,"%s: line %d: . at an inappropriate place\n",__progname,line); 365 | } 366 | n = -1; 367 | break; 368 | } 369 | break; 370 | case '-': 371 | switch (state) 372 | { default: 373 | abort(); 374 | break; 375 | case 1 ... 7: 376 | case 10 ... 19: 377 | fprintf(stderr,"%s: line %d: - at an inappropriate place\n",__progname,line); 378 | n = -1; 379 | break; 380 | case 8: 381 | if (n >= 0) a1 = (a << 8) | n; 382 | state = 10; 383 | break; 384 | case 9: 385 | a1 = a; 386 | state = 10; 387 | break; 388 | } 389 | break; 390 | case '/': 391 | switch (state) 392 | { default: 393 | abort(); 394 | break; 395 | case 1 ... 7: 396 | case 10 ... 19: 397 | fprintf(stderr,"%s: line %d: / at an inappropriate place\n",__progname,line); 398 | n = -1; 399 | break; 400 | case 8: 401 | if (n >= 0) a = (a << 8) | n; 402 | state = 18; 403 | break; 404 | case 9: 405 | state = 18; 406 | break; 407 | } 408 | break; 409 | case '\n': 410 | line ++; 411 | case ' ': case '\t': case '\r': 412 | switch (state) 413 | { default: 414 | abort(); 415 | break; 416 | case 1: 417 | case 9 ... 10: 418 | case 18: 419 | break; 420 | case 2 ... 7: 421 | case 11 ... 16: 422 | if (n >= 0) fprintf(stderr,"%s: line %d: whitespace at an inappropriate place\n",__progname,line); 423 | state = 1; 424 | break; 425 | case 8: 426 | if (n >= 0) a = (a << 8) | n; 427 | state = 9; 428 | break; 429 | case 17: 430 | if (n >= 0) save_range(a1,(a<<8)|n); 431 | state = 1; 432 | break; 433 | case 19: 434 | if (n >= 0) save_cidr(a,n); 435 | state = 1; 436 | break; 437 | } 438 | break; 439 | default: 440 | fprintf(stderr,"%s: invalid character 0x%02x in input\n",__progname,c); 441 | n = -1; 442 | state = 2; 443 | break; 444 | } 445 | } 446 | switch (state) 447 | { default: 448 | abort(); 449 | break; 450 | case 1: 451 | break; 452 | case 2 ... 7: 453 | case 10 ... 16: 454 | if (n >= 0) fprintf(stderr,"%s: line %d: EOF at an inappropriate place\n",__progname,line); 455 | break; 456 | case 8: 457 | if (n >= 0) save_one_addr((a<<8)|n); 458 | break; 459 | case 9: 460 | if (n >= 0) save_one_addr(a); 461 | break; 462 | case 17: 463 | if (n >= 0) save_range(a1,(a<<8)|n); 464 | break; 465 | } 466 | } 467 | 468 | /* 469 | * After accumulating all input, dump out the resulting CIDR blocks. 470 | * Because we collapse when possible during tree construction, there 471 | * is nothing to do here but call dump_tree to walk the tree and print 472 | * a line when it finds an ALL node. 473 | */ 474 | static void dump_output(void) 475 | { 476 | dump_tree(root,0,31); 477 | } 478 | 479 | /* 480 | * By this point, main() is pretty much trivial. 481 | */ 482 | int main(void); 483 | int main(void) 484 | { 485 | root = NONE; 486 | read_input(); 487 | dump_output(); 488 | exit(0); 489 | } 490 | 491 | -------------------------------------------------------------------------------- /cidr-convert.go: -------------------------------------------------------------------------------- 1 | // This file is in the public domain. 2 | package main 3 | 4 | import ( 5 | "bufio" 6 | "errors" 7 | "fmt" 8 | "net" 9 | "os" 10 | "regexp" 11 | "strconv" 12 | ) 13 | 14 | type node struct { 15 | sub [2]*node 16 | } 17 | 18 | var ( 19 | none = &node{} 20 | all = &node{} 21 | ) 22 | 23 | type Tree struct { 24 | root *node 25 | } 26 | 27 | func NewTree() *Tree { 28 | return &Tree{none} 29 | } 30 | 31 | func (t *Tree) insert_addr(a uint) { 32 | add_to_node(&t.root, a, 31, -1) 33 | } 34 | 35 | func (t *Tree) insert_range(start uint, end uint) { 36 | 37 | var ( 38 | bit int32 39 | m uint 40 | tt uint 41 | ) 42 | 43 | for start <= end { 44 | m = (start - 1) & ^start 45 | for start+m > end { 46 | m >>= 1 47 | } 48 | bit = -1 49 | tt = m 50 | for tt != 0 { 51 | bit++ 52 | tt >>= 1 53 | } 54 | add_to_node(&t.root, start, 31, bit) 55 | start += m + 1 56 | } 57 | } 58 | 59 | func (t *Tree) insert_CIDR(a uint, n int) { 60 | if n != 0 { 61 | a &= 0xffffffff & (0xffffffff << (32 - uint(n))) 62 | } else { 63 | a = 0 64 | } 65 | add_to_node(&t.root, a, 31, int32(31-n)) 66 | } 67 | 68 | func (t *Tree) DumpTree() { 69 | dump_tree(t.root, 0, 31) 70 | } 71 | 72 | func add_to_node(np **node, a uint, bit int32, end int32) { 73 | var n *node 74 | n = *np 75 | 76 | if n == all { 77 | return 78 | } 79 | if bit <= end { 80 | *np = all 81 | return 82 | } 83 | if n == none { 84 | n = &node{[2]*node{none, none}} 85 | *np = n 86 | } 87 | add_to_node(&n.sub[(a>>uint(bit))&1], a, bit-1, end) 88 | if n.sub[0] == all && n.sub[1] == all { 89 | *np = all 90 | } 91 | } 92 | 93 | func (t *Tree) FindNode(a uint) bool { 94 | n := t.root 95 | switch { 96 | case n == all: 97 | return true 98 | case n == none: 99 | return false 100 | } 101 | for bit := 31; bit >= 0; bit-- { //is >= correct? 102 | //some case where this is the exact node 103 | n = n.sub[(a>>uint(bit))&1] 104 | switch { 105 | case n == all: 106 | return true 107 | case n == none: 108 | return false 109 | } 110 | } 111 | return false 112 | } 113 | 114 | func dump_tree(n *node, v uint, bit uint) { 115 | switch { 116 | case n == none: 117 | return 118 | case n == all: 119 | fmt.Printf("%d.%d.%d.%d/%d\n", v>>24&0xff, (v>>16)&0xff, (v>>8)&0xff, v&0xff, 31-bit) 120 | return 121 | case bit < 0: 122 | panic("non-leaf node found at the bottom of the tree") 123 | } 124 | dump_tree(n.sub[0], v, bit-1) 125 | dump_tree(n.sub[1], v|(1<= ip2i { 160 | continue 161 | } 162 | t.insert_range(ip1i, ip2i) 163 | case matches[3] != "": 164 | // IP/CIDR 165 | ipi, err := ipstrToInt(matches[1]) 166 | if err != nil { 167 | continue 168 | } 169 | ci, err := strconv.ParseUint(matches[3], 10, 32) 170 | if err != nil { 171 | continue 172 | } 173 | t.insert_CIDR(ipi, int(ci)) 174 | default: 175 | // single IP 176 | ipi, err := ipstrToInt(matches[1]) 177 | if err != nil { 178 | continue 179 | } 180 | t.insert_addr(ipi) 181 | } 182 | } 183 | 184 | t.DumpTree() 185 | } 186 | --------------------------------------------------------------------------------