options.c 17 KB

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