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