flowtop: refactor parts of the code (more on todo)
[netsniff-ng.git] / src / flowtop.c
blob841db6136fb4c35662742e42da1bad20637636ec
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2011 - 2012 Daniel Borkmann.
5 * Copyright 2011 Emmanuel Roullit.
6 * Subject to the GPL, version 2.
8 * A tiny tool to provide top-like netfilter connection tracking information.
10 * The Dark Lord has Nine. But we have One, mightier than they: the White
11 * Rider. He has passed through the fire and the abyss, and they shall
12 * fear him. We will go where he leads.
14 * -- The Lord of the Rings, Aragorn,
15 * Chapter 'The White Rider'.
18 #define _LGPL_SOURCE
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <signal.h>
23 #include <getopt.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include <netdb.h>
27 #include <ctype.h>
28 #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
29 #include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
30 #include <GeoIP.h>
31 #include <GeoIPCity.h>
32 #include <netinet/in.h>
33 #include <curses.h>
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #include <urcu.h>
37 #include <libgen.h>
39 #include "die.h"
40 #include "xmalloc.h"
41 #include "xio.h"
42 #include "xutils.h"
43 #include "built_in.h"
44 #include "locking.h"
45 #include "dissector_eth.h"
46 #include "pkt_buff.h"
48 struct flow_entry {
49 uint32_t flow_id, use, status;
50 uint8_t l3_proto, l4_proto;
51 uint32_t ip4_src_addr, ip4_dst_addr;
52 uint32_t ip6_src_addr[4], ip6_dst_addr[4];
53 uint16_t port_src, port_dst;
54 uint8_t tcp_state, tcp_flags;
55 uint64_t counter_pkts, counter_bytes;
56 uint64_t timestamp_start, timestamp_stop;
57 char country_src[128], country_dst[128];
58 char city_src[128], city_dst[128];
59 char rev_dns_src[256], rev_dns_dst[256];
60 int first, procnum, inode;
61 char cmdline[256];
62 struct flow_entry *next;
65 struct flow_list {
66 struct flow_entry *head;
67 struct spinlock lock;
70 #ifndef ATTR_TIMESTAMP_START
71 # define ATTR_TIMESTAMP_START 63
72 #endif
73 #ifndef ATTR_TIMESTAMP_STOP
74 # define ATTR_TIMESTAMP_STOP 64
75 #endif
77 #define SCROLL_MAX 1000
79 volatile sig_atomic_t sigint = 0;
81 #define INCLUDE_UDP (1 << 0)
82 #define INCLUDE_TCP (1 << 1)
84 static int what = INCLUDE_TCP, show_src = 0;
86 static struct flow_list flow_list;
88 static GeoIP *gi_country = NULL, *gi_city = NULL;
89 static char *path_country_db = NULL, *path_city_db = NULL;
91 static const char *short_options = "vhTULKs";
92 static const struct option long_options[] = {
93 {"tcp", no_argument, NULL, 'T'},
94 {"udp", no_argument, NULL, 'U'},
95 {"show-src", no_argument, NULL, 's'},
96 {"city-db", required_argument, NULL, 'L'},
97 {"country-db", required_argument, NULL, 'K'},
98 {"version", no_argument, NULL, 'v'},
99 {"help", no_argument, NULL, 'h'},
100 {NULL, 0, NULL, 0}
103 static const char *const l3proto2str[AF_MAX] = {
104 [AF_INET] = "ipv4",
105 [AF_INET6] = "ipv6",
108 static const char *const proto2str[IPPROTO_MAX] = {
109 [IPPROTO_TCP] = "tcp",
110 [IPPROTO_UDP] = "udp",
111 [IPPROTO_UDPLITE] = "udplite",
112 [IPPROTO_ICMP] = "icmp",
113 [IPPROTO_ICMPV6] = "icmpv6",
114 [IPPROTO_SCTP] = "sctp",
115 [IPPROTO_GRE] = "gre",
116 [IPPROTO_DCCP] = "dccp",
117 [IPPROTO_IGMP] = "igmp",
118 [IPPROTO_IPIP] = "ipip",
119 [IPPROTO_EGP] = "egp",
120 [IPPROTO_PUP] = "pup",
121 [IPPROTO_IDP] = "idp",
122 [IPPROTO_RSVP] = "rsvp",
123 [IPPROTO_IPV6] = "ip6tun",
124 [IPPROTO_ESP] = "esp",
125 [IPPROTO_AH] = "ah",
126 [IPPROTO_PIM] = "pim",
127 [IPPROTO_COMP] = "comp",
130 static const char *const state2str[TCP_CONNTRACK_MAX] = {
131 [TCP_CONNTRACK_NONE] = "NOSTATE",
132 [TCP_CONNTRACK_SYN_SENT] = "SYN_SENT",
133 [TCP_CONNTRACK_SYN_RECV] = "SYN_RECV",
134 [TCP_CONNTRACK_ESTABLISHED] = "ESTABLISHED",
135 [TCP_CONNTRACK_FIN_WAIT] = "FIN_WAIT",
136 [TCP_CONNTRACK_CLOSE_WAIT] = "CLOSE_WAIT",
137 [TCP_CONNTRACK_LAST_ACK] = "LAST_ACK",
138 [TCP_CONNTRACK_TIME_WAIT] = "TIME_WAIT",
139 [TCP_CONNTRACK_CLOSE] = "CLOSE",
140 [TCP_CONNTRACK_SYN_SENT2] = "SYN_SENT2",
143 static const uint8_t states[] = {
144 TCP_CONNTRACK_SYN_SENT,
145 TCP_CONNTRACK_SYN_RECV,
146 TCP_CONNTRACK_ESTABLISHED,
147 TCP_CONNTRACK_FIN_WAIT,
148 TCP_CONNTRACK_CLOSE_WAIT,
149 TCP_CONNTRACK_LAST_ACK,
150 TCP_CONNTRACK_TIME_WAIT,
151 TCP_CONNTRACK_CLOSE,
152 TCP_CONNTRACK_SYN_SENT2,
153 TCP_CONNTRACK_NONE,
156 static const struct nfct_filter_ipv4 filter_ipv4 = {
157 .addr = __constant_htonl(INADDR_LOOPBACK),
158 .mask = 0xffffffff,
161 static const struct nfct_filter_ipv6 filter_ipv6 = {
162 .addr = { 0x0, 0x0, 0x0, 0x1 },
163 .mask = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
166 static void signal_handler(int number)
168 switch (number) {
169 case SIGINT:
170 sigint = 1;
171 break;
172 case SIGHUP:
173 default:
174 break;
178 static inline const char *make_n_a(const char *p)
180 return p ? : "N/A";
183 static void flow_entry_from_ct(struct flow_entry *n, struct nf_conntrack *ct);
184 static void flow_entry_get_extended(struct flow_entry *n);
186 static void help(void)
188 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
189 VERSION_STRING);
190 puts("http://www.netsniff-ng.org\n\n"
191 "Usage: flowtop [options]\n"
192 "Options:\n"
193 " -T|--tcp Show only TCP flows (default)\n"
194 " -U|--udp Show only UDP flows\n"
195 " -s|--show-src Also show source, not only dest\n"
196 " --city-db <path> Specifiy path for geoip city database\n"
197 " --country-db <path> Specifiy path for geoip country database\n"
198 " -v|--version Print version\n"
199 " -h|--help Print this help\n\n"
200 "Examples:\n"
201 " flowtop\n"
202 " flowtop -UT\n"
203 " flowtop -s\n\n"
204 "Note:\n"
205 " If netfilter is not running, you can activate it with i.e.:\n"
206 " iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT\n"
207 " iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT\n\n"
208 "Please report bugs to <bugs@netsniff-ng.org>\n"
209 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
210 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n"
211 "License: GNU GPL version 2.0\n"
212 "This is free software: you are free to change and redistribute it.\n"
213 "There is NO WARRANTY, to the extent permitted by law.\n");
214 die();
217 static void version(void)
219 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
220 VERSION_STRING);
221 puts("http://www.netsniff-ng.org\n\n"
222 "Please report bugs to <bugs@netsniff-ng.org>\n"
223 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
224 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n"
225 "License: GNU GPL version 2.0\n"
226 "This is free software: you are free to change and redistribute it.\n"
227 "There is NO WARRANTY, to the extent permitted by law.\n");
228 die();
231 static inline struct flow_entry *flow_entry_xalloc(void)
233 struct flow_entry *n;
235 n = xzmalloc(sizeof(*n));
236 n->first = 1;
238 return n;
241 static inline void flow_entry_xfree(struct flow_entry *n)
243 xfree(n);
246 static inline void flow_list_init(struct flow_list *fl)
248 fl->head = NULL;
249 spinlock_init(&fl->lock);
252 static void flow_list_new_entry(struct flow_list *fl, struct nf_conntrack *ct)
254 struct flow_entry *n = flow_entry_xalloc();
256 rcu_assign_pointer(n->next, fl->head);
257 rcu_assign_pointer(fl->head, n);
259 flow_entry_from_ct(n, ct);
260 flow_entry_get_extended(n);
263 static struct flow_entry *flow_list_find_id(struct flow_list *fl,
264 uint32_t id)
266 struct flow_entry *n = rcu_dereference(fl->head);
268 while (n != NULL) {
269 if (n->flow_id == id)
270 return n;
272 n = rcu_dereference(n->next);
275 return NULL;
278 static struct flow_entry *flow_list_find_prev_id(struct flow_list *fl,
279 uint32_t id)
281 struct flow_entry *n = rcu_dereference(fl->head), *tmp;
283 if (n->flow_id == id)
284 return NULL;
286 while ((tmp = rcu_dereference(n->next)) != NULL) {
287 if (tmp->flow_id == id)
288 return n;
290 n = tmp;
293 return NULL;
296 static void flow_list_update_entry(struct flow_list *fl,
297 struct nf_conntrack *ct)
299 int do_ext = 0;
300 struct flow_entry *n;
302 n = flow_list_find_id(fl, nfct_get_attr_u32(ct, ATTR_ID));
303 if (n == NULL) {
304 n = flow_entry_xalloc();
306 rcu_assign_pointer(n->next, fl->head);
307 rcu_assign_pointer(fl->head, n);
309 do_ext = 1;
312 flow_entry_from_ct(n, ct);
313 if (do_ext)
314 flow_entry_get_extended(n);
317 static void flow_list_destroy_entry(struct flow_list *fl,
318 struct nf_conntrack *ct)
320 struct flow_entry *n1, *n2;
321 uint32_t id = nfct_get_attr_u32(ct, ATTR_ID);
323 n1 = flow_list_find_id(fl, id);
324 if (n1) {
325 n2 = flow_list_find_prev_id(fl, id);
326 if (n2) {
327 rcu_assign_pointer(n2->next, n1->next);
328 rcu_assign_pointer(n1->next, NULL);
330 flow_entry_xfree(n1);
331 } else {
332 flow_entry_xfree(fl->head);
334 rcu_assign_pointer(fl->head, NULL);
339 static void flow_list_destroy(struct flow_list *fl)
341 struct flow_entry *n;
343 while (fl->head != NULL) {
344 n = rcu_dereference(fl->head->next);
345 rcu_assign_pointer(fl->head->next, NULL);
347 flow_entry_xfree(fl->head);
348 rcu_assign_pointer(fl->head, n);
351 synchronize_rcu();
352 spinlock_destroy(&fl->lock);
355 static int walk_process(char *process, struct flow_entry *n)
357 int ret;
358 DIR *dir;
359 struct dirent *ent;
360 char path[1024];
362 if (snprintf(path, sizeof(path), "/proc/%s/fd", process) == -1)
363 panic("giant process name! %s\n", process);
365 dir = opendir(path);
366 if (!dir)
367 return 0;
369 while ((ent = readdir(dir))) {
370 struct stat statbuf;
372 if (snprintf(path, sizeof(path), "/proc/%s/fd/%s",
373 process, ent->d_name) < 0)
374 continue;
376 if (stat(path, &statbuf) < 0)
377 continue;
379 if (S_ISSOCK(statbuf.st_mode) && n->inode == statbuf.st_ino) {
380 memset(n->cmdline, 0, sizeof(n->cmdline));
382 snprintf(path, sizeof(path), "/proc/%s/exe", process);
384 ret = readlink(path, n->cmdline,
385 sizeof(n->cmdline) - 1);
386 if (ret < 0)
387 panic("readlink error: %s\n", strerror(errno));
389 n->procnum = atoi(process);
390 return 1;
394 closedir(dir);
395 return 0;
398 static void walk_processes(struct flow_entry *n)
400 int ret;
401 DIR *dir;
402 struct dirent *ent;
404 /* n->inode must be set */
405 if (n->inode <= 0) {
406 memset(n->cmdline, 0, sizeof(n->cmdline));
407 return;
410 dir = opendir("/proc");
411 if (!dir)
412 panic("Cannot open /proc!\n");
414 while ((ent = readdir(dir))) {
415 if (strspn(ent->d_name, "0123456789") == strlen(ent->d_name)) {
416 ret = walk_process(ent->d_name, n);
417 if (ret > 0)
418 break;
422 closedir(dir);
425 static int get_port_inode(uint16_t port, int proto, int is_ip6)
427 int ret = -ENOENT;
428 char path[128], buff[1024];
429 FILE *proc;
431 memset(path, 0, sizeof(path));
432 snprintf(path, sizeof(path), "/proc/net/%s%s",
433 proto2str[proto], is_ip6 ? "6" : "");
435 proc = fopen(path, "r");
436 if (!proc)
437 return -EIO;
439 memset(buff, 0, sizeof(buff));
441 while (fgets(buff, sizeof(buff), proc) != NULL) {
442 int inode = 0;
443 unsigned int lport = 0;
445 buff[sizeof(buff) - 1] = 0;
446 if (sscanf(buff, "%*u: %*X:%X %*X:%*X %*X %*X:%*X %*X:%*X "
447 "%*X %*u %*u %u", &lport, &inode) == 2) {
448 if ((uint16_t) lport == port) {
449 ret = inode;
450 break;
454 memset(buff, 0, sizeof(buff));
457 fclose(proc);
458 return ret;
461 #define CP_NFCT(elem, attr, x) do { n->elem = nfct_get_attr_u##x(ct,(attr)); } while (0)
462 #define CP_NFCT_BUFF(elem, attr) do { \
463 const uint8_t *buff = nfct_get_attr(ct,(attr)); \
464 if (buff != NULL) \
465 memcpy(n->elem, buff, sizeof(n->elem)); \
466 } while (0)
468 static void flow_entry_from_ct(struct flow_entry *n, struct nf_conntrack *ct)
470 CP_NFCT(l3_proto, ATTR_ORIG_L3PROTO, 8);
471 CP_NFCT(l4_proto, ATTR_ORIG_L4PROTO, 8);
472 CP_NFCT(ip4_src_addr, ATTR_ORIG_IPV4_SRC, 32);
473 CP_NFCT(ip4_dst_addr, ATTR_ORIG_IPV4_DST, 32);
474 CP_NFCT(port_src, ATTR_ORIG_PORT_SRC, 16);
475 CP_NFCT(port_dst, ATTR_ORIG_PORT_DST, 16);
476 CP_NFCT(status, ATTR_STATUS, 32);
477 CP_NFCT(tcp_state, ATTR_TCP_STATE, 8);
478 CP_NFCT(tcp_flags, ATTR_TCP_FLAGS_ORIG, 8);
479 CP_NFCT(counter_pkts, ATTR_ORIG_COUNTER_PACKETS, 64);
480 CP_NFCT(counter_bytes, ATTR_ORIG_COUNTER_BYTES, 64);
481 CP_NFCT(timestamp_start, ATTR_TIMESTAMP_START, 64);
482 CP_NFCT(timestamp_stop, ATTR_TIMESTAMP_STOP, 64);
483 CP_NFCT(flow_id, ATTR_ID, 32);
484 CP_NFCT(use, ATTR_USE, 32);
486 CP_NFCT_BUFF(ip6_src_addr, ATTR_ORIG_IPV6_SRC);
487 CP_NFCT_BUFF(ip6_dst_addr, ATTR_ORIG_IPV6_DST);
489 if (n->first) {
490 n->inode = get_port_inode(ntohs(n->port_src), n->l4_proto,
491 n->l3_proto == AF_INET6);
492 if (n->inode > 0)
493 walk_processes(n);
496 n->first = 0;
499 /* TODO: IP4 + IP6 */
500 static void flow_entry_get_extended(struct flow_entry *n)
502 struct sockaddr_in sa;
503 struct hostent *hent;
504 GeoIPRecord *gir_src, *gir_dst;
506 if (n->flow_id == 0)
507 return;
508 if (ntohs(n->port_src) == 53 || ntohs(n->port_dst) == 53)
509 return;
511 memset(&sa, 0, sizeof(sa));
512 sa.sin_family = PF_INET; //XXX: IPv4
513 sa.sin_addr.s_addr = n->ip4_src_addr;
514 getnameinfo((struct sockaddr *) &sa, sizeof(sa), n->rev_dns_src,
515 sizeof(n->rev_dns_src), NULL, 0, NI_NUMERICHOST);
517 hent = gethostbyaddr(&sa.sin_addr, sizeof(sa.sin_addr), PF_INET);
518 if (hent) {
519 memset(n->rev_dns_src, 0, sizeof(n->rev_dns_src));
520 memcpy(n->rev_dns_src, hent->h_name,
521 min(sizeof(n->rev_dns_src), strlen(hent->h_name)));
524 gir_src = GeoIP_record_by_ipnum(gi_city, ntohl(n->ip4_src_addr));
525 if (gir_src) {
526 const char *country =
527 make_n_a(GeoIP_country_name_by_ipnum(gi_country,
528 ntohl(n->ip4_src_addr)));
529 const char *city = make_n_a(gir_src->city);
530 memcpy(n->country_src, country,
531 min(sizeof(n->country_src), strlen(country)));
532 memcpy(n->city_src, city,
533 min(sizeof(n->city_src), strlen(city)));
536 memset(&sa, 0, sizeof(sa));
537 sa.sin_family = PF_INET; //XXX: IPv4
538 sa.sin_addr.s_addr = n->ip4_dst_addr;
539 getnameinfo((struct sockaddr *) &sa, sizeof(sa), n->rev_dns_dst,
540 sizeof(n->rev_dns_dst), NULL, 0, NI_NUMERICHOST);
542 hent = gethostbyaddr(&sa.sin_addr, sizeof(sa.sin_addr), PF_INET);
543 if (hent) {
544 memset(n->rev_dns_dst, 0, sizeof(n->rev_dns_dst));
545 memcpy(n->rev_dns_dst, hent->h_name,
546 min(sizeof(n->rev_dns_dst), strlen(hent->h_name)));
549 gir_dst = GeoIP_record_by_ipnum(gi_city, ntohl(n->ip4_dst_addr));
550 if (gir_dst) {
551 const char *country =
552 make_n_a(GeoIP_country_name_by_ipnum(gi_country,
553 ntohl(n->ip4_dst_addr)));
554 const char *city = make_n_a(gir_dst->city);
555 memcpy(n->country_dst, country,
556 min(sizeof(n->country_dst), strlen(country)));
557 memcpy(n->city_dst, city,
558 min(sizeof(n->city_dst), strlen(city)));
562 static uint16_t presenter_get_port(uint16_t src, uint16_t dst)
564 char *tmp1, *tmp2;
566 src = ntohs(src);
567 dst = ntohs(dst);
569 /* XXX: Is there a better way to determine? */
570 if (src < dst && src < 1024) {
571 return src;
572 } else if (dst < src && dst < 1024) {
573 return dst;
574 } else {
575 tmp1 = lookup_port_tcp(src);
576 tmp2 = lookup_port_tcp(dst);
577 if (tmp1 && !tmp2) {
578 return src;
579 } else if (!tmp1 && tmp2) {
580 return dst;
581 } else {
582 if (src < dst)
583 return src;
584 else
585 return dst;
590 static void presenter_screen_init(WINDOW **screen)
592 (*screen) = initscr();
593 noecho();
594 cbreak();
595 keypad(stdscr, TRUE);
596 nodelay(*screen, TRUE);
597 refresh();
598 wrefresh(*screen);
601 static void presenter_screen_update(WINDOW *screen, struct flow_list *fl,
602 int skip_lines)
604 int i, line = 3, maxy;
605 struct flow_entry *n;
607 curs_set(0);
608 maxy = getmaxy(screen);
610 start_color();
611 init_pair(1, COLOR_RED, COLOR_BLACK);
612 init_pair(2, COLOR_BLUE, COLOR_BLACK);
613 init_pair(3, COLOR_YELLOW, COLOR_BLACK);
614 init_pair(4, COLOR_GREEN, COLOR_BLACK);
616 wclear(screen);
617 clear();
619 mvwprintw(screen, 1, 2, "Kernel netfilter TCP/UDP "
620 "flow statistics, [+%d]", skip_lines);
622 rcu_read_lock();
624 if (rcu_dereference(fl->head) == NULL)
625 mvwprintw(screen, line, 2, "(No active sessions! "
626 "Is netfilter running?)");
628 maxy -= 4;
629 /* Yes, that's lame :-P */
630 for (i = 0; i < sizeof(states); i++) {
631 n = rcu_dereference(fl->head);
633 while (n && maxy > 0) {
634 char tmp[128];
636 if (n->tcp_state != states[i] ||
637 (i != TCP_CONNTRACK_NONE &&
638 n->tcp_state == TCP_CONNTRACK_NONE) ||
639 /* Filter out DNS */
640 presenter_get_port(n->port_src, n->port_dst) == 53) {
641 n = rcu_dereference(n->next);
642 continue;
645 if (skip_lines > 0) {
646 n = rcu_dereference(n->next);
647 skip_lines--;
648 continue;
651 snprintf(tmp, sizeof(tmp), "%u/%s", n->procnum,
652 basename(n->cmdline));
653 tmp[sizeof(tmp) - 1] = 0;
655 mvwprintw(screen, line, 2, "[");
656 attron(COLOR_PAIR(3));
657 printw("%s", n->procnum > 0 ? tmp : "bridged(?)");
658 attroff(COLOR_PAIR(3));
659 printw("]:%s:%s[", l3proto2str[n->l3_proto],
660 proto2str[n->l4_proto]);
661 attron(COLOR_PAIR(3));
662 printw("%s", state2str[n->tcp_state]);
663 attroff(COLOR_PAIR(3));
664 printw("]:");
665 attron(A_BOLD);
666 if (n->tcp_state != TCP_CONNTRACK_NONE) {
667 printw("%s -> ", lookup_port_tcp(presenter_get_port(n->port_src,
668 n->port_dst)));
669 } else {
670 printw("%s -> ", lookup_port_udp(presenter_get_port(n->port_src,
671 n->port_dst)));
673 attroff(A_BOLD);
674 if (show_src) {
675 attron(COLOR_PAIR(1));
676 mvwprintw(screen, ++line, 8, "src: %s", n->rev_dns_src);
677 attroff(COLOR_PAIR(1));
678 printw(":%u (", ntohs(n->port_src));
679 attron(COLOR_PAIR(4));
680 printw("%s", (strlen(n->country_src) > 0 ?
681 n->country_src : "N/A"));
682 attroff(COLOR_PAIR(4));
683 printw(", %s) => ", (strlen(n->city_src) > 0 ?
684 n->city_src : "N/A"));
686 attron(COLOR_PAIR(2));
687 mvwprintw(screen, ++line, 8, "dst: %s", n->rev_dns_dst);
688 attroff(COLOR_PAIR(2));
689 printw(":%u (", ntohs(n->port_dst));
690 attron(COLOR_PAIR(4));
691 printw("%s", strlen(n->country_dst) > 0 ?
692 n->country_dst : "N/A");
693 attroff(COLOR_PAIR(4));
694 printw(", %s)", strlen(n->city_dst) > 0 ?
695 n->city_dst : "N/A");
697 line++;
698 maxy--;
699 n = rcu_dereference(n->next);
703 rcu_read_unlock();
705 wrefresh(screen);
706 refresh();
709 static inline void presenter_screen_end(void)
711 endwin();
714 static void presenter(void)
716 int skip_lines = 0;
717 WINDOW *screen = NULL;
719 dissector_init_ethernet(0);
720 presenter_screen_init(&screen);
722 rcu_register_thread();
723 while (!sigint) {
724 switch (getch()) {
725 case 'q':
726 sigint = 1;
727 break;
728 case KEY_UP:
729 case 'u':
730 case 'k':
731 skip_lines--;
732 if (skip_lines < 0)
733 skip_lines = 0;
734 break;
735 case KEY_DOWN:
736 case 'd':
737 case 'j':
738 skip_lines++;
739 if (skip_lines > SCROLL_MAX)
740 skip_lines = SCROLL_MAX;
741 break;
742 default:
743 fflush(stdin);
744 break;
747 presenter_screen_update(screen, &flow_list, skip_lines);
748 usleep(100000);
750 rcu_unregister_thread();
752 presenter_screen_end();
753 dissector_cleanup_ethernet();
756 static int collector_cb(enum nf_conntrack_msg_type type,
757 struct nf_conntrack *ct, void *data)
759 if (sigint)
760 return NFCT_CB_STOP;
762 synchronize_rcu();
763 spinlock_lock(&flow_list.lock);
765 switch (type) {
766 case NFCT_T_NEW:
767 flow_list_new_entry(&flow_list, ct);
768 break;
769 case NFCT_T_UPDATE:
770 flow_list_update_entry(&flow_list, ct);
771 break;
772 case NFCT_T_DESTROY:
773 flow_list_destroy_entry(&flow_list, ct);
774 break;
775 default:
776 break;
779 spinlock_unlock(&flow_list.lock);
781 return NFCT_CB_CONTINUE;
784 static inline GeoIP *collector_geoip_open(const char *path, int type)
786 if (path != NULL)
787 return GeoIP_open(path, GEOIP_MMAP_CACHE);
788 else
789 return GeoIP_open_type(type, GEOIP_MMAP_CACHE);
792 static void collector_load_geoip(void)
794 gi_country = collector_geoip_open(path_country_db,
795 GEOIP_COUNTRY_EDITION);
796 if (gi_country == NULL)
797 panic("Cannot open GeoIP country database!\n");
799 gi_city = collector_geoip_open(path_city_db,
800 GEOIP_CITY_EDITION_REV1);
801 if (gi_city == NULL)
802 panic("Cannot open GeoIP city database!\n");
804 GeoIP_set_charset(gi_country, GEOIP_CHARSET_UTF8);
805 GeoIP_set_charset(gi_city, GEOIP_CHARSET_UTF8);
808 static void collector_destroy_geoip(void)
810 GeoIP_delete(gi_city);
811 GeoIP_delete(gi_country);
814 static void *collector(void *null)
816 int ret;
817 struct nfct_handle *handle;
818 struct nfct_filter *filter;
820 handle = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
821 if (!handle)
822 panic("Cannot create a nfct handle!\n");
824 filter = nfct_filter_create();
825 if (!filter)
826 panic("Cannot create a nfct filter!\n");
828 if (what & INCLUDE_UDP)
829 nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO,
830 IPPROTO_UDP);
831 if (what & INCLUDE_TCP)
832 nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO,
833 IPPROTO_TCP);
835 nfct_filter_set_logic(filter, NFCT_FILTER_SRC_IPV4,
836 NFCT_FILTER_LOGIC_NEGATIVE);
837 nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4,
838 &filter_ipv4);
840 nfct_filter_set_logic(filter, NFCT_FILTER_SRC_IPV6,
841 NFCT_FILTER_LOGIC_NEGATIVE);
842 nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV6,
843 &filter_ipv6);
845 ret = nfct_filter_attach(nfct_fd(handle), filter);
846 if (ret < 0)
847 panic("Cannot attach filter to handle!\n");
849 nfct_callback_register(handle, NFCT_T_ALL, collector_cb, NULL);
851 collector_load_geoip();
853 flow_list_init(&flow_list);
855 rcu_register_thread();
856 while (!sigint)
857 nfct_catch(handle);
858 rcu_unregister_thread();
860 flow_list_destroy(&flow_list);
862 collector_destroy_geoip();
864 nfct_filter_destroy(filter);
865 nfct_close(handle);
867 pthread_exit(0);
870 int main(int argc, char **argv)
872 pthread_t tid;
873 int ret, c, opt_index, what_cmd = 0;
875 while ((c = getopt_long(argc, argv, short_options, long_options,
876 &opt_index)) != EOF) {
877 switch (c) {
878 case 'T':
879 what_cmd |= INCLUDE_TCP;
880 break;
881 case 'U':
882 what_cmd |= INCLUDE_UDP;
883 break;
884 case 's':
885 show_src = 1;
886 break;
887 case 'L':
888 path_city_db = xstrdup(optarg);
889 break;
890 case 'K':
891 path_country_db = xstrdup(optarg);
892 break;
893 case 'h':
894 help();
895 break;
896 case 'v':
897 version();
898 break;
899 case '?':
900 switch (optopt) {
901 case 'L':
902 case 'K':
903 panic("Option -%c requires an argument!\n",
904 optopt);
905 default:
906 if (isprint(optopt))
907 whine("Unknown option character "
908 "`0x%X\'!\n", optopt);
909 die();
911 default:
912 break;
916 if (what_cmd > 0)
917 what = what_cmd;
919 rcu_init();
921 register_signal(SIGINT, signal_handler);
922 register_signal(SIGHUP, signal_handler);
924 ret = pthread_create(&tid, NULL, collector, NULL);
925 if (ret < 0)
926 panic("Cannot create phthread!\n");
928 presenter();
930 free(path_city_db);
931 free(path_country_db);
933 return 0;