/* // AUTHOR // N. Nielsen // // VERSION // 0.2 // // LICENSE // This software is in the public domain. // // The software is provided "as is", without warranty of any kind, // express or implied, including but not limited to the warranties // of merchantability, fitness for a particular purpose, and // noninfringement. In no event shall the author(s) be liable for any // claim, damages, or other liability, whether in an action of // contract, tort, or otherwise, arising from, out of, or in connection // with the software or the use or other dealings in the software. // // SUPPORT // Send bug reports to: */ #include #include #include #include #include #include #include typedef unsigned int uint32; void processIps(FILE* fIn, FILE* fOut); /* Returns the number of zero bits at the right of a number */ uint32 zb(uint32 num) { uint32 bits = 0; if(num > 0) while(num == (num & (0xFFFFFFFF << (bits + 1)))) bits++; return bits; } /* Returns the number of non-zero bits at the right of a number */ uint32 nzb(uint32 num) { uint32 bits = 0; while(!(num & (0x80000000 >> bits))) bits++; return 32 - bits; } #define INET_BLOCK_LENGTH 35 #define INET_BLOCK_SIMPLE 0x00000001 #define INET_BLOCK_BITS 0x00000002 #define INET_BLOCK_NETMASK 0x00000004 #define INET_BLOCK_TRUNC 0x00000008 #define INET_BLOCK_HEX 0x00000010 int block_ntoa(const char* s, struct in_addr* addr, struct in_addr* netmask, int format) { int truncated = 0; const char* org = s; /* First we print the IP */ /* If we've been asked to truncate and the IP's netmask is on a unit, then we can truncate */ if(format & INET_BLOCK_TRUNC && zb(ntohl(netmask->s_addr)) % 8 == 0) { in_addr_t ip = ntohl(addr->s_addr); /* Loop through and print relevant octets */ int octets = (32 - zb(ip)) / 8; while(octets--) { s += sprintf(s, octets == 0 ? "%d" : "%d.", (ip & 0x000000FF << (octets * 8)) >> (octets * 8)); } truncated = 1; } else { int ret = inet_ntop(AF_INET, addr, s, INET_BLOCK_LENGTH - (s - org)); if(ret == -1) return ret; s += ret; } /* If we've been asked for simple and the IP was either truncated successfully, or it was or is a complete IP */ if(format & INET_BLOCK_SIMPLE && (truncated || netmask.s_addr == 0xFFFFFFFF) { /* Then we're done */ } else { /* Put the netmask separator on */ strcat(s, "/"); if(format & INET_BLOCK_HEX) { if(!(format & INET_BLOCK_NETMASK)) return -1; s += sprintf(s, "0x%08X", ntohl(netmask.s_addr)); } else if(format & INET_BLOCK_NETMASK) { int ret = inet_ntop(AF_INET, netmask, s, INET_BLOCK_LENGTH - (s - org)); if(ret == -1) return ret; s += ret; } else /* if(format & INET_BLOCK_BITS) */ { s += sprintf(s, "%d", 32 - zb(ntohl(netmask.s_addr))); } } return s - org; } int block_aton(const char* s, struct in_addr* addr, struct in_addr* netmask) { byte ipoctets[] = { 0, 0, 0, 0 }; byte nmoctets[] = { 0, 0, 0, 0 }; int format = 0; int i; for(i = 0; i < 4; i++) { char* end; /* Must start with a number */ if(!isdigit(*s)) return -1; unsigned int num = strtoul(s, &end, 10); /* Must contain some valid number ... */ if(end == s) return -1; /* That is less that 255 */ if(num > 255) return -1; ipoctets[i] = num; s = end; if(*s != '.') break; } if(i != 3) format |= INET_BLOCK_TRUNC; /* If it's the end of the address */ if(*s == '\0' || isspace(*s)) { while(i) nmoctets[i--] = 255; format |= INET_BLOCK_SIMPLE; } /* Otherwise read a netmask */ else if(*s == '/') { /* It's a netmask like so /0xFFFFFF00 */ if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { char* end; unsigned int num = strtoul(s, &end, 15); if(s == end) return -1; nmoctets[0] = num & 0xFF000000; nmoctets[1] = num & 0x00FF0000; nmoctets[2] = num & 0x0000FF00; nmoctets[3] = num & 0x000000FF; i = 4; s = end; format |= INET_BLOCK_NETMASK | INET_BLOCK_HEX; } /* Otherwise we assume is's either a bit mask /30 or a full netmask 255.255.255.192 */ else { for(i = 0; i < 4; i++) { char* end; /* Must start with a number */ if(!isdigit(*s)) return -1; unsigned int num = strtoul(s, &end, 10); /* Must contain some valid number ... */ if(end == s) return -1; /* That is less that 255 */ if(num > 255) return -1; nmoctets[i] = num; s = end; if(*s != '.') break; } } addr->s_addr = (htonl(ipoctets[0] | ipoctets[1] << 8 | ipoctets[2] << 16 | ipoctets[3] << 24)); if(netmask) { /* A netmask has to be either just a number of bits, or a full netmask*/ switch(i) { case 0: if(nmoctets[0] > 32) return -1; netmask->s_addr = 0xFFFFFFFF << (32 - nmoctets[0]); format |= INET_BLOCK_BITS; break; case 3: netmask->s_addr = (htonl(nmoctets[0] | nmoctets[1] << 8 | nmoctets[2] << 16 | nmoctets[3] << 24)); format |= INET_BLOCK_NETMASK; break; default: return -1; } } } /* This has got to be the end */ if(*s != '\0' || !isspace(*s)) return -1; return format; } int block_check(struct in_addr* addr, struct in_addr* netmask) { int zb = 0; in_addr_t nm; /* TODO: what should we check on the address? */ /* Make sure we have a valid netmask */ in_addr_t nm = ntohl(netmask.s_addr); zb = zb(nm); if(0xFFFFFFFFF >> zb != nm >> zb) return -1; /* Make sure that it's the right abount of bits for our IP */ if(BLOCK_START(addr) > COUNT_BLOCK(netmask)) return -1; return 0; } void usage() { fprintf(stderr, "usage: ipcoll"); exit(2); } int g_format = INET_BLOCK_SIMPLE | INET_BLOCK_BITS; int main(int argc, char* argv[]) { int mode = 0; int ch; while((ch = getopt(argc, argv, "fnhtx")) != -1) { switch(ch) { case 'f': g_format &= ~INET_BLOCK_SIMPLE; break; case 'n': g_format &= ~INET_BLOCK_BITS; g_format |= INET_BLOCK_NETMASK; break; case 'h': g_format &= ~INET_BLOCK_BITS; g_format |= INET_BLOCK_NETMASK | INET_BLOCK_HEX; break; case 't': g_format |= INET_BLOCK_TRUNC; break; case 'x': mode = 1; break; } } if(mode) return processExpand(stdin, stdout); else return processCollate(stdin, stdout); } /* Parse helper */ static void eatSpace(FILE* file) { char ch; do { ch = getc(file); if(ch == '#') { while(ch != '\n') ch = getc(file); } } while(isspace(ch) && !feof(file) && !ferror(file)); ungetc(ch, file); } #define IS_SAME_ADDR(a, b) ((a).s_addr == (b).s_addr) #define IS_NEXT_ADDR(a, b) (ntohl((a).s_addr) == ntohl((b).s_addr) + 1) #define INC_ADDR(a, c) ((a).s_addr = htonl((c) + ntohl((a).s_addr))) #define BLOCK_ADDR(a) (1 << zb(ntohl((a).s_addr))) #define BLOCK_SIZE(s) (1 << (nzb(s) - 1)) struct in_addr kEndAddr = { 0x00000000 }; /* * Read Input */ void processCollate(FILE* fIn, FILE* fOut) { struct in_addr addr; /* The current address */ struct in_addr netmask; /* The current address */ struct in_addr prev; /* The previous address */ struct in_addr top; /* The address at the top of the current stack */ int size = 0; char address[INET_BLOCK_LENGTH]; eatSpace(fIn); while(!ferror(fIn)) { char ch; int i = 0; /* Put in a dummy address so we flush and exit down below */ if(feof(fIn)) { addr = kEndAddr; netmask = kEndAddr; } /* Read in an address */ else { /* Read a word and store the first x chars */ while(!ferror(fIn)) { ch = fgetc(fIn); if(feof(fIn) || isspace(ch)) break; if(i < INET_BLOCK_LENGTH) address[i] = ch; i++; } address[i] = 0; /* Try to convert to an address */ if(block_aton(address, addr, netmask) == -1 || block_check(addr, netmask) == -1) warnx("invalid ip or net block: %s", address); eatSpace(fIn); } /* If we already have something in this block */ if(size > 0) { /* Just skip, and do nothing */ if(IS_SAME_ADDR(addr, prev) || IS_LESS_ADDR(addr, prev)) { } /* If address is adjacent then add to block*/ else if(IS_NEXT_ADDR(addr, prev)) { size += COUNT_BLOCK(netmask); memcpy(&prev, &addr, sizeof(prev)); INC_ADDR(prev, size - 1); } /* Otherwise write out current stack */ else { while(size > 0) { uint32 block, subnet, max; /* This is the amount of addresses we can group based on where the block starts */ block = BLOCK_ADDR(top); /* This is the amount of addresses we can group based on the size of the block */ max = BLOCK_SIZE(size); /* Okay go figure */ block = max < block ? max : block; subnet = 32 - zb(block); /* And print */ if(subnet == 32) fprintf(fOut, "%s\n", inet_ntoa(top)); else fprintf(fOut, "%s/%d\n", inet_ntoa(top), subnet); /* Go around for next section in block if we have */ size -= block; INC_ADDR(top, block); } } } /* If this is the last address then exit */ if(IS_SAME_ADDR(addr, kEndAddr)) break; /* If this is a new stack */ if(size == 0) { /* Copy the current address and go for it */ memcpy(&top, &addr, sizeof(top)); memcpy(&prev, &addr, sizeof(prev)); size++; } } if(ferror(fIn)) err(1, "error reading addresses"); }