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