all: minor: updated copyright year
[netsniff-ng.git] / src / flowtop.c
blobea448026750ab5c6820f4c677e32e45cb6c08861
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2011 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'.
19 * Debian: apt-get install libnetfilter-conntrack3 libnetfilter-conntrack-dev
20 * liburcu0 liburcu-dev
22 * Start conntrack (if not yet running):
23 * iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
24 * iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
29 =head1 NAME
31 flowtop - provide top-like netfilter connection tracking information
33 =head1 SYNOPSIS
35 flowtop [--city-db <path>][--country-db <path>]
36 [-T|--tcp][-U|--udp][-v|--version][-h|--help]
38 =head1 DESCRIPTION
40 flowtop is a tiny tool to print human-readable
41 netfilter connection tracking information.
43 =head1 EXAMPLES
45 =over
47 =item flowtop
49 Show only TCP flows
51 =item flowtop --udp
53 Show only UDP flows
55 =item flowtop --city-db /usr/share/GeoIP/GeoIPCity.dat
57 Use the specified GeoIP city database
59 =item flowtop --country-db /usr/share/GeoIP/GeoIP.dat
61 Use the specified GeoIP country database
63 =back
65 =head1 OPTIONS
67 =over
69 =item -T|--tcp
71 Only show TCP flows (default)
73 =item -U|--udp
75 Only show UDP flows
77 =item --city-db
79 Path to GeoIP city database
81 =item --country-db
83 Path to GeoIP country database
85 =item -v|--version
87 Print version.
89 =item -h|--help
91 Print help text and lists all options.
93 =back
95 =head1 AUTHOR
97 Written by Daniel Borkmann <daniel@netsniff-ng.org>
99 =head1 DOCUMENTATION
101 Documentation by Emmanuel Roullit <emmanuel@netsniff-ng.org>
103 =head1 BUGS
105 Please report bugs to <bugs@netsniff-ng.org>
107 =cut
111 #define _LGPL_SOURCE
112 #include <stdio.h>
113 #include <stdint.h>
114 #include <stdlib.h>
115 #include <signal.h>
116 #include <getopt.h>
117 #include <pthread.h>
118 #include <signal.h>
119 #include <netdb.h>
120 #include <ctype.h>
121 #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
122 #include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
123 #include <GeoIP.h>
124 #include <GeoIPCity.h>
125 #include <netinet/in.h>
126 #include <curses.h>
127 #include <dirent.h>
128 #include <sys/stat.h>
129 #include <urcu.h>
130 #include <libgen.h>
132 #include "die.h"
133 #include "xmalloc.h"
134 #include "xio.h"
135 #include "xsys.h"
136 #include "built_in.h"
137 #include "locking.h"
138 #include "dissector_eth.h"
140 #define INCLUDE_UDP (1 << 0)
141 #define INCLUDE_TCP (1 << 1)
143 #ifndef ATTR_TIMESTAMP_START
144 # define ATTR_TIMESTAMP_START 63
145 #endif
146 #ifndef ATTR_TIMESTAMP_STOP
147 # define ATTR_TIMESTAMP_STOP 64
148 #endif
150 #define SCROLL_MAX 1000
152 struct flow_entry {
153 uint32_t flow_id;
154 int first;
155 struct flow_entry *next;
156 uint32_t use;
157 uint32_t status;
158 uint8_t l3_proto;
159 uint8_t l4_proto;
160 uint32_t ip4_src_addr;
161 uint32_t ip4_dst_addr;
162 uint32_t ip6_src_addr[4];
163 uint32_t ip6_dst_addr[4];
164 uint16_t port_src;
165 uint16_t port_dst;
166 uint8_t tcp_state;
167 uint8_t tcp_flags;
168 uint64_t counter_pkts;
169 uint64_t counter_bytes;
170 uint64_t timestamp_start;
171 uint64_t timestamp_stop;
172 char country_src[128];
173 char city_src[128];
174 char rev_dns_src[256];
175 char country_dst[128];
176 char city_dst[128];
177 char rev_dns_dst[256];
178 int procnum;
179 int inode;
180 char cmdline[256];
183 struct flow_list {
184 struct flow_entry *head;
185 struct spinlock lock;
188 volatile sig_atomic_t sigint = 0;
190 static int what = INCLUDE_TCP;
192 static struct flow_list flow_list;
194 static GeoIP *gi_country = NULL;
195 static GeoIP *gi_city = NULL;
197 static char *path_city_db = NULL, *path_country_db = NULL;
199 static const char *short_options = "vhTULK";
201 static struct option long_options[] = {
202 {"tcp", no_argument, 0, 'T'},
203 {"udp", no_argument, 0, 'U'},
204 {"city-db", required_argument, 0, 'L'},
205 {"country-db", required_argument, 0, 'K'},
206 {"version", no_argument, 0, 'v'},
207 {"help", no_argument, 0, 'h'},
208 {0, 0, 0, 0}
211 const char *const l3proto2str[AF_MAX] = {
212 [AF_INET] = "ipv4",
213 [AF_INET6] = "ipv6",
216 const char *const proto2str[IPPROTO_MAX] = {
217 [IPPROTO_TCP] = "tcp",
218 [IPPROTO_UDP] = "udp",
219 [IPPROTO_UDPLITE] = "udplite",
220 [IPPROTO_ICMP] = "icmp",
221 [IPPROTO_ICMPV6] = "icmpv6",
222 [IPPROTO_SCTP] = "sctp",
223 [IPPROTO_GRE] = "gre",
224 [IPPROTO_DCCP] = "dccp",
225 [IPPROTO_IGMP] = "igmp",
226 [IPPROTO_IPIP] = "ipip",
227 [IPPROTO_EGP] = "egp",
228 [IPPROTO_PUP] = "pup",
229 [IPPROTO_IDP] = "idp",
230 [IPPROTO_RSVP] = "rsvp",
231 [IPPROTO_IPV6] = "ip6tun",
232 [IPPROTO_ESP] = "esp",
233 [IPPROTO_AH] = "ah",
234 [IPPROTO_PIM] = "pim",
235 [IPPROTO_COMP] = "comp",
238 const char *const state2str[TCP_CONNTRACK_MAX] = {
239 [TCP_CONNTRACK_NONE] = "NOSTATE",
240 [TCP_CONNTRACK_SYN_SENT] = "SYN_SENT",
241 [TCP_CONNTRACK_SYN_RECV] = "SYN_RECV",
242 [TCP_CONNTRACK_ESTABLISHED] = "ESTABLISHED",
243 [TCP_CONNTRACK_FIN_WAIT] = "FIN_WAIT",
244 [TCP_CONNTRACK_CLOSE_WAIT] = "CLOSE_WAIT",
245 [TCP_CONNTRACK_LAST_ACK] = "LAST_ACK",
246 [TCP_CONNTRACK_TIME_WAIT] = "TIME_WAIT",
247 [TCP_CONNTRACK_CLOSE] = "CLOSE",
248 [TCP_CONNTRACK_SYN_SENT2] = "SYN_SENT2",
251 const uint8_t states[] = {
252 TCP_CONNTRACK_SYN_SENT,
253 TCP_CONNTRACK_SYN_RECV,
254 TCP_CONNTRACK_ESTABLISHED,
255 TCP_CONNTRACK_FIN_WAIT,
256 TCP_CONNTRACK_CLOSE_WAIT,
257 TCP_CONNTRACK_LAST_ACK,
258 TCP_CONNTRACK_TIME_WAIT,
259 TCP_CONNTRACK_CLOSE,
260 TCP_CONNTRACK_SYN_SENT2,
261 TCP_CONNTRACK_NONE,
264 static void signal_handler(int number)
266 switch (number) {
267 case SIGINT:
268 sigint = 1;
269 break;
270 case SIGHUP:
271 default:
272 break;
276 static void help(void)
278 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
279 VERSION_STRING);
280 printf("http://www.netsniff-ng.org\n\n");
281 printf("Usage: flowtop [options]\n");
282 printf("Options:\n");
283 printf(" -T|--tcp Show only TCP flows (default)\n");
284 printf(" -U|--udp Show only UDP flows\n");
285 printf(" --city-db <path> Specifiy path for geoip city database\n");
286 printf(" --country-db <path> Specifiy path for geoip country database\n");
287 printf(" -v|--version Print version\n");
288 printf(" -h|--help Print this help\n");
289 printf("\n");
290 printf("Examples:\n");
291 printf(" flowtop\n\n");
292 printf("Note:\n");
293 printf(" If netfilter is not running, you can activate it with i.e.:\n");
294 printf(" iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT\n");
295 printf(" iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT\n");
296 printf("\n");
297 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
298 printf("Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
299 printf("Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n");
300 printf("License: GNU GPL version 2\n");
301 printf("This is free software: you are free to change and redistribute it.\n");
302 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
303 die();
306 static void version(void)
308 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
309 VERSION_STRING);
310 printf("http://www.netsniff-ng.org\n\n");
311 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
312 printf("Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
313 printf("Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n");
314 printf("License: GNU GPL version 2\n");
315 printf("This is free software: you are free to change and redistribute it.\n");
316 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
317 die();
320 static void screen_init(WINDOW **screen)
322 (*screen) = initscr();
323 noecho();
324 cbreak();
325 keypad(stdscr, TRUE);
326 nodelay(*screen, TRUE);
327 refresh();
328 wrefresh(*screen);
331 static inline uint16_t get_port(uint16_t src, uint16_t dst)
333 char *tmp1, *tmp2;
335 src = ntohs(src);
336 dst = ntohs(dst);
338 /* XXX: Is there a better way to determine? */
339 if (src < dst && src < 1024) {
340 return src;
341 } else if (dst < src && dst < 1024) {
342 return dst;
343 } else {
344 tmp1 = lookup_port_tcp(src);
345 tmp2 = lookup_port_tcp(dst);
346 if (tmp1 && !tmp2) {
347 return src;
348 } else if (!tmp1 && tmp2) {
349 return dst;
350 } else {
351 if (src < dst)
352 return src;
353 else
354 return dst;
359 static void screen_update(WINDOW *screen, struct flow_list *fl, int skip_lines)
361 int i, line = 3;
362 int maxx, maxy;
363 struct flow_entry *n;
365 curs_set(0);
366 getmaxyx(screen, maxy, maxx);
368 start_color();
369 init_pair(1, COLOR_RED, COLOR_BLACK);
370 init_pair(2, COLOR_BLUE, COLOR_BLACK);
371 init_pair(3, COLOR_YELLOW, COLOR_BLACK);
372 init_pair(4, COLOR_GREEN, COLOR_BLACK);
374 clear();
376 rcu_read_lock();
378 mvwprintw(screen, 1, 2, "Kernel netfilter TCP/UDP flow statistics, [+%d]",
379 skip_lines);
381 if (rcu_dereference(fl->head) == NULL)
382 mvwprintw(screen, line, 2, "(No active sessions! Is netfilter running?)");
384 maxy -= 4;
385 /* Yes, that's lame :-P */
386 for (i = 0; i < sizeof(states); i++) {
387 n = rcu_dereference(fl->head);
389 while (n && maxy > 0) {
390 char tmp[128];
392 if (n->tcp_state != states[i] ||
393 (i != TCP_CONNTRACK_NONE &&
394 n->tcp_state == TCP_CONNTRACK_NONE) ||
395 /* Filter out DNS */
396 get_port(n->port_src, n->port_dst) == 53) {
397 n = rcu_dereference(n->next);
398 continue;
401 if (skip_lines > 0) {
402 n = rcu_dereference(n->next);
403 skip_lines--;
404 continue;
407 snprintf(tmp, sizeof(tmp), "%u/%s", n->procnum,
408 basename(n->cmdline));
409 tmp[sizeof(tmp) - 1] = 0;
411 mvwprintw(screen, line, 2, "[");
412 attron(COLOR_PAIR(3));
413 printw("%s", n->procnum > 0 ? tmp : "bridged(?)");
414 attroff(COLOR_PAIR(3));
415 printw("]:%s:%s[", l3proto2str[n->l3_proto],
416 proto2str[n->l4_proto]);
417 attron(COLOR_PAIR(3));
418 printw("%s", state2str[n->tcp_state]);
419 attroff(COLOR_PAIR(3));
420 printw("]:");
421 attron(A_BOLD);
422 if (n->tcp_state != TCP_CONNTRACK_NONE) {
423 printw("%s -> ", lookup_port_tcp(get_port(n->port_src,
424 n->port_dst)));
425 } else {
426 printw("%s -> ", lookup_port_udp(get_port(n->port_src,
427 n->port_dst)));
429 attroff(A_BOLD);
430 attron(COLOR_PAIR(1));
431 printw("%s", n->rev_dns_src);
432 attroff(COLOR_PAIR(1));
433 printw(":%u (", ntohs(n->port_src));
434 attron(COLOR_PAIR(4));
435 printw("%s", (strlen(n->country_src) > 0 ?
436 n->country_src : "N/A"));
437 attroff(COLOR_PAIR(4));
438 printw(", %s) => ", (strlen(n->city_src) > 0 ?
439 n->city_src : "N/A"));
440 attron(COLOR_PAIR(2));
441 printw("%s", n->rev_dns_dst);
442 attroff(COLOR_PAIR(2));
443 printw(":%u (", ntohs(n->port_dst));
444 attron(COLOR_PAIR(4));
445 printw("%s", strlen(n->country_dst) > 0 ?
446 n->country_dst : "N/A");
447 attroff(COLOR_PAIR(4));
448 printw(", %s)", strlen(n->city_dst) > 0 ?
449 n->city_dst : "N/A");
451 line++;
452 maxy--;
453 n = rcu_dereference(n->next);
457 rcu_read_unlock();
459 wrefresh(screen);
460 refresh();
463 static void screen_end(void)
465 endwin();
468 static void presenter(void)
470 int skip_lines = 0;
471 WINDOW *screen = NULL;
473 dissector_init_ethernet(0);
474 screen_init(&screen);
475 rcu_register_thread();
477 while (!sigint) {
478 switch (getch()) {
479 case 'q':
480 sigint = 1;
481 break;
482 case KEY_UP:
483 case 'u':
484 case 'k':
485 skip_lines--;
486 if (skip_lines < 0)
487 skip_lines = 0;
488 break;
489 case KEY_DOWN:
490 case 'd':
491 case 'j':
492 skip_lines++;
493 if (skip_lines > SCROLL_MAX)
494 skip_lines = SCROLL_MAX;
495 break;
496 default:
497 fflush(stdin);
498 break;
501 screen_update(screen, &flow_list, skip_lines);
502 usleep(100000);
505 rcu_unregister_thread();
506 screen_end();
507 dissector_cleanup_ethernet();
510 static inline const char *make_n_a(const char *p)
512 return p ? : "N/A";
515 static void walk_process(char *process, struct flow_entry *n)
517 int rc;
518 DIR *dir;
519 struct dirent *ent;
520 char path[1024];
522 if (snprintf(path, sizeof(path), "/proc/%s/fd", process) == -1)
523 panic("giant process name! %s\n", process);
525 dir = opendir(path);
526 if (!dir)
527 return;
529 while ((ent = readdir(dir))) {
530 struct stat statbuf;
532 if (snprintf(path, sizeof(path), "/proc/%s/fd/%s",
533 process, ent->d_name) < 0)
534 continue;
535 if (stat(path, &statbuf) < 0)
536 continue;
537 if (S_ISSOCK(statbuf.st_mode) && n->inode == statbuf.st_ino) {
538 memset(n->cmdline, 0, sizeof(n->cmdline));
539 snprintf(path, sizeof(path), "/proc/%s/exe", process);
540 rc = readlink(path, n->cmdline, sizeof(n->cmdline) - 1);
542 if (rc < 0)
543 panic("readlink error: %s\n", strerror(errno));
545 n->procnum = atoi(process);
549 closedir(dir);
552 /* Derived from ifpromisc, Fred N. van Kempen, GPL v2.0 */
553 /* n->inode must be set */
554 static void walk_processes(struct flow_entry *n)
556 DIR *dir;
557 struct dirent *ent;
559 if (n->inode <= 0) {
560 memset(n->cmdline, 0, sizeof(n->cmdline));
561 return;
564 dir = opendir("/proc");
565 if (!dir)
566 panic("Cannot open /proc!\n");
568 while ((ent = readdir(dir)))
569 if (strspn(ent->d_name, "0123456789") == strlen(ent->d_name))
570 walk_process(ent->d_name, n);
572 closedir(dir);
575 static int get_inode_from_local_port(int port, const char *proto, int ip6)
577 int ret = -ENOENT;
578 char path[128];
579 char buff[1024];
580 FILE *proc;
582 memset(path, 0, sizeof(path));
583 snprintf(path, sizeof(path), "/proc/net/%s%s", proto, ip6 ? "6" : "");
584 proc = fopen(path, "r");
585 if (!proc)
586 return -EIO;
587 memset(buff, 0, sizeof(buff));
588 while (fgets(buff, sizeof(buff), proc) != NULL) {
589 int lport = 0, inode = 0;
590 buff[sizeof(buff) - 1] = 0;
591 if (sscanf(buff, "%*u: %*X:%X %*X:%*X %*X %*X:%*X %*X:%*X "
592 "%*X %*u %*u %u", &lport, &inode) == 2) {
593 if (lport == port) {
594 ret = inode;
595 break;
598 memset(buff, 0, sizeof(buff));
600 fclose(proc);
602 return ret;
605 static void flow_entry_from_ct(struct flow_entry *n, struct nf_conntrack *ct)
607 n->flow_id = nfct_get_attr_u32(ct, ATTR_ID);
608 n->use = nfct_get_attr_u32(ct, ATTR_USE);
609 n->status = nfct_get_attr_u32(ct, ATTR_STATUS);
610 n->l3_proto = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
611 n->l4_proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
612 n->ip4_src_addr = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC);
613 n->ip4_dst_addr = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST);
615 const uint8_t *ipv6_src = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
616 if (ipv6_src)
617 memcpy(n->ip6_src_addr, ipv6_src, sizeof(n->ip6_src_addr));
618 const uint8_t *ipv6_dst = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST);
619 if (ipv6_dst)
620 memcpy(n->ip6_dst_addr, ipv6_dst, sizeof(n->ip6_dst_addr));
622 n->port_src = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC);
623 n->port_dst = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
624 n->tcp_state = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
625 n->tcp_flags = nfct_get_attr_u8(ct, ATTR_TCP_FLAGS_ORIG);
626 n->counter_pkts = nfct_get_attr_u64(ct, ATTR_ORIG_COUNTER_PACKETS);
627 n->counter_bytes = nfct_get_attr_u64(ct, ATTR_ORIG_COUNTER_BYTES);
628 n->timestamp_start = nfct_get_attr_u64(ct, ATTR_TIMESTAMP_START);
629 n->timestamp_stop = nfct_get_attr_u64(ct, ATTR_TIMESTAMP_STOP);
631 if (n->first) {
632 n->inode = get_inode_from_local_port(ntohs(n->port_src),
633 proto2str[n->l4_proto],
634 !!(ipv6_src));
635 if (n->inode > 0)
636 walk_processes(n);
638 /* if this really runs on a router, we try it once and then let it be */
639 n->first = 0;
642 /* TODO: IP4 + IP6 */
643 static void flow_entry_get_extended(struct flow_entry *n)
645 struct sockaddr_in sa;
646 struct hostent *hent;
647 GeoIPRecord *gir_src, *gir_dst;
649 if (n->flow_id == 0)
650 return;
651 if (ntohs(n->port_src) == 53 || ntohs(n->port_dst) == 53)
652 return;
654 memset(&sa, 0, sizeof(sa));
655 sa.sin_family = PF_INET; //XXX: IPv4
656 sa.sin_addr.s_addr = n->ip4_src_addr;
657 getnameinfo((struct sockaddr *) &sa, sizeof(sa), n->rev_dns_src,
658 sizeof(n->rev_dns_src), NULL, 0, NI_NUMERICHOST);
660 hent = gethostbyaddr(&sa.sin_addr, sizeof(sa.sin_addr), PF_INET);
661 if (hent) {
662 memset(n->rev_dns_src, 0, sizeof(n->rev_dns_src));
663 memcpy(n->rev_dns_src, hent->h_name,
664 min(sizeof(n->rev_dns_src), strlen(hent->h_name)));
667 gir_src = GeoIP_record_by_ipnum(gi_city, ntohl(n->ip4_src_addr));
668 if (gir_src) {
669 const char *country =
670 make_n_a(GeoIP_country_name_by_ipnum(gi_country,
671 ntohl(n->ip4_src_addr)));
672 const char *city = make_n_a(gir_src->city);
673 memcpy(n->country_src, country,
674 min(sizeof(n->country_src), strlen(country)));
675 memcpy(n->city_src, city,
676 min(sizeof(n->city_src), strlen(city)));
679 memset(&sa, 0, sizeof(sa));
680 sa.sin_family = PF_INET; //XXX: IPv4
681 sa.sin_addr.s_addr = n->ip4_dst_addr;
682 getnameinfo((struct sockaddr *) &sa, sizeof(sa), n->rev_dns_dst,
683 sizeof(n->rev_dns_dst), NULL, 0, NI_NUMERICHOST);
685 hent = gethostbyaddr(&sa.sin_addr, sizeof(sa.sin_addr), PF_INET);
686 if (hent) {
687 memset(n->rev_dns_dst, 0, sizeof(n->rev_dns_dst));
688 memcpy(n->rev_dns_dst, hent->h_name,
689 min(sizeof(n->rev_dns_dst), strlen(hent->h_name)));
692 gir_dst = GeoIP_record_by_ipnum(gi_city, ntohl(n->ip4_dst_addr));
693 if (gir_dst) {
694 const char *country =
695 make_n_a(GeoIP_country_name_by_ipnum(gi_country,
696 ntohl(n->ip4_dst_addr)));
697 const char *city = make_n_a(gir_dst->city);
698 memcpy(n->country_dst, country,
699 min(sizeof(n->country_dst), strlen(country)));
700 memcpy(n->city_dst, city,
701 min(sizeof(n->city_dst), strlen(city)));
705 static void flow_list_init(struct flow_list *fl)
707 fl->head = NULL;
708 spinlock_init(&fl->lock);
711 static struct flow_entry *__flow_list_find_by_id(struct flow_list *fl, uint32_t id)
713 struct flow_entry *n = rcu_dereference(fl->head);
714 while (n != NULL) {
715 if (n->flow_id == id)
716 return n;
717 n = rcu_dereference(n->next);
719 return NULL;
722 static struct flow_entry *__flow_list_find_prev_by_id(struct flow_list *fl, uint32_t id)
724 struct flow_entry *n = rcu_dereference(fl->head);
725 if (n->flow_id == id)
726 return NULL;
727 while (rcu_dereference(n->next) != NULL) {
728 if (rcu_dereference(n->next)->flow_id == id)
729 return n;
730 n = rcu_dereference(n->next);
732 return NULL;
735 static void flow_list_new_entry(struct flow_list *fl, struct nf_conntrack *ct)
737 struct flow_entry *n = xzmalloc(sizeof(*n));
738 n->first = 1;
739 rcu_assign_pointer(n->next, fl->head);
740 rcu_assign_pointer(fl->head, n);
741 flow_entry_from_ct(n, ct);
742 flow_entry_get_extended(n);
745 static void flow_list_update_entry(struct flow_list *fl, struct nf_conntrack *ct)
747 int do_ext = 0;
748 uint32_t id = nfct_get_attr_u32(ct, ATTR_ID);
749 struct flow_entry *n;
750 n = __flow_list_find_by_id(fl, id);
751 if (n == NULL) {
752 n = xzmalloc(sizeof(*n));
753 n->first = 1;
754 rcu_assign_pointer(n->next, fl->head);
755 rcu_assign_pointer(fl->head, n);
756 do_ext = 1;
758 flow_entry_from_ct(n, ct);
759 if (do_ext)
760 flow_entry_get_extended(n);
763 static void flow_list_destroy_entry(struct flow_list *fl, struct nf_conntrack *ct)
765 uint32_t id = nfct_get_attr_u32(ct, ATTR_ID);
766 struct flow_entry *n1, *n2;
768 n1 = __flow_list_find_by_id(fl, id);
769 if (n1) {
770 n2 = __flow_list_find_prev_by_id(fl, id);
771 if (n2) {
772 rcu_assign_pointer(n2->next, n1->next);
773 rcu_assign_pointer(n1->next, NULL);
774 xfree(n1);
775 } else {
776 xfree(fl->head);
777 rcu_assign_pointer(fl->head, NULL);
782 static void flow_list_destroy(struct flow_list *fl)
784 struct flow_entry *n;
786 while (fl->head != NULL) {
787 n = rcu_dereference(fl->head->next);
788 rcu_assign_pointer(fl->head->next, NULL);
789 xfree(fl->head);
790 rcu_assign_pointer(fl->head, n);
793 synchronize_rcu();
794 spinlock_destroy(&fl->lock);
797 static int collector_cb(enum nf_conntrack_msg_type type,
798 struct nf_conntrack *ct,
799 void *data)
801 if (sigint)
802 return NFCT_CB_STOP;
804 synchronize_rcu();
806 spinlock_lock(&flow_list.lock);
807 switch (type) {
808 case NFCT_T_NEW:
809 flow_list_new_entry(&flow_list, ct);
810 break;
811 case NFCT_T_UPDATE:
812 flow_list_update_entry(&flow_list, ct);
813 break;
814 case NFCT_T_DESTROY:
815 flow_list_destroy_entry(&flow_list, ct);
816 break;
817 default:
818 break;
820 spinlock_unlock(&flow_list.lock);
822 return NFCT_CB_CONTINUE;
825 static int dummy_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct,
826 void *data)
828 return NFCT_CB_STOP;
831 static void *collector(void *null)
833 int ret;
834 u_int32_t family = AF_INET;
835 struct nfct_handle *handle;
836 struct nfct_filter *filter;
838 handle = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
839 if (!handle)
840 panic("Cannot create a nfct handle!\n");
842 /* Hack: inits ct */
843 nfct_callback_register(handle, NFCT_T_ALL, dummy_cb, NULL);
844 nfct_query(handle, NFCT_Q_DUMP, &family);
845 nfct_close(handle);
847 handle = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
848 if (!handle)
849 panic("Cannot create a nfct handle!\n");
851 nfct_query(handle, NFCT_Q_FLUSH, &family);
853 filter = nfct_filter_create();
854 if (!filter)
855 panic("Cannot create a nfct filter!\n");
856 if (what & INCLUDE_UDP)
857 nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, IPPROTO_UDP);
858 if (what & INCLUDE_TCP)
859 nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, IPPROTO_TCP);
861 struct nfct_filter_ipv4 filter_ipv4 = {
862 .addr = ntohl(INADDR_LOOPBACK),
863 .mask = 0xffffffff,
866 nfct_filter_set_logic(filter, NFCT_FILTER_SRC_IPV4,
867 NFCT_FILTER_LOGIC_NEGATIVE);
868 nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, &filter_ipv4);
870 struct nfct_filter_ipv6 filter_ipv6 = {
871 .addr = { 0x0, 0x0, 0x0, 0x1 },
872 .mask = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
875 nfct_filter_set_logic(filter, NFCT_FILTER_SRC_IPV6,
876 NFCT_FILTER_LOGIC_NEGATIVE);
877 nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV6, &filter_ipv6);
879 ret = nfct_filter_attach(nfct_fd(handle), filter);
880 if (ret < 0)
881 panic("Cannot attach filter to handle!\n");
883 nfct_filter_destroy(filter);
885 if (path_country_db)
886 gi_country = GeoIP_open(path_country_db, GEOIP_MMAP_CACHE);
887 else
888 gi_country = GeoIP_open_type(GEOIP_COUNTRY_EDITION,
889 GEOIP_MMAP_CACHE);
891 if (path_city_db)
892 gi_city = GeoIP_open(path_city_db, GEOIP_MMAP_CACHE);
893 else
894 gi_city = GeoIP_open_type(GEOIP_CITY_EDITION_REV1,
895 GEOIP_MMAP_CACHE);
896 if (!gi_country || !gi_city)
897 panic("Cannot open GeoIP database!\n");
899 GeoIP_set_charset(gi_country, GEOIP_CHARSET_UTF8);
900 GeoIP_set_charset(gi_city, GEOIP_CHARSET_UTF8);
902 flow_list_init(&flow_list);
904 rcu_register_thread();
906 nfct_callback_register(handle, NFCT_T_ALL, collector_cb, NULL);
908 while (!sigint)
909 nfct_catch(handle);
911 rcu_unregister_thread();
913 flow_list_destroy(&flow_list);
915 GeoIP_delete(gi_city);
916 GeoIP_delete(gi_country);
918 nfct_close(handle);
920 if (path_city_db)
921 xfree(path_city_db);
922 if (path_country_db)
923 xfree(path_country_db);
925 pthread_exit(0);
928 int main(int argc, char **argv)
930 pthread_t tid;
931 int ret, c, opt_index, what_cmd = 0;
933 check_for_root_maybe_die();
935 while ((c = getopt_long(argc, argv, short_options, long_options,
936 &opt_index)) != EOF) {
937 switch (c) {
938 case 'T':
939 what_cmd |= INCLUDE_TCP;
940 break;
941 case 'U':
942 what_cmd |= INCLUDE_UDP;
943 break;
944 case 'L':
945 path_city_db = xstrdup(optarg);
946 break;
947 case 'K':
948 path_country_db = xstrdup(optarg);
949 break;
950 case 'h':
951 help();
952 break;
953 case 'v':
954 version();
955 break;
956 case '?':
957 switch (optopt) {
958 case 'L':
959 case 'K':
960 panic("Option -%c requires an argument!\n",
961 optopt);
962 default:
963 if (isprint(optopt))
964 whine("Unknown option character "
965 "`0x%X\'!\n", optopt);
966 die();
968 default:
969 break;
973 if (what_cmd > 0)
974 what = what_cmd;
976 rcu_init();
978 register_signal(SIGINT, signal_handler);
979 register_signal(SIGHUP, signal_handler);
981 ret = pthread_create(&tid, NULL, collector, NULL);
982 if (ret < 0)
983 panic("Cannot create phthread!\n");
985 presenter();
986 return 0;