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"
54 uint32_t flow_id
, use
, status
;
55 uint8_t l3_proto
, l4_proto
;
56 uint32_t ip4_src_addr
, ip4_dst_addr
;
57 uint32_t ip6_src_addr
[4], ip6_dst_addr
[4];
58 uint16_t port_src
, port_dst
;
59 uint8_t tcp_state
, tcp_flags
;
60 uint64_t counter_pkts
, counter_bytes
;
61 uint64_t timestamp_start
, timestamp_stop
;
62 char country_src
[128], country_dst
[128];
63 char city_src
[128], city_dst
[128];
64 char rev_dns_src
[256], rev_dns_dst
[256];
65 int first
, procnum
, inode
;
67 struct flow_entry
*next
;
71 struct flow_entry
*head
;
75 #ifndef ATTR_TIMESTAMP_START
76 # define ATTR_TIMESTAMP_START 63
78 #ifndef ATTR_TIMESTAMP_STOP
79 # define ATTR_TIMESTAMP_STOP 64
82 #define SCROLL_MAX 1000
84 #define INCLUDE_UDP (1 << 0)
85 #define INCLUDE_TCP (1 << 1)
87 volatile sig_atomic_t sigint
= 0;
89 static int what
= INCLUDE_TCP
, show_src
= 0;
91 struct geo_ip_db geo_country
, geo_city
;
93 static struct flow_list flow_list
;
95 static const char *short_options
= "vhTULKs";
96 static const struct option long_options
[] = {
97 {"tcp", no_argument
, NULL
, 'T'},
98 {"udp", no_argument
, NULL
, 'U'},
99 {"show-src", no_argument
, NULL
, 's'},
100 {"city-db4", required_argument
, NULL
, 'L'},
101 {"country-db4", required_argument
, NULL
, 'K'},
102 {"city-db6", required_argument
, NULL
, 'O'},
103 {"country-db6", required_argument
, NULL
, 'P'},
104 {"version", no_argument
, NULL
, 'v'},
105 {"help", no_argument
, NULL
, 'h'},
109 static const char *const l3proto2str
[AF_MAX
] = {
114 static const char *const proto2str
[IPPROTO_MAX
] = {
115 [IPPROTO_TCP
] = "tcp",
116 [IPPROTO_UDP
] = "udp",
117 [IPPROTO_UDPLITE
] = "udplite",
118 [IPPROTO_ICMP
] = "icmp",
119 [IPPROTO_ICMPV6
] = "icmpv6",
120 [IPPROTO_SCTP
] = "sctp",
121 [IPPROTO_GRE
] = "gre",
122 [IPPROTO_DCCP
] = "dccp",
123 [IPPROTO_IGMP
] = "igmp",
124 [IPPROTO_IPIP
] = "ipip",
125 [IPPROTO_EGP
] = "egp",
126 [IPPROTO_PUP
] = "pup",
127 [IPPROTO_IDP
] = "idp",
128 [IPPROTO_RSVP
] = "rsvp",
129 [IPPROTO_IPV6
] = "ip6tun",
130 [IPPROTO_ESP
] = "esp",
132 [IPPROTO_PIM
] = "pim",
133 [IPPROTO_COMP
] = "comp",
136 static const char *const state2str
[TCP_CONNTRACK_MAX
] = {
137 [TCP_CONNTRACK_NONE
] = "NOSTATE",
138 [TCP_CONNTRACK_SYN_SENT
] = "SYN_SENT",
139 [TCP_CONNTRACK_SYN_RECV
] = "SYN_RECV",
140 [TCP_CONNTRACK_ESTABLISHED
] = "ESTABLISHED",
141 [TCP_CONNTRACK_FIN_WAIT
] = "FIN_WAIT",
142 [TCP_CONNTRACK_CLOSE_WAIT
] = "CLOSE_WAIT",
143 [TCP_CONNTRACK_LAST_ACK
] = "LAST_ACK",
144 [TCP_CONNTRACK_TIME_WAIT
] = "TIME_WAIT",
145 [TCP_CONNTRACK_CLOSE
] = "CLOSE",
146 [TCP_CONNTRACK_SYN_SENT2
] = "SYN_SENT2",
149 static const uint8_t states
[] = {
150 TCP_CONNTRACK_SYN_SENT
,
151 TCP_CONNTRACK_SYN_RECV
,
152 TCP_CONNTRACK_ESTABLISHED
,
153 TCP_CONNTRACK_FIN_WAIT
,
154 TCP_CONNTRACK_CLOSE_WAIT
,
155 TCP_CONNTRACK_LAST_ACK
,
156 TCP_CONNTRACK_TIME_WAIT
,
158 TCP_CONNTRACK_SYN_SENT2
,
162 static const struct nfct_filter_ipv4 filter_ipv4
= {
163 .addr
= __constant_htonl(INADDR_LOOPBACK
),
167 static const struct nfct_filter_ipv6 filter_ipv6
= {
168 .addr
= { 0x0, 0x0, 0x0, 0x1 },
169 .mask
= { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
172 static void signal_handler(int number
)
184 static void flow_entry_from_ct(struct flow_entry
*n
, struct nf_conntrack
*ct
);
185 static void flow_entry_get_extended(struct flow_entry
*n
);
187 static void help(void)
189 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
191 puts("http://www.netsniff-ng.org\n\n"
192 "Usage: flowtop [options]\n"
194 " -T|--tcp Show only TCP flows (default)\n"
195 " -U|--udp Show only UDP flows\n"
196 " -s|--show-src Also show source, not only dest\n"
197 " --city-db4 <path> Specifiy path for geoip4 city database\n"
198 " --country-db4 <path> Specifiy path for geoip4 country database\n"
199 " --city-db6 <path> Specifiy path for geoip6 city database\n"
200 " --country-db6 <path> Specifiy path for geoip6 country database\n"
201 " -v|--version Print version\n"
202 " -h|--help Print this help\n\n"
207 " If netfilter is not running, you can activate it with i.e.:\n"
208 " iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT\n"
209 " iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT\n\n"
210 "Please report bugs to <bugs@netsniff-ng.org>\n"
211 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
212 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n"
213 "License: GNU GPL version 2.0\n"
214 "This is free software: you are free to change and redistribute it.\n"
215 "There is NO WARRANTY, to the extent permitted by law.\n");
219 static void version(void)
221 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
223 puts("http://www.netsniff-ng.org\n\n"
224 "Please report bugs to <bugs@netsniff-ng.org>\n"
225 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
226 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n"
227 "License: GNU GPL version 2.0\n"
228 "This is free software: you are free to change and redistribute it.\n"
229 "There is NO WARRANTY, to the extent permitted by law.\n");
233 static inline struct flow_entry
*flow_entry_xalloc(void)
235 struct flow_entry
*n
;
237 n
= xzmalloc(sizeof(*n
));
243 static inline void flow_entry_xfree(struct flow_entry
*n
)
248 static inline void flow_list_init(struct flow_list
*fl
)
251 spinlock_init(&fl
->lock
);
254 static void flow_list_new_entry(struct flow_list
*fl
, struct nf_conntrack
*ct
)
256 struct flow_entry
*n
= flow_entry_xalloc();
258 rcu_assign_pointer(n
->next
, fl
->head
);
259 rcu_assign_pointer(fl
->head
, n
);
261 flow_entry_from_ct(n
, ct
);
262 flow_entry_get_extended(n
);
265 static struct flow_entry
*flow_list_find_id(struct flow_list
*fl
,
268 struct flow_entry
*n
= rcu_dereference(fl
->head
);
271 if (n
->flow_id
== id
)
274 n
= rcu_dereference(n
->next
);
280 static struct flow_entry
*flow_list_find_prev_id(struct flow_list
*fl
,
283 struct flow_entry
*n
= rcu_dereference(fl
->head
), *tmp
;
285 if (n
->flow_id
== id
)
288 while ((tmp
= rcu_dereference(n
->next
)) != NULL
) {
289 if (tmp
->flow_id
== id
)
298 static void flow_list_update_entry(struct flow_list
*fl
,
299 struct nf_conntrack
*ct
)
302 struct flow_entry
*n
;
304 n
= flow_list_find_id(fl
, nfct_get_attr_u32(ct
, ATTR_ID
));
306 n
= flow_entry_xalloc();
308 rcu_assign_pointer(n
->next
, fl
->head
);
309 rcu_assign_pointer(fl
->head
, n
);
314 flow_entry_from_ct(n
, ct
);
316 flow_entry_get_extended(n
);
319 static void flow_list_destroy_entry(struct flow_list
*fl
,
320 struct nf_conntrack
*ct
)
322 struct flow_entry
*n1
, *n2
;
323 uint32_t id
= nfct_get_attr_u32(ct
, ATTR_ID
);
325 n1
= flow_list_find_id(fl
, id
);
327 n2
= flow_list_find_prev_id(fl
, id
);
329 rcu_assign_pointer(n2
->next
, n1
->next
);
330 rcu_assign_pointer(n1
->next
, NULL
);
332 flow_entry_xfree(n1
);
334 flow_entry_xfree(fl
->head
);
336 rcu_assign_pointer(fl
->head
, NULL
);
341 static void flow_list_destroy(struct flow_list
*fl
)
343 struct flow_entry
*n
;
345 while (fl
->head
!= NULL
) {
346 n
= rcu_dereference(fl
->head
->next
);
347 rcu_assign_pointer(fl
->head
->next
, NULL
);
349 flow_entry_xfree(fl
->head
);
350 rcu_assign_pointer(fl
->head
, n
);
354 spinlock_destroy(&fl
->lock
);
357 static int walk_process(char *process
, struct flow_entry
*n
)
364 if (snprintf(path
, sizeof(path
), "/proc/%s/fd", process
) == -1)
365 panic("giant process name! %s\n", process
);
371 while ((ent
= readdir(dir
))) {
374 if (snprintf(path
, sizeof(path
), "/proc/%s/fd/%s",
375 process
, ent
->d_name
) < 0)
378 if (stat(path
, &statbuf
) < 0)
381 if (S_ISSOCK(statbuf
.st_mode
) && n
->inode
== statbuf
.st_ino
) {
382 memset(n
->cmdline
, 0, sizeof(n
->cmdline
));
384 snprintf(path
, sizeof(path
), "/proc/%s/exe", process
);
386 ret
= readlink(path
, n
->cmdline
,
387 sizeof(n
->cmdline
) - 1);
389 panic("readlink error: %s\n", strerror(errno
));
391 n
->procnum
= atoi(process
);
400 static void walk_processes(struct flow_entry
*n
)
406 /* n->inode must be set */
408 memset(n
->cmdline
, 0, sizeof(n
->cmdline
));
412 dir
= opendir("/proc");
414 panic("Cannot open /proc!\n");
416 while ((ent
= readdir(dir
))) {
417 if (strspn(ent
->d_name
, "0123456789") == strlen(ent
->d_name
)) {
418 ret
= walk_process(ent
->d_name
, n
);
427 static int get_port_inode(uint16_t port
, int proto
, int is_ip6
)
430 char path
[128], buff
[1024];
433 memset(path
, 0, sizeof(path
));
434 snprintf(path
, sizeof(path
), "/proc/net/%s%s",
435 proto2str
[proto
], is_ip6
? "6" : "");
437 proc
= fopen(path
, "r");
441 memset(buff
, 0, sizeof(buff
));
443 while (fgets(buff
, sizeof(buff
), proc
) != NULL
) {
445 unsigned int lport
= 0;
447 buff
[sizeof(buff
) - 1] = 0;
448 if (sscanf(buff
, "%*u: %*X:%X %*X:%*X %*X %*X:%*X %*X:%*X "
449 "%*X %*u %*u %u", &lport
, &inode
) == 2) {
450 if ((uint16_t) lport
== port
) {
456 memset(buff
, 0, sizeof(buff
));
463 #define CP_NFCT(elem, attr, x) \
464 do { n->elem = nfct_get_attr_u##x(ct,(attr)); } while (0)
465 #define CP_NFCT_BUFF(elem, attr) do { \
466 const uint8_t *buff = nfct_get_attr(ct,(attr)); \
468 memcpy(n->elem, buff, sizeof(n->elem)); \
471 static void flow_entry_from_ct(struct flow_entry
*n
, struct nf_conntrack
*ct
)
473 CP_NFCT(l3_proto
, ATTR_ORIG_L3PROTO
, 8);
474 CP_NFCT(l4_proto
, ATTR_ORIG_L4PROTO
, 8);
475 CP_NFCT(ip4_src_addr
, ATTR_ORIG_IPV4_SRC
, 32);
476 CP_NFCT(ip4_dst_addr
, ATTR_ORIG_IPV4_DST
, 32);
477 CP_NFCT(port_src
, ATTR_ORIG_PORT_SRC
, 16);
478 CP_NFCT(port_dst
, ATTR_ORIG_PORT_DST
, 16);
479 CP_NFCT(status
, ATTR_STATUS
, 32);
480 CP_NFCT(tcp_state
, ATTR_TCP_STATE
, 8);
481 CP_NFCT(tcp_flags
, ATTR_TCP_FLAGS_ORIG
, 8);
482 CP_NFCT(counter_pkts
, ATTR_ORIG_COUNTER_PACKETS
, 64);
483 CP_NFCT(counter_bytes
, ATTR_ORIG_COUNTER_BYTES
, 64);
484 CP_NFCT(timestamp_start
, ATTR_TIMESTAMP_START
, 64);
485 CP_NFCT(timestamp_stop
, ATTR_TIMESTAMP_STOP
, 64);
486 CP_NFCT(flow_id
, ATTR_ID
, 32);
487 CP_NFCT(use
, ATTR_USE
, 32);
489 CP_NFCT_BUFF(ip6_src_addr
, ATTR_ORIG_IPV6_SRC
);
490 CP_NFCT_BUFF(ip6_dst_addr
, ATTR_ORIG_IPV6_DST
);
492 n
->port_src
= ntohs(n
->port_src
);
493 n
->port_dst
= ntohs(n
->port_dst
);
495 n
->ip4_src_addr
= ntohl(n
->ip4_src_addr
);
496 n
->ip4_dst_addr
= ntohl(n
->ip4_dst_addr
);
499 n
->inode
= get_port_inode(n
->port_src
, n
->l4_proto
,
500 n
->l3_proto
== AF_INET6
);
508 enum flow_entry_direction
{
513 static inline int flow_entry_get_extended_is_dns(struct flow_entry
*n
)
515 /* We don't want to analyze / display DNS itself, since we
516 * use it to resolve reverse dns.
518 return n
->port_src
== 53 || n
->port_dst
== 53;
521 #define SELFLD(dir,src_member,dst_member) \
522 (((dir) == flow_entry_src) ? n->src_member : n->dst_member)
524 static struct sockaddr_in
*
525 flow_entry_get_sain4_obj(struct flow_entry
*n
, enum flow_entry_direction dir
,
526 struct sockaddr_in
*sa
)
528 memset(sa
, 0, sizeof(*sa
));
529 sa
->sin_family
= PF_INET
;
530 sa
->sin_addr
.s_addr
= htonl(SELFLD(dir
, ip4_src_addr
, ip4_dst_addr
));
535 static struct sockaddr_in6
*
536 flow_entry_get_sain6_obj(struct flow_entry
*n
, enum flow_entry_direction dir
,
537 struct sockaddr_in6
*sa
)
539 memset(sa
, 0, sizeof(*sa
));
540 sa
->sin6_family
= PF_INET6
;
542 memcpy(&sa
->sin6_addr
, SELFLD(dir
, ip6_src_addr
, ip6_dst_addr
),
543 sizeof(SELFLD(dir
, ip6_src_addr
, ip6_dst_addr
)));
549 flow_entry_geo_city_lookup_generic(struct flow_entry
*n
,
550 enum flow_entry_direction dir
)
552 GeoIPRecord
*gir
= NULL
;
553 struct sockaddr_in sa4
;
554 struct sockaddr_in6 sa6
;
555 inline const char *make_na(const char *p
) { return p
? : "N/A"; }
556 const char *city
= NULL
;
558 switch (n
->l3_proto
) {
563 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
564 gir
= GeoIP_record_by_ipnum(geo_city
.gi4
,
565 ntohl(sa4
.sin_addr
.s_addr
));
569 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
570 gir
= GeoIP_record_by_ipnum_v6(geo_city
.gi6
, sa6
.sin6_addr
);
574 city
= make_na(gir
!= NULL
? gir
->city
: city
);
576 bug_on(sizeof(n
->city_src
) != sizeof(n
->city_dst
));
577 memcpy(SELFLD(dir
, city_src
, city_dst
), city
,
578 min(sizeof(n
->city_src
), strlen(city
)));
582 flow_entry_geo_country_lookup_generic(struct flow_entry
*n
,
583 enum flow_entry_direction dir
)
585 struct sockaddr_in sa4
;
586 struct sockaddr_in6 sa6
;
587 inline const char *make_na(const char *p
) { return p
? : "N/A"; }
588 const char *country
= NULL
;
590 switch (n
->l3_proto
) {
595 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
596 country
= GeoIP_country_name_by_ipnum(geo_country
.gi4
,
597 ntohl(sa4
.sin_addr
.s_addr
));
601 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
602 country
= GeoIP_country_name_by_ipnum_v6(geo_country
.gi6
,
607 country
= make_na(country
);
609 bug_on(sizeof(n
->country_src
) != sizeof(n
->country_dst
));
610 memcpy(SELFLD(dir
, country_src
, country_dst
), country
,
611 min(sizeof(n
->country_src
), strlen(country
)));
614 static void flow_entry_get_extended_geo(struct flow_entry
*n
,
615 enum flow_entry_direction dir
)
617 flow_entry_geo_city_lookup_generic(n
, dir
);
618 flow_entry_geo_country_lookup_generic(n
, dir
);
621 static void flow_entry_get_extended_revdns(struct flow_entry
*n
,
622 enum flow_entry_direction dir
)
625 struct sockaddr_in sa4
;
626 struct sockaddr_in6 sa6
;
628 struct hostent
*hent
;
630 switch (n
->l3_proto
) {
635 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
636 sa
= (struct sockaddr
*) &sa4
;
637 sa_len
= sizeof(sa4
);
638 hent
= gethostbyaddr(&sa4
.sin_addr
, sizeof(sa4
.sin_addr
),
643 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
644 sa
= (struct sockaddr
*) &sa6
;
645 sa_len
= sizeof(sa6
);
646 hent
= gethostbyaddr(&sa6
.sin6_addr
, sizeof(sa6
.sin6_addr
),
651 bug_on(sizeof(n
->rev_dns_src
) != sizeof(n
->rev_dns_dst
));
652 getnameinfo(sa
, sa_len
, SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
653 sizeof(n
->rev_dns_src
), NULL
, 0, NI_NUMERICHOST
);
656 memset(n
->rev_dns_dst
, 0, sizeof(n
->rev_dns_dst
));
657 memcpy(SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
658 hent
->h_name
, min(sizeof(n
->rev_dns_src
),
659 strlen(hent
->h_name
)));
663 static void flow_entry_get_extended(struct flow_entry
*n
)
665 if (n
->flow_id
== 0 || flow_entry_get_extended_is_dns(n
))
668 flow_entry_get_extended_revdns(n
, flow_entry_src
);
669 flow_entry_get_extended_geo(n
, flow_entry_src
);
671 flow_entry_get_extended_revdns(n
, flow_entry_dst
);
672 flow_entry_get_extended_geo(n
, flow_entry_dst
);
675 static uint16_t presenter_get_port(uint16_t src
, uint16_t dst
, int tcp
)
677 if (src
< dst
&& src
< 1024) {
679 } else if (dst
< src
&& dst
< 1024) {
682 const char *tmp1
, *tmp2
;
684 tmp1
= lookup_port_tcp(src
);
685 tmp2
= lookup_port_tcp(dst
);
687 tmp1
= lookup_port_udp(src
);
688 tmp2
= lookup_port_udp(dst
);
692 } else if (!tmp1
&& tmp2
) {
703 static void presenter_screen_init(WINDOW
**screen
)
705 (*screen
) = initscr();
708 keypad(stdscr
, TRUE
);
709 nodelay(*screen
, TRUE
);
714 static void presenter_screen_do_line(WINDOW
*screen
, struct flow_entry
*n
,
720 slprintf(tmp
, sizeof(tmp
), "%u/%s", n
->procnum
, basename(n
->cmdline
));
722 /* PID, application name */
723 mvwprintw(screen
, *line
, 2, "[");
724 attron(COLOR_PAIR(3));
725 printw("%s", n
->procnum
> 0 ? tmp
: "N/A");
726 attroff(COLOR_PAIR(3));
729 /* L3 protocol, L4 protocol, TCP state */
730 printw(":%s:%s", l3proto2str
[n
->l3_proto
], proto2str
[n
->l4_proto
]);
732 attron(COLOR_PAIR(3));
733 printw("%s", state2str
[n
->tcp_state
]);
734 attroff(COLOR_PAIR(3));
737 /* Guess application port */
739 if (n
->tcp_state
!= TCP_CONNTRACK_NONE
) {
740 port
= presenter_get_port(n
->port_src
, n
->port_dst
, 1);
741 printw("%s -> ", lookup_port_tcp(port
));
743 port
= presenter_get_port(n
->port_src
, n
->port_dst
, 0);
744 printw("%s -> ", lookup_port_udp(port
));
748 /* Show source information: reverse DNS, port, country, city */
750 attron(COLOR_PAIR(1));
751 mvwprintw(screen
, ++(*line
), 8, "src: %s", n
->rev_dns_src
);
752 attroff(COLOR_PAIR(1));
754 printw(":%u (", n
->port_src
);
756 attron(COLOR_PAIR(4));
757 printw("%s", n
->country_src
);
758 attroff(COLOR_PAIR(4));
760 printw(", %s) => ", n
->city_src
);
763 /* Show dest information: reverse DNS, port, country, city */
764 attron(COLOR_PAIR(2));
765 mvwprintw(screen
, ++(*line
), 8, "dst: %s", n
->rev_dns_dst
);
766 attroff(COLOR_PAIR(2));
768 printw(":%u (", n
->port_dst
);
770 attron(COLOR_PAIR(4));
771 printw("%s", n
->country_dst
);
772 attroff(COLOR_PAIR(4));
774 printw(", %s)", n
->city_dst
);
777 static void presenter_screen_update(WINDOW
*screen
, struct flow_list
*fl
,
781 unsigned int line
= 3;
782 struct flow_entry
*n
;
786 maxy
= getmaxy(screen
);
789 init_pair(1, COLOR_RED
, COLOR_BLACK
);
790 init_pair(2, COLOR_BLUE
, COLOR_BLACK
);
791 init_pair(3, COLOR_YELLOW
, COLOR_BLACK
);
792 init_pair(4, COLOR_GREEN
, COLOR_BLACK
);
797 mvwprintw(screen
, 1, 2, "Kernel netfilter TCP/UDP "
798 "flow statistics, [+%d]", skip_lines
);
802 if (rcu_dereference(fl
->head
) == NULL
)
803 mvwprintw(screen
, line
, 2, "(No active sessions! "
804 "Is netfilter running?)");
807 /* Yes, that's lame :-P */
808 for (i
= 0; i
< sizeof(states
); i
++) {
809 n
= rcu_dereference(fl
->head
);
810 while (n
&& maxy
> 0) {
811 if (n
->tcp_state
!= states
[i
] ||
812 (i
!= TCP_CONNTRACK_NONE
&&
813 n
->tcp_state
== TCP_CONNTRACK_NONE
) ||
815 presenter_get_port(n
->port_src
,
816 n
->port_dst
, 0) == 53) {
817 n
= rcu_dereference(n
->next
);
820 if (skip_lines
> 0) {
821 n
= rcu_dereference(n
->next
);
826 presenter_screen_do_line(screen
, n
, &line
);
829 maxy
-= (2 + 1 * show_src
);
830 n
= rcu_dereference(n
->next
);
840 static inline void presenter_screen_end(void)
845 static void presenter(void)
848 WINDOW
*screen
= NULL
;
850 dissector_init_ethernet(0);
851 presenter_screen_init(&screen
);
853 rcu_register_thread();
870 if (skip_lines
> SCROLL_MAX
)
871 skip_lines
= SCROLL_MAX
;
878 presenter_screen_update(screen
, &flow_list
, skip_lines
);
881 rcu_unregister_thread();
883 presenter_screen_end();
884 dissector_cleanup_ethernet();
887 static int collector_cb(enum nf_conntrack_msg_type type
,
888 struct nf_conntrack
*ct
, void *data
)
894 spinlock_lock(&flow_list
.lock
);
898 flow_list_new_entry(&flow_list
, ct
);
901 flow_list_update_entry(&flow_list
, ct
);
904 flow_list_destroy_entry(&flow_list
, ct
);
910 spinlock_unlock(&flow_list
.lock
);
912 return NFCT_CB_CONTINUE
;
915 static inline GeoIP
*collector_geoip_open(const char *path
, int type
)
918 return GeoIP_open(path
, GEOIP_MMAP_CACHE
);
920 return GeoIP_open_type(type
, GEOIP_MMAP_CACHE
);
923 static void collector_load_geoip(void)
925 geo_country
.gi4
= collector_geoip_open(geo_country
.path4
,
926 GEOIP_COUNTRY_EDITION
);
927 if (geo_country
.gi4
== NULL
)
928 panic("Cannot open GeoIP4 country database!\n");
930 geo_country
.gi6
= collector_geoip_open(geo_country
.path6
,
931 GEOIP_COUNTRY_EDITION_V6
);
932 if (geo_country
.gi6
== NULL
)
933 panic("Cannot open GeoIP6 country database!\n");
935 geo_city
.gi4
= collector_geoip_open(geo_city
.path4
,
936 GEOIP_CITY_EDITION_REV1
);
937 if (geo_city
.gi4
== NULL
)
938 panic("Cannot open GeoIP4 city database!\n");
940 geo_city
.gi6
= collector_geoip_open(geo_city
.path6
,
941 GEOIP_CITY_EDITION_REV1_V6
);
942 if (geo_city
.gi6
== NULL
)
943 panic("Cannot open GeoIP6 city database!\n");
945 GeoIP_set_charset(geo_country
.gi4
, GEOIP_CHARSET_UTF8
);
946 GeoIP_set_charset(geo_country
.gi6
, GEOIP_CHARSET_UTF8
);
948 GeoIP_set_charset(geo_city
.gi4
, GEOIP_CHARSET_UTF8
);
949 GeoIP_set_charset(geo_city
.gi6
, GEOIP_CHARSET_UTF8
);
952 static void collector_destroy_geoip(void)
954 GeoIP_delete(geo_country
.gi4
);
955 GeoIP_delete(geo_country
.gi6
);
957 GeoIP_delete(geo_city
.gi4
);
958 GeoIP_delete(geo_city
.gi6
);
961 static void *collector(void *null
)
964 struct nfct_handle
*handle
;
965 struct nfct_filter
*filter
;
967 handle
= nfct_open(CONNTRACK
, NFCT_ALL_CT_GROUPS
);
969 panic("Cannot create a nfct handle!\n");
971 filter
= nfct_filter_create();
973 panic("Cannot create a nfct filter!\n");
975 if (what
& INCLUDE_UDP
)
976 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
,
978 if (what
& INCLUDE_TCP
)
979 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
,
982 nfct_filter_set_logic(filter
, NFCT_FILTER_SRC_IPV4
,
983 NFCT_FILTER_LOGIC_NEGATIVE
);
984 nfct_filter_add_attr(filter
, NFCT_FILTER_SRC_IPV4
,
987 nfct_filter_set_logic(filter
, NFCT_FILTER_SRC_IPV6
,
988 NFCT_FILTER_LOGIC_NEGATIVE
);
989 nfct_filter_add_attr(filter
, NFCT_FILTER_SRC_IPV6
,
992 ret
= nfct_filter_attach(nfct_fd(handle
), filter
);
994 panic("Cannot attach filter to handle!\n");
996 nfct_callback_register(handle
, NFCT_T_ALL
, collector_cb
, NULL
);
998 collector_load_geoip();
1000 flow_list_init(&flow_list
);
1002 rcu_register_thread();
1005 rcu_unregister_thread();
1007 flow_list_destroy(&flow_list
);
1009 collector_destroy_geoip();
1011 nfct_filter_destroy(filter
);
1017 int main(int argc
, char **argv
)
1020 int ret
, c
, opt_index
, what_cmd
= 0;
1022 memset(&geo_country
, 0, sizeof(geo_country
));
1023 memset(&geo_city
, 0, sizeof(geo_city
));
1025 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
1026 &opt_index
)) != EOF
) {
1029 what_cmd
|= INCLUDE_TCP
;
1032 what_cmd
|= INCLUDE_UDP
;
1038 geo_city
.path4
= xstrdup(optarg
);
1041 geo_country
.path4
= xstrdup(optarg
);
1044 geo_city
.path6
= xstrdup(optarg
);
1047 geo_country
.path6
= xstrdup(optarg
);
1061 panic("Option -%c requires an argument!\n",
1064 if (isprint(optopt
))
1065 whine("Unknown option character "
1066 "`0x%X\'!\n", optopt
);
1079 register_signal(SIGINT
, signal_handler
);
1080 register_signal(SIGHUP
, signal_handler
);
1082 ret
= pthread_create(&tid
, NULL
, collector
, NULL
);
1084 panic("Cannot create phthread!\n");
1088 free(geo_country
.path4
);
1089 free(geo_country
.path6
);
1091 free(geo_city
.path4
);
1092 free(geo_city
.path6
);