1 /* netmask.c - a netmask generator
2  *
3  * Copyright (c) 1999  Robert Stone <talby@trap.mtview.ca.us>,
4  *                     Tom Lear <tom@trap.mtview.ca.us>
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19 
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <getopt.h>
25 #include <netdb.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "errors.h"
30 
31 struct addrmask {
32   u_int32_t neta;
33   u_int32_t mask;
34   struct addrmask *next;
35   struct addrmask *prev;
36 };
37 
38 struct option longopts[] = {
39   { "version",	0, 0, 'v' },
40   { "help",	0, 0, 'h' },
41   { "debug",	0, 0, 'd' },
42   { "standard",	0, 0, 's' },
43   { "cidr",	0, 0, 'c' },
44   { "cisco",	0, 0, 'i' },
45   { "range",	0, 0, 'r' },
46   { "hex",	0, 0, 'x' },
47   { "octal",	0, 0, 'o' },
48   { "binary",	0, 0, 'b' },
49   { "nodns",	0, 0, 'n' },
50   { "max",	1, 0, 'M' },
51   { "min",	1, 0, 'm' },
52   { NULL,	0, 0, 0   }
53 };
54 
55 typedef enum {
56   OUT_STD, OUT_CIDR, OUT_CISCO, OUT_RANGE, OUT_HEX, OUT_OCTAL, OUT_BINARY
57 } output_t;
58 
59 int spectoaml(char *, int);
60 int display(output_t);
61 int addtoaml(u_int32_t addr, u_int32_t mask);
62 static u_int32_t mspectou32(char *);
63 
64 char version[] = "netmask, version "VERSION;
65 char vversion[] = __DATE__" "__TIME__;
66 char usage[] = "Try `%s --help' for more information.\n";
67 char *progname = NULL;
68 static struct addrmask *aml;
69 
main(int argc,char * argv[])70 int main(int argc, char *argv[]) {
71   int optc, h = 0, v = 0, debug = 0, dns = 1, lose = 0;
72 //  u_int32_t min = ~0, max = 0;
73   output_t output = OUT_CIDR;
74 
75   progname = argv[0];
76   initerrors(progname, 0, 0); /* stderr, nostatus */
77   while((optc = getopt_long(argc, argv, "shoxdrvbincM:m:", longopts,
78     (int *) NULL)) != EOF) switch(optc) {
79    case 'h': h = 1;   break;
80    case 'v': v++;     break;
81    case 'n': dns = 0; break;
82 //   case 'M': max = mspectou32(optarg); break;
83 //   case 'm': min = mspectou32(optarg); break;
84    case 'd':
85     initerrors(NULL, -1, 1); /* showstatus */
86     debug = 1;
87     break;
88    case 's': output = OUT_STD;    break;
89    case 'c': output = OUT_CIDR;   break;
90    case 'i': output = OUT_CISCO;  break;
91    case 'r': output = OUT_RANGE;  break;
92    case 'x': output = OUT_HEX;    break;
93    case 'o': output = OUT_OCTAL;  break;
94    case 'b': output = OUT_BINARY; break;
95    default: lose = 1; break;
96   }
97   if(v) {
98     if(v == 1) fprintf(stderr, "%s\n", version);
99     else fprintf(stderr, "%s, %s\n", version, vversion);
100     if(!h) exit(0);
101   }
102   if(h) {
103     fprintf(stderr,
104       "This is netmask, an address netmask generation utility\n"
105       "Usage: %s spec [spec ...]\n"
106       "  -h, --help\t\t\tPrint a summary of the options\n"
107       "  -v, --version\t\t\tPrint the version number\n"
108       "  -d, --debug\t\t\tPrint status/progress information\n"
109       "  -s, --standard\t\tOutput address/netmask pairs\n"
110       "  -c, --cidr\t\t\tOutput CIDR format address lists\n"
111       "  -i, --cisco\t\t\tOutput Cisco style address lists\n"
112       "  -r, --range\t\t\tOutput ip address ranges\n"
113       "  -x, --hex\t\t\tOutput address/netmask pairs in hex\n"
114       "  -o, --octal\t\t\tOutput address/netmask pairs in octal\n"
115       "  -b, --binary\t\t\tOutput address/netmask pairs in binary\n"
116       "  -n, --nodns\t\t\tDisable DNS lookups for addresses\n"
117 //      "  -M, --max mask\t\tLimit maximum mask size\n"
118 //      "  -m, --min mask\t\tLimit minimum mask size (drop small ranges)\n"
119       "Definitions:\n"
120       "  a spec can be any of:\n"
121       "    address\n"
122       "    address:address\n"
123       "    address:+address\n"
124       "    address/mask\n"
125       "  an address can be any of:\n"
126       "    N\t\tdecimal number\n"
127       "    0N\t\toctal number\n"
128       "    0xN\t\thex number\n"
129       "    N.N.N.N\tdotted quad\n"
130       "    hostname\tdns domain name\n"
131       "  a mask is the number of bits set to one from the left\n", progname);
132     exit(0);
133   }
134   if(lose || optind == argc) {
135     fprintf(stderr, usage, progname);
136     exit(1);
137   }
138   while(optind < argc) spectoaml(argv[optind++], dns);
139   display(output);
140   return(0);
141 }
142 
143 /**************************************
144  * PART I - Read input                *
145  **************************************/
146 
147 static u_int32_t aspectou32(char *, int);
148 static int       rangetoaml(u_int32_t, u_int32_t);
149 static int       strtou32(u_int32_t *, char *);
150 #ifndef HAVE_STRTOUL
151 static u_int32_t strtoul(const char *nptr, char **endptr, int base);
152 #endif
153 
154 /* spectoaml adds a spec to the aml.
155  * deals with:
156  * "address"
157  * "address:address"
158  * "address:+address"
159  * "address/mask" */
spectoaml(char * addrspec,int dns)160 int spectoaml(char *addrspec, int dns) {
161   char *sep;
162   u_int32_t addr;
163 
164   if((sep = strchr(addrspec, ':')) != NULL) {		/* range */
165     u_int32_t addr2;
166 
167     *sep++ = '\0';
168     addr = aspectou32(addrspec, dns);
169     if(*sep == '+') {
170       sep++;
171       addr2 = addr;
172     } else addr2 = 0;
173     addr2 += aspectou32(sep, dns);
174     rangetoaml(addr, addr2);
175   } else if((sep = strchr(addrspec, '/')) != NULL) {	/* mask */
176     u_int32_t mask;
177 
178     *sep++ = '\0';
179     addr = aspectou32(addrspec, dns);
180     mask = mspectou32(sep);
181     addtoaml(addr, mask);
182   } else {						/* host */
183     addr = aspectou32(addrspec, dns);
184     addtoaml(addr, ~0);
185   }
186   return(0);
187 }
188 /* aspectou32 should convert the address portion of a spec...
189  * "base10"
190  * "0base8"
191  * "0xbase16"
192  * "hostname"
193  * "address" */
aspectou32(char * astr,int dns)194 static u_int32_t aspectou32(char *astr, int dns) {
195   u_int32_t addr;
196   struct hostent *h;
197   struct in_addr s;
198 
199   if(strtou32(&addr, astr));
200   else if(dns  && ((h = gethostbyname(astr)) != NULL))
201     addr = ntohl(*((u_int32_t *)h->h_addr_list[0]));
202   else if(inet_aton(astr, &s))
203     addr = ntohl(s.s_addr);
204   else panic("unable to parse \"%s\"", astr);
205   return(addr);
206 }
207 /* mspectou32 should convert the mask portion of a spec...
208  * "base10"
209  * "0base8"
210  * "0xbase16"
211  * "address"
212  * "~base10"
213  * "~0base8"
214  * "~0xbase16"
215  * "~address" */
mspectou32(char * istr)216 static u_int32_t mspectou32(char *istr) {
217   u_int32_t mask = 0, num;
218   char *mstr = istr;
219   struct in_addr s;
220   int cisco = 0;
221 
222   if(mstr[0] == '~') { /* prefix for cisco style bit inversion */
223     cisco = 1;
224     mstr++;
225   }
226   if(strtou32(&num, mstr)) {
227     if(cisco)          { mask = ~num; }
228     else if(num <= 32) { mask = ~0 << (32 - num); }
229     else               { mask = num; }
230   } else if(inet_aton(mstr, &s)) {
231     mask = ntohl(s.s_addr);
232     if(cisco) mask = ~mask;
233   } else panic("unable to parse \"%s\"", mstr);
234   for(num = ~mask; num & 1; num >>= 1);
235   if(num) panic("invalid mask 0x%08x from \"%s\"", mask, istr);
236   return(mask);
237 }
238 /* strtou32 shuld take an int pointer and a string looking like...
239  *   "base10"
240  *   "0base8"
241  *   "0xbase16"
242  * and write the value as an unsigned 32 bit int
243  * returning true on success and false on failure */
strtou32(u_int32_t * num,char * istr)244 static int strtou32(u_int32_t *num, char *istr) {
245   u_int32_t value, base;
246   char *str, *endp;
247 
248   if(istr == NULL || *istr == '\0') return(0);
249   if(istr[0] == '0' && istr[1]) {
250     if(istr[1] == 'x' && istr[2]) {
251       base = 16;
252       str = istr + 2;
253     } else {
254       base = 8;
255       str = istr + 1;
256     }
257   } else {
258     base = 10;
259     str = istr;
260   }
261   value = strtoul(str, &endp, base);
262   if(*endp != '\0') return(0);
263   *num = value;
264   return(1);
265 }
266 /* range to aml should take two addresses
267  * and add the shortest list of address/mask pairs between them */
rangetoaml(u_int32_t low,u_int32_t high)268 static int rangetoaml(u_int32_t low, u_int32_t high) {
269   u_int32_t i, j, lxh;
270 
271   if(low > high) {
272   	i = low;
273   	low = high;
274   	high = i;
275   }
276   for(lxh = i = low ^ high; i & 1; i >>= 1);
277   if(i == 0 && (low | lxh) == high) addtoaml(low, ~(high - low));
278   else {
279     for(i = lxh, j = 0; i >> 1; j++) i >>= 1;
280     i <<= j;
281     i = ~(i - 1) & high;
282     rangetoaml(low, i - 1);
283     rangetoaml(i, high);
284   }
285   return(0);
286 }
287 #ifndef HAVE_STRTOUL
288 #warning no ISO 9899 strtoul()? enabling sub-optimal workaround.
strtoul(const char * nptr,char ** endptr,int base)289 static u_int32_t strtoul(const char *nptr, char **endptr, int base) {
290   char *fmt;
291   u_int32_t val;
292 
293   switch(base) {
294    case 8:  fmt = "%lo"; break;
295    case 10: fmt = "%lu"; break;
296    case 16: fmt = "%lx"; break;
297   }
298   if(sscanf(nptr, fmt, &val) > 0) *endptr = "";
299   else *endptr = nptr;
300   return(val);
301 }
302 #endif
303 
304 /**************************************
305  * PART II - List management          *
306  **************************************/
307 
308 static int optimize(void);
309 
310 /* addtoaml takes an address and mask
311  * and adds it to the list
312  * note: it's a little bigger than it needs to be,
313  * but it's very modest about memory use
314  * and efficient enough considering it's use in optimize() */
addtoaml(u_int32_t addr,u_int32_t mask)315 int addtoaml(u_int32_t addr, u_int32_t mask) {
316   int neta = addr & mask;
317   char first = 0;
318   struct addrmask *cur = aml, *lst = NULL,
319                   *src = NULL, *dst = NULL, *old = NULL;
320 
321   if(aml == NULL || aml->mask > mask ||
322     (aml->mask == mask && aml->neta > neta)) first = 1;
323   else {
324     for(cur = aml; cur && (cur->mask < mask ||
325       (cur->mask == mask && cur->neta <= neta)); lst = cur, cur = cur->next)
326       if(cur->neta == (neta & cur->mask)) {
327         status("skip %08x/%08x (%08x/%08x exists)",
328           neta, mask, cur->neta, cur->mask);
329         return(0);
330       }
331     dst = lst;
332   }
333   while(cur) {
334     if(neta == (cur->neta & mask)) {
335       status("pull %08x/%08x (%08x/%08x added)",
336         cur->neta, cur->mask, neta, mask);
337       old = cur;
338       cur = cur->next;
339       if(old->prev) old->prev->next = old->next;
340       if(old->next) old->next->prev = old->prev;
341       if(aml == old) aml = cur; // suggested by Erik Gavert <erik@jsdata.se>
342       if(!old->prev && !old->next) {
343         aml = cur = NULL;
344         first = 1;
345       }
346       if(!src) src = old;
347       else free(old);
348     } else cur = cur->next;
349   }
350   if(!src && (src = (struct addrmask *)malloc(sizeof(struct addrmask)))
351     == NULL) panic("malloc failure");
352   src->neta = neta;
353   src->mask = mask;
354   if(first) {
355     status("add first                   {%08x/%08x}%08x/%08x",
356       neta, mask, aml?aml->neta:0, aml?aml->mask:0);
357     src->prev = NULL;
358     src->next = aml;
359     if(aml) aml->prev = src;
360     aml = src;
361   } else if(dst) {
362     status("add middle %08x/%08x{%08x/%08x}%08x/%08x",
363       dst->neta, dst->mask,
364       neta, mask,
365       dst->next?dst->next->neta:0, dst->next?dst->next->mask:0);
366     src->prev = dst;
367     src->next = dst->next;
368     dst->next = src;
369     if(src->next) src->next->prev = src;
370   } else {
371     status("add last   %08x/%08x{%08x/%08x}",
372       lst->neta, lst->mask, neta, mask);
373     src->prev = lst;
374     src->next = NULL;
375     lst->next = src;
376   }
377   while(optimize());
378   return(0);
379 }
380 
381 /* optimize - joins two AMs that can be expressed by a larger mask. */
optimize(void)382 static int optimize(void) {
383   struct addrmask *cur;
384 
385   for(cur = aml; cur->next; cur = cur->next)
386     if(cur->mask == cur->next->mask &&
387       cur->neta == (cur->next->neta & (cur->mask << 1))) {
388       addtoaml(cur->neta, cur->mask << 1);
389       return(1);
390     }
391   return(0);
392 }
393 
394 /**************************************
395  * PART III - Output formatting       *
396  **************************************/
397 
398 static int dispcidr(struct addrmask *);
399 static int dispstd(struct addrmask *);
400 static int dispcisco(struct addrmask *);
401 static int disprange(struct addrmask *);
402 static int disphex(struct addrmask *);
403 static int dispoctal(struct addrmask *);
404 static int dispbinary(struct addrmask *);
405 
406 /* display - shows the aml in a format specified by style
407  * it destroys the list as it sorts */
display(output_t style)408 int display(output_t style) {
409   struct addrmask *am, *list;
410   int (*disp)(struct addrmask *) = NULL;
411 
412   switch(style) {
413     case OUT_STD:    disp = &dispstd;    break;
414     case OUT_CIDR:   disp = &dispcidr;   break;
415     case OUT_CISCO:  disp = &dispcisco;  break;
416     case OUT_RANGE:  disp = &disprange;  break;
417     case OUT_HEX:    disp = &disphex;    break;
418     case OUT_OCTAL:  disp = &dispoctal;  break;
419     case OUT_BINARY: disp = &dispbinary; break;
420     default: panic("memfrob() apparently called on code segment");
421   }
422   while(aml) {
423     am = NULL;
424     for(list = aml; list; list = list->next)
425       if(!am || am->neta > list->neta) am = list;
426     if(am->next) am->next->prev = am->prev;
427     if(am->prev) am->prev->next = am->next;
428     else aml = am->next;
429     disp(am);
430     free(am);
431   }
432   return(0);
433 }
434 
dispcidr(struct addrmask * am)435 static int dispcidr(struct addrmask *am) {
436   u_int32_t mask;
437   int ctr = 0;
438 
439   for(mask = am->mask; mask; mask <<= 1) ctr++;
440   printf("%15s/%d\n", inet_ntoa((struct in_addr){htonl(am->neta)}), ctr);
441   return(0);
442 }
dispstd(struct addrmask * am)443 static int dispstd(struct addrmask *am) {
444   char buf[32];
445 
446   sprintf(buf,      "%15s/", inet_ntoa((struct in_addr){htonl(am->neta)}));
447   sprintf(buf + 16, "%-15s", inet_ntoa((struct in_addr){htonl(am->mask)}));
448   puts(buf);
449   return(0);
450 }
dispcisco(struct addrmask * am)451 static int dispcisco(struct addrmask *am) {
452   char buf[32];
453 
454   sprintf(buf,      "%15s ", inet_ntoa((struct in_addr){htonl(am->neta)}));
455   sprintf(buf + 16, "%-15s", inet_ntoa((struct in_addr){htonl(~am->mask)}));
456   puts(buf);
457   return(0);
458 }
disprange(struct addrmask * am)459 static int disprange(struct addrmask *am) {
460   char buf[80];
461   u_int32_t range = ~am->mask + 1;
462 
463   sprintf(buf,      "%15s-", inet_ntoa((struct in_addr){htonl(am->neta)}));
464   sprintf(buf + 16, "%-15s (%u)",
465     inet_ntoa((struct in_addr){htonl(am->neta + range - 1)}), range);
466   puts(buf);
467   return(0);
468 }
disphex(struct addrmask * am)469 static int disphex(struct addrmask *am) {
470   printf("0x%08x/0x%08x\n", am->neta, am->mask);
471   return(0);
472 }
dispoctal(struct addrmask * am)473 static int dispoctal(struct addrmask *am) {
474   printf("0%10o/0%10o\n", am->neta, am->mask);
475   return(0);
476 }
dispbinary(struct addrmask * am)477 static int dispbinary(struct addrmask *am) {
478   char abuf[36], mbuf[36];
479   int  i, j;
480 
481   for(i = 0; i < 32; i++) {
482     j = 34 - (int)(i * 9 / 8);  /* array index skips every 9th element */
483     abuf[j] = am->neta & 1 ? '1' : '0';
484     mbuf[j] = am->mask & 1 ? '1' : '0';
485     am->neta >>= 1;
486     am->mask >>= 1;
487   }
488   abuf[8]  = abuf[17] = abuf[26] = mbuf[8] = mbuf[17] = mbuf[26] = ' ';
489   abuf[35] = mbuf[35] = '\0';
490   printf("%s / %s\n", abuf, mbuf);
491   return(0);
492 }
493