Annotation of embedaddon/iftop/options.c, revision 1.1.1.2
1.1 misho 1: /*
2: * options.c:
3: *
4: *
5: */
6:
7: #include "config.h"
8:
9: #include <sys/types.h>
10:
11: #include <stdio.h>
12: #include <string.h>
13: #include <stdlib.h>
14: #include <unistd.h>
15:
16: #include <sys/ioctl.h>
17: #include <sys/socket.h>
18: #include <netinet/in.h>
19: #include <arpa/inet.h>
20: #include <net/if.h>
21:
22: #include "iftop.h"
23: #include "options.h"
24: #include "cfgfile.h"
25: #include "integers.h"
26:
27: #if !defined(HAVE_INET_ATON) && defined(HAVE_INET_PTON)
28: # define inet_aton(a, b) inet_pton(AF_INET, (a), (b))
29: #endif
30:
31: options_t options;
32:
1.1.1.2 ! misho 33: char optstr[] = "+i:f:nNF:G:lhpbBPm:c:s:tL:o:";
1.1 misho 34:
35: /* Global options. */
36:
37: /* Selecting an interface on which to listen: */
38:
39: /* This is a list of interface name prefixes which are `bad' in the sense
40: * that they don't refer to interfaces of external type on which we are
41: * likely to want to listen. We also compare candidate interfaces to lo. */
42: static char *bad_interface_names[] = {
43: "lo:",
44: "lo",
45: "stf", /* pseudo-device 6to4 tunnel interface */
46: "gif", /* psuedo-device generic tunnel interface */
47: "dummy",
48: "vmnet",
1.1.1.2 ! misho 49: "wmaster", /* wmaster0 is an internal-use interface for mac80211, a Linux WiFi API. */
! 50: NULL /* last entry must be NULL */
1.1 misho 51: };
52:
53: config_enumeration_type sort_enumeration[] = {
54: { "2s", OPTION_SORT_DIV1 },
55: { "10s", OPTION_SORT_DIV2 },
56: { "40s", OPTION_SORT_DIV3 },
57: { "source", OPTION_SORT_SRC },
1.1.1.2 ! misho 58: { "destination", OPTION_SORT_DEST },
1.1 misho 59: { NULL, -1 }
60: };
61:
62: config_enumeration_type linedisplay_enumeration[] = {
63: { "two-line", OPTION_LINEDISPLAY_TWO_LINE },
64: { "one-line-both", OPTION_LINEDISPLAY_ONE_LINE_BOTH },
65: { "one-line-sent", OPTION_LINEDISPLAY_ONE_LINE_SENT },
66: { "one-line-received", OPTION_LINEDISPLAY_ONE_LINE_RECV },
67: { NULL, -1 }
68: };
69:
70: config_enumeration_type showports_enumeration[] = {
71: { "off", OPTION_PORTS_OFF },
72: { "source-only", OPTION_PORTS_SRC },
73: { "destination-only", OPTION_PORTS_DEST },
74: { "on", OPTION_PORTS_ON },
75: { NULL, -1 }
76: };
77:
78: static int is_bad_interface_name(char *i) {
79: char **p;
80: for (p = bad_interface_names; *p; ++p)
81: if (strncmp(i, *p, strlen(*p)) == 0)
82: return 1;
83: return 0;
84: }
85:
86: /* This finds the first interface which is up and is not the loopback
87: * interface or one of the interface types listed in bad_interface_names. */
88: static char *get_first_interface(void) {
89: struct if_nameindex * nameindex;
1.1.1.2 ! misho 90: struct ifreq ifr;
1.1 misho 91: char *i = NULL;
92: int j = 0;
1.1.1.2 ! misho 93: int s;
1.1 misho 94: /* Use if_nameindex(3) instead? */
95:
96: nameindex = if_nameindex();
97: if(nameindex == NULL) {
98: return NULL;
99: }
100:
1.1.1.2 ! misho 101: s = socket(AF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
! 102:
1.1 misho 103: while(nameindex[j].if_index != 0) {
104: if (strcmp(nameindex[j].if_name, "lo") != 0 && !is_bad_interface_name(nameindex[j].if_name)) {
1.1.1.2 ! misho 105: strncpy(ifr.ifr_name, nameindex[j].if_name, sizeof(ifr.ifr_name));
! 106: if ((s == -1) || (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) || (ifr.ifr_flags & IFF_UP)) {
! 107: i = xstrdup(nameindex[j].if_name);
! 108: break;
! 109: }
1.1 misho 110: }
111: j++;
112: }
113: if_freenameindex(nameindex);
114: return i;
115: }
116:
117: void options_set_defaults() {
118: char *s;
119: /* Should go through the list of interfaces, and find the first one which
120: * is up and is not lo or dummy*. */
121: options.interface = get_first_interface();
122: if (!options.interface)
123: options.interface = "eth0";
124:
125: options.filtercode = NULL;
126: options.netfilter = 0;
127: inet_aton("10.0.1.0", &options.netfilternet);
128: inet_aton("255.255.255.0", &options.netfiltermask);
1.1.1.2 ! misho 129: options.netfilter6 = 0;
! 130: inet_pton(AF_INET6, "fe80::", &options.netfilter6net); /* Link-local */
! 131: inet_pton(AF_INET6, "ffff::", &options.netfilter6mask);
! 132: options.link_local = 0;
1.1 misho 133: options.dnsresolution = 1;
134: options.portresolution = 1;
135: #ifdef NEED_PROMISCUOUS_FOR_OUTGOING
136: options.promiscuous = 1;
137: options.promiscuous_but_choosy = 1;
138: #else
139: options.promiscuous = 0;
140: options.promiscuous_but_choosy = 0;
141: #endif
142: options.showbars = 1;
143: options.showports = OPTION_PORTS_OFF;
144: options.aggregate_src = 0;
145: options.aggregate_dest = 0;
146: options.paused = 0;
147: options.showhelp = 0;
148: options.bandwidth_in_bytes = 0;
149: options.sort = OPTION_SORT_DIV2;
150: options.screenfilter = NULL;
151: options.freezeorder = 0;
152: options.linedisplay = OPTION_LINEDISPLAY_TWO_LINE;
153: options.screen_offset = 0;
154: options.show_totals = 0;
155: options.max_bandwidth = 0; /* auto */
156: options.log_scale = 0;
157: options.bar_interval = 1;
1.1.1.2 ! misho 158: options.timed_output = 0;
! 159: options.no_curses = 0;
! 160: options.num_lines = 10;
1.1 misho 161:
162: /* Figure out the name for the config file */
163: s = getenv("HOME");
164: if(s != NULL) {
165: int i = strlen(s) + 9 + 1;
166: options.config_file = xmalloc(i);
167: snprintf(options.config_file,i,"%s/.iftoprc",s);
168: }
169: else {
170: options.config_file = xstrdup("iftoprc");
171: }
172: options.config_file_specified = 0;
173:
174: }
175:
176: /* usage:
177: * Print usage information. */
178: static void usage(FILE *fp) {
179: fprintf(fp,
180: "iftop: display bandwidth usage on an interface by host\n"
181: "\n"
1.1.1.2 ! misho 182: "Synopsis: iftop -h | [-npblNBP] [-i interface] [-f filter code]\n"
! 183: " [-F net/mask] [-G net6/mask6]\n"
1.1 misho 184: "\n"
185: " -h display this message\n"
186: " -n don't do hostname lookups\n"
187: " -N don't convert port numbers to services\n"
188: " -p run in promiscuous mode (show traffic between other\n"
189: " hosts on the same network segment)\n"
190: " -b don't display a bar graph of traffic\n"
191: " -B Display bandwidth in bytes\n"
192: " -i interface listen on named interface\n"
193: " -f filter code use filter code to select packets to count\n"
194: " (default: none, but only IP packets are counted)\n"
1.1.1.2 ! misho 195: " -F net/mask show traffic flows in/out of IPv4 network\n"
! 196: " -G net6/mask6 show traffic flows in/out of IPv6 network\n"
! 197: " -l display and count link-local IPv6 traffic (default: off)\n"
1.1 misho 198: " -P show ports as well as hosts\n"
199: " -m limit sets the upper limit for the bandwidth scale\n"
200: " -c config file specifies an alternative configuration file\n"
1.1.1.2 ! misho 201: " -t use text interface without ncurses\n"
1.1 misho 202: "\n"
1.1.1.2 ! misho 203: " Sorting orders:\n"
! 204: " -o 2s Sort by first column (2s traffic average)\n"
! 205: " -o 10s Sort by second column (10s traffic average) [default]\n"
! 206: " -o 40s Sort by third column (40s traffic average)\n"
! 207: " -o source Sort by source address\n"
! 208: " -o destination Sort by destination address\n"
! 209: "\n"
! 210: " The following options are only available in combination with -t\n"
! 211: " -s num print one single text output afer num seconds, then quit\n"
! 212: " -L num number of lines to print\n"
! 213: "\n"
! 214: "iftop, version " PACKAGE_VERSION "\n"
1.1 misho 215: "copyright (c) 2002 Paul Warren <pdw@ex-parrot.com> and contributors\n"
216: );
217: }
218:
219: void options_read_args(int argc, char **argv) {
220: int opt;
221:
222: opterr = 0;
223: while ((opt = getopt(argc, argv, optstr)) != -1) {
224: switch (opt) {
225: case 'h':
226: usage(stdout);
227: exit(0);
228:
229: case 'n':
230: config_set_string("dns-resolution","false");
231: break;
232:
233: case 'N':
234: config_set_string("port-resolution","false");
235: break;
236:
237: case 'i':
238: config_set_string("interface", optarg);
239: break;
240:
241: case 'f':
242: config_set_string("filter-code", optarg);
243: break;
244:
1.1.1.2 ! misho 245: case 'l':
! 246: config_set_string("link-local", "true");
! 247: break;
! 248:
1.1 misho 249: case 'p':
250: config_set_string("promiscuous", "true");
251: break;
252:
253: case 'P':
254: config_set_string("port-display", "on");
255: break;
256:
257: case 'F':
258: config_set_string("net-filter", optarg);
259: break;
260:
1.1.1.2 ! misho 261: case 'G':
! 262: config_set_string("net-filter6", optarg);
! 263: break;
! 264:
1.1 misho 265: case 'm':
266: config_set_string("max-bandwidth", optarg);
267: break;
268:
269: case 'b':
1.1.1.2 ! misho 270: config_set_string("show-bars", "false");
1.1 misho 271: break;
272:
273: case 'B':
274: config_set_string("use-bytes", "true");
275: break;
276:
1.1.1.2 ! misho 277: case 's':
! 278: config_set_string("timed-output", optarg);
! 279: break;
! 280:
! 281: case 't':
! 282: config_set_string("no-curses", "true");
! 283: break;
! 284:
! 285: case 'L':
! 286: config_set_string("num-lines", optarg);
! 287: break;
! 288:
! 289: case 'o':
! 290: config_set_string("sort", optarg);
! 291: break;
! 292:
1.1 misho 293: case 'c':
294: xfree(options.config_file);
295: options.config_file = xstrdup(optarg);
296: options.config_file_specified = 1;
297: break;
298:
299: case '?':
300: fprintf(stderr, "iftop: unknown option -%c\n", optopt);
301: usage(stderr);
302: exit(1);
303:
304: case ':':
305: fprintf(stderr, "iftop: option -%c requires an argument\n", optopt);
306: usage(stderr);
307: exit(1);
308: }
309: }
310:
1.1.1.2 ! misho 311:
1.1 misho 312: if (optind != argc) {
313: fprintf(stderr, "iftop: found arguments following options\n");
314: fprintf(stderr, "*** some options have changed names since v0.9 ***\n");
315: usage(stderr);
316: exit(1);
317: }
318: }
319:
320: /* options_config_get_string:
321: * Gets a value from the config, sets *value to a copy of the value, if
322: * found. Leaves the option unchanged otherwise. */
323: int options_config_get_string(const char *name, char** value) {
324: char *s;
325: s = config_get_string(name);
326: if(s != NULL) {
327: *value = xstrdup(s);
328: return 1;
329: }
330: return 0;
331: }
332:
333: int options_config_get_bool(const char *name, int* value) {
334: if(config_get_string(name)) {
335: *value = config_get_bool(name);
336: return 1;
337: }
338: return 0;
339: }
340:
341: int options_config_get_int(const char *name, int* value) {
342: if(config_get_string(name)) {
343: config_get_int(name, value);
344: return 1;
345: }
346: return 0;
347: }
348:
349: int options_config_get_enum(char *name, config_enumeration_type* enumeration, int *result) {
350: int i;
351: if(config_get_string(name)) {
352: if(config_get_enum(name, enumeration, &i)) {
353: *result = i;
354: return 1;
355: }
356: }
357: return 0;
358: }
359:
360: int options_config_get_promiscuous() {
361: if(config_get_string("promiscuous")) {
362: options.promiscuous = config_get_bool("promiscuous");
363: if(options.promiscuous) {
364: /* User has explicitly requested promiscuous mode, so don't be
365: * choosy */
366: options.promiscuous_but_choosy = 0;
367: }
368: return 1;
369: }
370: return 0;
371: }
372:
373: int options_config_get_bw_rate(char *directive, long long* result) {
374: char* units;
375: long long mult = 1;
376: long long value;
377: char *s;
378: s = config_get_string(directive);
379: if(s) {
380: units = s + strspn(s, "0123456789");
381: if(strlen(units) > 1) {
382: fprintf(stderr, "Invalid units in value: %s\n", s);
383: return 0;
384: }
385: if(strlen(units) == 1) {
386: if(*units == 'k' || *units == 'K') {
387: mult = 1024;
388: }
389: else if(*units == 'm' || *units == 'M') {
390: mult = 1024 * 1024;
391: }
392: else if(*units == 'g' || *units == 'G') {
393: mult = 1024 * 1024 * 1024;
394: }
395: else if(*units == 'b' || *units == 'B') {
396: /* bits => mult = 1 */
397: }
398: else {
399: fprintf(stderr, "Invalid units in value: %s\n", s);
400: return 0;
401: }
402: }
403: *units = '\0';
404: if(sscanf(s, "%lld", &value) != 1) {
405: fprintf(stderr, "Error reading rate: %s\n", s);
406: }
407: options.max_bandwidth = value * mult;
408: return 1;
409: }
410: return 0;
411: }
412:
413: /*
414: * Read the net filter option.
415: */
416: int options_config_get_net_filter() {
417: char* s;
418: s = config_get_string("net-filter");
419: if(s) {
420: char* mask;
421:
1.1.1.2 ! misho 422: options.netfilter = 0;
! 423:
1.1 misho 424: mask = strchr(s, '/');
425: if (mask == NULL) {
426: fprintf(stderr, "Could not parse net/mask: %s\n", s);
427: return 0;
428: }
429: *mask = '\0';
430: mask++;
431: if (inet_aton(s, &options.netfilternet) == 0) {
432: fprintf(stderr, "Invalid network address: %s\n", s);
433: return 0;
434: }
435: /* Accept a netmask like /24 or /255.255.255.0. */
436: if (mask[strspn(mask, "0123456789")] == '\0') {
437: /* Whole string is numeric */
438: int n;
439: n = atoi(mask);
440: if (n > 32) {
1.1.1.2 ! misho 441: fprintf(stderr, "Invalid netmask length: %s\n", mask);
1.1 misho 442: }
443: else {
444: if(n == 32) {
445: /* This needs to be special cased, although I don't fully
446: * understand why -pdw
447: */
448: options.netfiltermask.s_addr = htonl(0xffffffffl);
449: }
450: else {
451: u_int32_t mm = 0xffffffffl;
452: mm >>= n;
453: options.netfiltermask.s_addr = htonl(~mm);
454: }
455: }
1.1.1.2 ! misho 456: options.netfilter = 1;
1.1 misho 457: }
1.1.1.2 ! misho 458: else {
! 459: if (inet_aton(mask, &options.netfiltermask) != 0)
! 460: options.netfilter = 1;
! 461: else {
! 462: fprintf(stderr, "Invalid netmask: %s\n", s);
! 463: return 0;
! 464: }
1.1 misho 465: }
466: options.netfilternet.s_addr = options.netfilternet.s_addr & options.netfiltermask.s_addr;
467: return 1;
468: }
469: return 0;
470: }
471:
1.1.1.2 ! misho 472: /*
! 473: * Read the net filter IPv6 option.
! 474: */
! 475: int options_config_get_net_filter6() {
! 476: char* s;
! 477: int j;
! 478:
! 479: s = config_get_string("net-filter6");
! 480: if(s) {
! 481: char* mask;
! 482:
! 483: options.netfilter6 = 0;
! 484:
! 485: mask = strchr(s, '/');
! 486: if (mask == NULL) {
! 487: fprintf(stderr, "Could not parse IPv6 net/prefix: %s\n", s);
! 488: return 0;
! 489: }
! 490: *mask = '\0';
! 491: mask++;
! 492: if (inet_pton(AF_INET6, s, &options.netfilter6net) == 0) {
! 493: fprintf(stderr, "Invalid IPv6 network address: %s\n", s);
! 494: return 0;
! 495: }
! 496: /* Accept prefix lengths and address expressions. */
! 497: if (mask[strspn(mask, "0123456789")] == '\0') {
! 498: /* Whole string is numeric */
! 499: unsigned int n;
! 500:
! 501: n = atoi(mask);
! 502: if (n > 128 || n < 1) {
! 503: fprintf(stderr, "Invalid IPv6 prefix length: %s\n", mask);
! 504: }
! 505: else {
! 506: int bl, rem;
! 507: const uint8_t mm = 0xff;
! 508: uint8_t part = mm;
! 509:
! 510: bl = n / 8;
! 511: rem = n % 8;
! 512: part <<= 8 - rem;
! 513: for (j=0; j < bl; ++j)
! 514: options.netfilter6mask.s6_addr[j] = mm;
! 515:
! 516: if (rem > 0)
! 517: options.netfilter6mask.s6_addr[bl] = part;
! 518: options.netfilter6 = 1;
! 519: }
! 520: }
! 521: else {
! 522: if (inet_pton(AF_INET6, mask, &options.netfilter6mask) != 0)
! 523: options.netfilter6 = 1;
! 524: else {
! 525: fprintf(stderr, "Invalid IPv6 netmask: %s\n", s);
! 526: return 0;
! 527: }
! 528: }
! 529: /* Prepare any comparison by masking the provided filtered net. */
! 530: for (j=0; j < 16; ++j)
! 531: options.netfilter6net.s6_addr[j] &= options.netfilter6mask.s6_addr[j];
! 532:
! 533: return 1;
! 534: }
! 535: return 0;
! 536: }
1.1 misho 537:
538: void options_make() {
539: options_config_get_string("interface", &options.interface);
540: options_config_get_bool("dns-resolution", &options.dnsresolution);
541: options_config_get_bool("port-resolution", &options.portresolution);
542: options_config_get_string("filter-code", &options.filtercode);
543: options_config_get_bool("show-bars", &options.showbars);
544: options_config_get_promiscuous();
545: options_config_get_bool("hide-source", &options.aggregate_src);
546: options_config_get_bool("hide-destination", &options.aggregate_dest);
547: options_config_get_bool("use-bytes", &options.bandwidth_in_bytes);
548: options_config_get_enum("sort", sort_enumeration, (int*)&options.sort);
549: options_config_get_enum("line-display", linedisplay_enumeration, (int*)&options.linedisplay);
550: options_config_get_bool("show-totals", &options.show_totals);
551: options_config_get_bool("log-scale", &options.log_scale);
552: options_config_get_bw_rate("max-bandwidth", &options.max_bandwidth);
553: options_config_get_enum("port-display", showports_enumeration, (int*)&options.showports);
554: options_config_get_string("screen-filter", &options.screenfilter);
1.1.1.2 ! misho 555: options_config_get_bool("link-local", &options.link_local);
! 556: options_config_get_int("timed-output", &options.timed_output);
! 557: options_config_get_bool("no-curses", &options.no_curses);
! 558: options_config_get_int("num-lines", &options.num_lines);
1.1 misho 559: options_config_get_net_filter();
1.1.1.2 ! misho 560: options_config_get_net_filter6();
1.1 misho 561: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>