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'.
28 #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
29 #include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
31 #include <GeoIPCity.h>
32 #include <netinet/in.h>
45 #include "dissector_eth.h"
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
;
62 struct flow_entry
*next
;
66 struct flow_entry
*head
;
70 #ifndef ATTR_TIMESTAMP_START
71 # define ATTR_TIMESTAMP_START 63
73 #ifndef ATTR_TIMESTAMP_STOP
74 # define ATTR_TIMESTAMP_STOP 64
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'},
103 static const char *const l3proto2str
[AF_MAX
] = {
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",
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
,
152 TCP_CONNTRACK_SYN_SENT2
,
156 static const struct nfct_filter_ipv4 filter_ipv4
= {
157 .addr
= __constant_htonl(INADDR_LOOPBACK
),
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
)
178 static inline const char *make_n_a(const char *p
)
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",
190 puts("http://www.netsniff-ng.org\n\n"
191 "Usage: flowtop [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"
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");
217 static void version(void)
219 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
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");
231 static inline struct flow_entry
*flow_entry_xalloc(void)
233 struct flow_entry
*n
;
235 n
= xzmalloc(sizeof(*n
));
241 static inline void flow_entry_xfree(struct flow_entry
*n
)
246 static inline void flow_list_init(struct flow_list
*fl
)
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
,
266 struct flow_entry
*n
= rcu_dereference(fl
->head
);
269 if (n
->flow_id
== id
)
272 n
= rcu_dereference(n
->next
);
278 static struct flow_entry
*flow_list_find_prev_id(struct flow_list
*fl
,
281 struct flow_entry
*n
= rcu_dereference(fl
->head
), *tmp
;
283 if (n
->flow_id
== id
)
286 while ((tmp
= rcu_dereference(n
->next
)) != NULL
) {
287 if (tmp
->flow_id
== id
)
296 static void flow_list_update_entry(struct flow_list
*fl
,
297 struct nf_conntrack
*ct
)
300 struct flow_entry
*n
;
302 n
= flow_list_find_id(fl
, nfct_get_attr_u32(ct
, ATTR_ID
));
304 n
= flow_entry_xalloc();
306 rcu_assign_pointer(n
->next
, fl
->head
);
307 rcu_assign_pointer(fl
->head
, n
);
312 flow_entry_from_ct(n
, ct
);
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
);
325 n2
= flow_list_find_prev_id(fl
, id
);
327 rcu_assign_pointer(n2
->next
, n1
->next
);
328 rcu_assign_pointer(n1
->next
, NULL
);
330 flow_entry_xfree(n1
);
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
);
352 spinlock_destroy(&fl
->lock
);
355 static int walk_process(char *process
, struct flow_entry
*n
)
362 if (snprintf(path
, sizeof(path
), "/proc/%s/fd", process
) == -1)
363 panic("giant process name! %s\n", process
);
369 while ((ent
= readdir(dir
))) {
372 if (snprintf(path
, sizeof(path
), "/proc/%s/fd/%s",
373 process
, ent
->d_name
) < 0)
376 if (stat(path
, &statbuf
) < 0)
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);
387 panic("readlink error: %s\n", strerror(errno
));
389 n
->procnum
= atoi(process
);
398 static void walk_processes(struct flow_entry
*n
)
404 /* n->inode must be set */
406 memset(n
->cmdline
, 0, sizeof(n
->cmdline
));
410 dir
= opendir("/proc");
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
);
425 static int get_port_inode(uint16_t port
, int proto
, int is_ip6
)
428 char path
[128], buff
[1024];
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");
439 memset(buff
, 0, sizeof(buff
));
441 while (fgets(buff
, sizeof(buff
), proc
) != NULL
) {
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
) {
454 memset(buff
, 0, sizeof(buff
));
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)); \
465 memcpy(n->elem, buff, sizeof(n->elem)); \
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
);
490 n
->inode
= get_port_inode(ntohs(n
->port_src
), n
->l4_proto
,
491 n
->l3_proto
== AF_INET6
);
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
;
508 if (ntohs(n
->port_src
) == 53 || ntohs(n
->port_dst
) == 53)
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
);
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
));
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
);
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
));
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
)
569 /* XXX: Is there a better way to determine? */
570 if (src
< dst
&& src
< 1024) {
572 } else if (dst
< src
&& dst
< 1024) {
575 tmp1
= lookup_port_tcp(src
);
576 tmp2
= lookup_port_tcp(dst
);
579 } else if (!tmp1
&& tmp2
) {
590 static void presenter_screen_init(WINDOW
**screen
)
592 (*screen
) = initscr();
595 keypad(stdscr
, TRUE
);
596 nodelay(*screen
, TRUE
);
601 static void presenter_screen_update(WINDOW
*screen
, struct flow_list
*fl
,
604 int i
, line
= 3, maxy
;
605 struct flow_entry
*n
;
608 maxy
= getmaxy(screen
);
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
);
619 mvwprintw(screen
, 1, 2, "Kernel netfilter TCP/UDP "
620 "flow statistics, [+%d]", skip_lines
);
624 if (rcu_dereference(fl
->head
) == NULL
)
625 mvwprintw(screen
, line
, 2, "(No active sessions! "
626 "Is netfilter running?)");
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) {
636 if (n
->tcp_state
!= states
[i
] ||
637 (i
!= TCP_CONNTRACK_NONE
&&
638 n
->tcp_state
== TCP_CONNTRACK_NONE
) ||
640 presenter_get_port(n
->port_src
, n
->port_dst
) == 53) {
641 n
= rcu_dereference(n
->next
);
645 if (skip_lines
> 0) {
646 n
= rcu_dereference(n
->next
);
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));
666 if (n
->tcp_state
!= TCP_CONNTRACK_NONE
) {
667 printw("%s -> ", lookup_port_tcp(presenter_get_port(n
->port_src
,
670 printw("%s -> ", lookup_port_udp(presenter_get_port(n
->port_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");
699 n
= rcu_dereference(n
->next
);
709 static inline void presenter_screen_end(void)
714 static void presenter(void)
717 WINDOW
*screen
= NULL
;
719 dissector_init_ethernet(0);
720 presenter_screen_init(&screen
);
722 rcu_register_thread();
739 if (skip_lines
> SCROLL_MAX
)
740 skip_lines
= SCROLL_MAX
;
747 presenter_screen_update(screen
, &flow_list
, skip_lines
);
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
)
763 spinlock_lock(&flow_list
.lock
);
767 flow_list_new_entry(&flow_list
, ct
);
770 flow_list_update_entry(&flow_list
, ct
);
773 flow_list_destroy_entry(&flow_list
, ct
);
779 spinlock_unlock(&flow_list
.lock
);
781 return NFCT_CB_CONTINUE
;
784 static inline GeoIP
*collector_geoip_open(const char *path
, int type
)
787 return GeoIP_open(path
, GEOIP_MMAP_CACHE
);
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
);
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
)
817 struct nfct_handle
*handle
;
818 struct nfct_filter
*filter
;
820 handle
= nfct_open(CONNTRACK
, NFCT_ALL_CT_GROUPS
);
822 panic("Cannot create a nfct handle!\n");
824 filter
= nfct_filter_create();
826 panic("Cannot create a nfct filter!\n");
828 if (what
& INCLUDE_UDP
)
829 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
,
831 if (what
& INCLUDE_TCP
)
832 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
,
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
,
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
,
845 ret
= nfct_filter_attach(nfct_fd(handle
), filter
);
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();
858 rcu_unregister_thread();
860 flow_list_destroy(&flow_list
);
862 collector_destroy_geoip();
864 nfct_filter_destroy(filter
);
870 int main(int argc
, char **argv
)
873 int ret
, c
, opt_index
, what_cmd
= 0;
875 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
876 &opt_index
)) != EOF
) {
879 what_cmd
|= INCLUDE_TCP
;
882 what_cmd
|= INCLUDE_UDP
;
888 path_city_db
= xstrdup(optarg
);
891 path_country_db
= xstrdup(optarg
);
903 panic("Option -%c requires an argument!\n",
907 whine("Unknown option character "
908 "`0x%X\'!\n", optopt
);
921 register_signal(SIGINT
, signal_handler
);
922 register_signal(SIGHUP
, signal_handler
);
924 ret
= pthread_create(&tid
, NULL
, collector
, NULL
);
926 panic("Cannot create phthread!\n");
931 free(path_country_db
);