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>
30 #include <libnetfilter_conntrack/libnetfilter_conntrack_dccp.h>
31 #include <libnetfilter_conntrack/libnetfilter_conntrack_sctp.h>
33 #include <GeoIPCity.h>
34 #include <netinet/in.h>
47 #include "dissector_eth.h"
56 uint32_t flow_id
, use
, status
;
57 uint8_t l3_proto
, l4_proto
;
58 uint32_t ip4_src_addr
, ip4_dst_addr
;
59 uint32_t ip6_src_addr
[4], ip6_dst_addr
[4];
60 uint16_t port_src
, port_dst
;
61 uint8_t tcp_state
, tcp_flags
;
62 uint64_t counter_pkts
, counter_bytes
;
63 uint64_t timestamp_start
, timestamp_stop
;
64 char country_src
[128], country_dst
[128];
65 char city_src
[128], city_dst
[128];
66 char rev_dns_src
[256], rev_dns_dst
[256];
67 int first
, procnum
, inode
;
69 struct flow_entry
*next
;
73 struct flow_entry
*head
;
77 #ifndef ATTR_TIMESTAMP_START
78 # define ATTR_TIMESTAMP_START 63
80 #ifndef ATTR_TIMESTAMP_STOP
81 # define ATTR_TIMESTAMP_STOP 64
84 #define SCROLL_MAX 1000
86 #define INCLUDE_UDP (1 << 0)
87 #define INCLUDE_TCP (1 << 1)
88 #define INCLUDE_DCCP (1 << 2)
89 #define INCLUDE_ICMP (1 << 3)
90 #define INCLUDE_SCTP (1 << 4)
92 volatile sig_atomic_t sigint
= 0;
94 static int what
= INCLUDE_TCP
, show_src
= 0;
96 struct geo_ip_db geo_country
, geo_city
;
98 static struct flow_list flow_list
;
100 static const char *short_options
= "vhTULKsOPDIS";
101 static const struct option long_options
[] = {
102 {"tcp", no_argument
, NULL
, 'T'},
103 {"udp", no_argument
, NULL
, 'U'},
104 {"dccp", no_argument
, NULL
, 'D'},
105 {"icmp", no_argument
, NULL
, 'I'},
106 {"sctp", no_argument
, NULL
, 'S'},
107 {"show-src", no_argument
, NULL
, 's'},
108 {"city-db4", required_argument
, NULL
, 'L'},
109 {"country-db4", required_argument
, NULL
, 'K'},
110 {"city-db6", required_argument
, NULL
, 'O'},
111 {"country-db6", required_argument
, NULL
, 'P'},
112 {"version", no_argument
, NULL
, 'v'},
113 {"help", no_argument
, NULL
, 'h'},
117 static const char *const l3proto2str
[AF_MAX
] = {
122 static const char *const l4proto2str
[IPPROTO_MAX
] = {
123 [IPPROTO_TCP
] = "tcp",
124 [IPPROTO_UDP
] = "udp",
125 [IPPROTO_UDPLITE
] = "udplite",
126 [IPPROTO_ICMP
] = "icmp",
127 [IPPROTO_ICMPV6
] = "icmpv6",
128 [IPPROTO_SCTP
] = "sctp",
129 [IPPROTO_GRE
] = "gre",
130 [IPPROTO_DCCP
] = "dccp",
131 [IPPROTO_IGMP
] = "igmp",
132 [IPPROTO_IPIP
] = "ipip",
133 [IPPROTO_EGP
] = "egp",
134 [IPPROTO_PUP
] = "pup",
135 [IPPROTO_IDP
] = "idp",
136 [IPPROTO_RSVP
] = "rsvp",
137 [IPPROTO_IPV6
] = "ip6tun",
138 [IPPROTO_ESP
] = "esp",
140 [IPPROTO_PIM
] = "pim",
141 [IPPROTO_COMP
] = "comp",
144 static const char *const tcp_state2str
[TCP_CONNTRACK_MAX
] = {
145 [TCP_CONNTRACK_NONE
] = "NOSTATE",
146 [TCP_CONNTRACK_SYN_SENT
] = "SYN_SENT",
147 [TCP_CONNTRACK_SYN_RECV
] = "SYN_RECV",
148 [TCP_CONNTRACK_ESTABLISHED
] = "ESTABLISHED",
149 [TCP_CONNTRACK_FIN_WAIT
] = "FIN_WAIT",
150 [TCP_CONNTRACK_CLOSE_WAIT
] = "CLOSE_WAIT",
151 [TCP_CONNTRACK_LAST_ACK
] = "LAST_ACK",
152 [TCP_CONNTRACK_TIME_WAIT
] = "TIME_WAIT",
153 [TCP_CONNTRACK_CLOSE
] = "CLOSE",
154 [TCP_CONNTRACK_SYN_SENT2
] = "SYN_SENT2",
157 static const uint8_t tcp_states
[] = {
158 TCP_CONNTRACK_SYN_SENT
,
159 TCP_CONNTRACK_SYN_RECV
,
160 TCP_CONNTRACK_ESTABLISHED
,
161 TCP_CONNTRACK_FIN_WAIT
,
162 TCP_CONNTRACK_CLOSE_WAIT
,
163 TCP_CONNTRACK_LAST_ACK
,
164 TCP_CONNTRACK_TIME_WAIT
,
166 TCP_CONNTRACK_SYN_SENT2
,
170 static const char *const dccp_state2str
[DCCP_CONNTRACK_MAX
] = {
171 [DCCP_CONNTRACK_NONE
] = "NOSTATE",
172 [DCCP_CONNTRACK_REQUEST
] = "REQUEST",
173 [DCCP_CONNTRACK_RESPOND
] = "RESPOND",
174 [DCCP_CONNTRACK_PARTOPEN
] = "PARTOPEN",
175 [DCCP_CONNTRACK_OPEN
] = "OPEN",
176 [DCCP_CONNTRACK_CLOSEREQ
] = "CLOSEREQ",
177 [DCCP_CONNTRACK_CLOSING
] = "CLOSING",
178 [DCCP_CONNTRACK_TIMEWAIT
] = "TIMEWAIT",
179 [DCCP_CONNTRACK_IGNORE
] = "IGNORE",
180 [DCCP_CONNTRACK_INVALID
] = "INVALID",
183 static const uint8_t dccp_states
[] = {
185 DCCP_CONNTRACK_REQUEST
,
186 DCCP_CONNTRACK_RESPOND
,
187 DCCP_CONNTRACK_PARTOPEN
,
189 DCCP_CONNTRACK_CLOSEREQ
,
190 DCCP_CONNTRACK_CLOSING
,
191 DCCP_CONNTRACK_TIMEWAIT
,
192 DCCP_CONNTRACK_IGNORE
,
193 DCCP_CONNTRACK_INVALID
,
196 static const char *const sctp_state2str
[SCTP_CONNTRACK_MAX
] = {
197 [SCTP_CONNTRACK_NONE
] = "NOSTATE",
198 [SCTP_CONNTRACK_CLOSED
] = "CLOSED",
199 [SCTP_CONNTRACK_COOKIE_WAIT
] = "COOKIE_WAIT",
200 [SCTP_CONNTRACK_COOKIE_ECHOED
] = "COOKIE_ECHOED",
201 [SCTP_CONNTRACK_ESTABLISHED
] = "ESTABLISHED",
202 [SCTP_CONNTRACK_SHUTDOWN_SENT
] = "SHUTDOWN_SENT",
203 [SCTP_CONNTRACK_SHUTDOWN_RECD
] = "SHUTDOWN_RECD",
204 [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
] = "SHUTDOWN_ACK_SENT",
207 static const uint8_t sctp_states
[] = {
209 SCTP_CONNTRACK_CLOSED
,
210 SCTP_CONNTRACK_COOKIE_WAIT
,
211 SCTP_CONNTRACK_COOKIE_ECHOED
,
212 SCTP_CONNTRACK_ESTABLISHED
,
213 SCTP_CONNTRACK_SHUTDOWN_SENT
,
214 SCTP_CONNTRACK_SHUTDOWN_RECD
,
215 SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
,
218 static const struct nfct_filter_ipv4 filter_ipv4
= {
219 .addr
= __constant_htonl(INADDR_LOOPBACK
),
223 static const struct nfct_filter_ipv6 filter_ipv6
= {
224 .addr
= { 0x0, 0x0, 0x0, 0x1 },
225 .mask
= { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
228 static void signal_handler(int number
)
240 static void flow_entry_from_ct(struct flow_entry
*n
, struct nf_conntrack
*ct
);
241 static void flow_entry_get_extended(struct flow_entry
*n
);
243 static void help(void)
245 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
247 puts("http://www.netsniff-ng.org\n\n"
248 "Usage: flowtop [options]\n"
250 " -T|--tcp Show only TCP flows (default)\n"
251 " -U|--udp Show only UDP flows\n"
252 " -D|--dccp Show only DCCP flows\n"
253 " -I|--icmp Show only ICMP/ICMPv6 flows\n"
254 " -S|--sctp Show only SCTP flows\n"
255 " -s|--show-src Also show source, not only dest\n"
256 " --city-db4 <path> Specifiy path for geoip4 city database\n"
257 " --country-db4 <path> Specifiy path for geoip4 country database\n"
258 " --city-db6 <path> Specifiy path for geoip6 city database\n"
259 " --country-db6 <path> Specifiy path for geoip6 country database\n"
260 " -v|--version Print version\n"
261 " -h|--help Print this help\n\n"
264 " flowtop -UTDISs\n\n"
266 " If netfilter is not running, you can activate it with i.e.:\n"
267 " iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT\n"
268 " iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT\n\n"
269 "Please report bugs to <bugs@netsniff-ng.org>\n"
270 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
271 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n"
272 "License: GNU GPL version 2.0\n"
273 "This is free software: you are free to change and redistribute it.\n"
274 "There is NO WARRANTY, to the extent permitted by law.\n");
278 static void version(void)
280 printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n",
282 puts("http://www.netsniff-ng.org\n\n"
283 "Please report bugs to <bugs@netsniff-ng.org>\n"
284 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
285 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel@netsniff-ng.org>\n"
286 "License: GNU GPL version 2.0\n"
287 "This is free software: you are free to change and redistribute it.\n"
288 "There is NO WARRANTY, to the extent permitted by law.\n");
292 static inline struct flow_entry
*flow_entry_xalloc(void)
294 struct flow_entry
*n
;
296 n
= xzmalloc(sizeof(*n
));
302 static inline void flow_entry_xfree(struct flow_entry
*n
)
307 static inline void flow_list_init(struct flow_list
*fl
)
310 spinlock_init(&fl
->lock
);
313 static void flow_list_new_entry(struct flow_list
*fl
, struct nf_conntrack
*ct
)
315 struct flow_entry
*n
= flow_entry_xalloc();
317 rcu_assign_pointer(n
->next
, fl
->head
);
318 rcu_assign_pointer(fl
->head
, n
);
320 flow_entry_from_ct(n
, ct
);
321 flow_entry_get_extended(n
);
324 static struct flow_entry
*flow_list_find_id(struct flow_list
*fl
,
327 struct flow_entry
*n
= rcu_dereference(fl
->head
);
330 if (n
->flow_id
== id
)
333 n
= rcu_dereference(n
->next
);
339 static struct flow_entry
*flow_list_find_prev_id(struct flow_list
*fl
,
342 struct flow_entry
*n
= rcu_dereference(fl
->head
), *tmp
;
344 if (n
->flow_id
== id
)
347 while ((tmp
= rcu_dereference(n
->next
)) != NULL
) {
348 if (tmp
->flow_id
== id
)
357 static void flow_list_update_entry(struct flow_list
*fl
,
358 struct nf_conntrack
*ct
)
361 struct flow_entry
*n
;
363 n
= flow_list_find_id(fl
, nfct_get_attr_u32(ct
, ATTR_ID
));
365 n
= flow_entry_xalloc();
367 rcu_assign_pointer(n
->next
, fl
->head
);
368 rcu_assign_pointer(fl
->head
, n
);
373 flow_entry_from_ct(n
, ct
);
375 flow_entry_get_extended(n
);
378 static void flow_list_destroy_entry(struct flow_list
*fl
,
379 struct nf_conntrack
*ct
)
381 struct flow_entry
*n1
, *n2
;
382 uint32_t id
= nfct_get_attr_u32(ct
, ATTR_ID
);
384 n1
= flow_list_find_id(fl
, id
);
386 n2
= flow_list_find_prev_id(fl
, id
);
388 rcu_assign_pointer(n2
->next
, n1
->next
);
389 rcu_assign_pointer(n1
->next
, NULL
);
391 flow_entry_xfree(n1
);
393 flow_entry_xfree(fl
->head
);
395 rcu_assign_pointer(fl
->head
, NULL
);
400 static void flow_list_destroy(struct flow_list
*fl
)
402 struct flow_entry
*n
;
404 while (fl
->head
!= NULL
) {
405 n
= rcu_dereference(fl
->head
->next
);
406 rcu_assign_pointer(fl
->head
->next
, NULL
);
408 flow_entry_xfree(fl
->head
);
409 rcu_assign_pointer(fl
->head
, n
);
413 spinlock_destroy(&fl
->lock
);
416 static int walk_process(char *process
, struct flow_entry
*n
)
423 if (snprintf(path
, sizeof(path
), "/proc/%s/fd", process
) == -1)
424 panic("giant process name! %s\n", process
);
430 while ((ent
= readdir(dir
))) {
433 if (snprintf(path
, sizeof(path
), "/proc/%s/fd/%s",
434 process
, ent
->d_name
) < 0)
437 if (stat(path
, &statbuf
) < 0)
440 if (S_ISSOCK(statbuf
.st_mode
) && n
->inode
== statbuf
.st_ino
) {
441 memset(n
->cmdline
, 0, sizeof(n
->cmdline
));
443 snprintf(path
, sizeof(path
), "/proc/%s/exe", process
);
445 ret
= readlink(path
, n
->cmdline
,
446 sizeof(n
->cmdline
) - 1);
448 panic("readlink error: %s\n", strerror(errno
));
450 n
->procnum
= atoi(process
);
459 static void walk_processes(struct flow_entry
*n
)
465 /* n->inode must be set */
467 memset(n
->cmdline
, 0, sizeof(n
->cmdline
));
471 dir
= opendir("/proc");
473 panic("Cannot open /proc!\n");
475 while ((ent
= readdir(dir
))) {
476 if (strspn(ent
->d_name
, "0123456789") == strlen(ent
->d_name
)) {
477 ret
= walk_process(ent
->d_name
, n
);
486 static int get_port_inode(uint16_t port
, int proto
, int is_ip6
)
489 char path
[128], buff
[1024];
492 memset(path
, 0, sizeof(path
));
493 snprintf(path
, sizeof(path
), "/proc/net/%s%s",
494 l4proto2str
[proto
], is_ip6
? "6" : "");
496 proc
= fopen(path
, "r");
500 memset(buff
, 0, sizeof(buff
));
502 while (fgets(buff
, sizeof(buff
), proc
) != NULL
) {
504 unsigned int lport
= 0;
506 buff
[sizeof(buff
) - 1] = 0;
507 if (sscanf(buff
, "%*u: %*X:%X %*X:%*X %*X %*X:%*X %*X:%*X "
508 "%*X %*u %*u %u", &lport
, &inode
) == 2) {
509 if ((uint16_t) lport
== port
) {
515 memset(buff
, 0, sizeof(buff
));
522 #define CP_NFCT(elem, attr, x) \
523 do { n->elem = nfct_get_attr_u##x(ct,(attr)); } while (0)
524 #define CP_NFCT_BUFF(elem, attr) do { \
525 const uint8_t *buff = nfct_get_attr(ct,(attr)); \
527 memcpy(n->elem, buff, sizeof(n->elem)); \
530 static void flow_entry_from_ct(struct flow_entry
*n
, struct nf_conntrack
*ct
)
532 CP_NFCT(l3_proto
, ATTR_ORIG_L3PROTO
, 8);
533 CP_NFCT(l4_proto
, ATTR_ORIG_L4PROTO
, 8);
534 CP_NFCT(ip4_src_addr
, ATTR_ORIG_IPV4_SRC
, 32);
535 CP_NFCT(ip4_dst_addr
, ATTR_ORIG_IPV4_DST
, 32);
536 CP_NFCT(port_src
, ATTR_ORIG_PORT_SRC
, 16);
537 CP_NFCT(port_dst
, ATTR_ORIG_PORT_DST
, 16);
538 CP_NFCT(status
, ATTR_STATUS
, 32);
539 CP_NFCT(tcp_state
, ATTR_TCP_STATE
, 8);
540 CP_NFCT(tcp_flags
, ATTR_TCP_FLAGS_ORIG
, 8);
541 CP_NFCT(counter_pkts
, ATTR_ORIG_COUNTER_PACKETS
, 64);
542 CP_NFCT(counter_bytes
, ATTR_ORIG_COUNTER_BYTES
, 64);
543 CP_NFCT(timestamp_start
, ATTR_TIMESTAMP_START
, 64);
544 CP_NFCT(timestamp_stop
, ATTR_TIMESTAMP_STOP
, 64);
545 CP_NFCT(flow_id
, ATTR_ID
, 32);
546 CP_NFCT(use
, ATTR_USE
, 32);
548 CP_NFCT_BUFF(ip6_src_addr
, ATTR_ORIG_IPV6_SRC
);
549 CP_NFCT_BUFF(ip6_dst_addr
, ATTR_ORIG_IPV6_DST
);
551 n
->port_src
= ntohs(n
->port_src
);
552 n
->port_dst
= ntohs(n
->port_dst
);
554 n
->ip4_src_addr
= ntohl(n
->ip4_src_addr
);
555 n
->ip4_dst_addr
= ntohl(n
->ip4_dst_addr
);
558 n
->inode
= get_port_inode(n
->port_src
, n
->l4_proto
,
559 n
->l3_proto
== AF_INET6
);
567 enum flow_entry_direction
{
572 static inline int flow_entry_get_extended_is_dns(struct flow_entry
*n
)
574 /* We don't want to analyze / display DNS itself, since we
575 * use it to resolve reverse dns.
577 return n
->port_src
== 53 || n
->port_dst
== 53;
580 #define SELFLD(dir,src_member,dst_member) \
581 (((dir) == flow_entry_src) ? n->src_member : n->dst_member)
583 static struct sockaddr_in
*
584 flow_entry_get_sain4_obj(struct flow_entry
*n
, enum flow_entry_direction dir
,
585 struct sockaddr_in
*sa
)
587 memset(sa
, 0, sizeof(*sa
));
588 sa
->sin_family
= PF_INET
;
589 sa
->sin_addr
.s_addr
= htonl(SELFLD(dir
, ip4_src_addr
, ip4_dst_addr
));
594 static struct sockaddr_in6
*
595 flow_entry_get_sain6_obj(struct flow_entry
*n
, enum flow_entry_direction dir
,
596 struct sockaddr_in6
*sa
)
598 memset(sa
, 0, sizeof(*sa
));
599 sa
->sin6_family
= PF_INET6
;
601 memcpy(&sa
->sin6_addr
, SELFLD(dir
, ip6_src_addr
, ip6_dst_addr
),
602 sizeof(SELFLD(dir
, ip6_src_addr
, ip6_dst_addr
)));
608 flow_entry_geo_city_lookup_generic(struct flow_entry
*n
,
609 enum flow_entry_direction dir
)
611 GeoIPRecord
*gir
= NULL
;
612 struct sockaddr_in sa4
;
613 struct sockaddr_in6 sa6
;
614 inline const char *make_na(const char *p
) { return p
? : "N/A"; }
615 const char *city
= NULL
;
617 switch (n
->l3_proto
) {
622 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
623 gir
= GeoIP_record_by_ipnum(geo_city
.gi4
,
624 ntohl(sa4
.sin_addr
.s_addr
));
628 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
629 gir
= GeoIP_record_by_ipnum_v6(geo_city
.gi6
, sa6
.sin6_addr
);
633 city
= make_na(gir
!= NULL
? gir
->city
: city
);
635 bug_on(sizeof(n
->city_src
) != sizeof(n
->city_dst
));
636 memcpy(SELFLD(dir
, city_src
, city_dst
), city
,
637 min(sizeof(n
->city_src
), strlen(city
)));
641 flow_entry_geo_country_lookup_generic(struct flow_entry
*n
,
642 enum flow_entry_direction dir
)
644 struct sockaddr_in sa4
;
645 struct sockaddr_in6 sa6
;
646 inline const char *make_na(const char *p
) { return p
? : "N/A"; }
647 const char *country
= NULL
;
649 switch (n
->l3_proto
) {
654 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
655 country
= GeoIP_country_name_by_ipnum(geo_country
.gi4
,
656 ntohl(sa4
.sin_addr
.s_addr
));
660 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
661 country
= GeoIP_country_name_by_ipnum_v6(geo_country
.gi6
,
666 country
= make_na(country
);
668 bug_on(sizeof(n
->country_src
) != sizeof(n
->country_dst
));
669 memcpy(SELFLD(dir
, country_src
, country_dst
), country
,
670 min(sizeof(n
->country_src
), strlen(country
)));
673 static void flow_entry_get_extended_geo(struct flow_entry
*n
,
674 enum flow_entry_direction dir
)
676 flow_entry_geo_city_lookup_generic(n
, dir
);
677 flow_entry_geo_country_lookup_generic(n
, dir
);
680 static void flow_entry_get_extended_revdns(struct flow_entry
*n
,
681 enum flow_entry_direction dir
)
684 struct sockaddr_in sa4
;
685 struct sockaddr_in6 sa6
;
687 struct hostent
*hent
;
689 switch (n
->l3_proto
) {
694 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
695 sa
= (struct sockaddr
*) &sa4
;
696 sa_len
= sizeof(sa4
);
697 hent
= gethostbyaddr(&sa4
.sin_addr
, sizeof(sa4
.sin_addr
),
702 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
703 sa
= (struct sockaddr
*) &sa6
;
704 sa_len
= sizeof(sa6
);
705 hent
= gethostbyaddr(&sa6
.sin6_addr
, sizeof(sa6
.sin6_addr
),
710 bug_on(sizeof(n
->rev_dns_src
) != sizeof(n
->rev_dns_dst
));
711 getnameinfo(sa
, sa_len
, SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
712 sizeof(n
->rev_dns_src
), NULL
, 0, NI_NUMERICHOST
);
715 memset(n
->rev_dns_dst
, 0, sizeof(n
->rev_dns_dst
));
716 memcpy(SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
717 hent
->h_name
, min(sizeof(n
->rev_dns_src
),
718 strlen(hent
->h_name
)));
722 static void flow_entry_get_extended(struct flow_entry
*n
)
724 if (n
->flow_id
== 0 || flow_entry_get_extended_is_dns(n
))
727 flow_entry_get_extended_revdns(n
, flow_entry_src
);
728 flow_entry_get_extended_geo(n
, flow_entry_src
);
730 flow_entry_get_extended_revdns(n
, flow_entry_dst
);
731 flow_entry_get_extended_geo(n
, flow_entry_dst
);
734 static uint16_t presenter_get_port(uint16_t src
, uint16_t dst
, int tcp
)
736 if (src
< dst
&& src
< 1024) {
738 } else if (dst
< src
&& dst
< 1024) {
741 const char *tmp1
, *tmp2
;
743 tmp1
= lookup_port_tcp(src
);
744 tmp2
= lookup_port_tcp(dst
);
746 tmp1
= lookup_port_udp(src
);
747 tmp2
= lookup_port_udp(dst
);
751 } else if (!tmp1
&& tmp2
) {
762 static void presenter_screen_init(WINDOW
**screen
)
764 (*screen
) = initscr();
767 keypad(stdscr
, TRUE
);
768 nodelay(*screen
, TRUE
);
773 static void presenter_screen_do_line(WINDOW
*screen
, struct flow_entry
*n
,
779 mvwprintw(screen
, *line
, 2, "");
781 /* PID, application name */
782 if (n
->procnum
> 0) {
783 slprintf(tmp
, sizeof(tmp
), "%u/%s", n
->procnum
,
784 basename(n
->cmdline
));
787 attron(COLOR_PAIR(3));
789 attroff(COLOR_PAIR(3));
793 /* L3 protocol, L4 protocol, TCP state */
794 printw("%s:%s", l3proto2str
[n
->l3_proto
], l4proto2str
[n
->l4_proto
]);
796 attron(COLOR_PAIR(3));
797 printw("%s", tcp_state2str
[n
->tcp_state
]);
798 attroff(COLOR_PAIR(3));
801 /* Guess application port */
803 if (n
->tcp_state
!= TCP_CONNTRACK_NONE
) {
804 port
= presenter_get_port(n
->port_src
, n
->port_dst
, 1);
805 printw("%s ->", lookup_port_tcp(port
));
807 port
= presenter_get_port(n
->port_src
, n
->port_dst
, 0);
808 printw("%s ->", lookup_port_udp(port
));
813 /* Number packets, bytes */
814 printw("(%upkts/%ubytes) ->", n
->counter_pkts
, n
->counter_bytes
);
817 /* Show source information: reverse DNS, port, country, city */
819 attron(COLOR_PAIR(1));
820 mvwprintw(screen
, ++(*line
), 8, "src: %s", n
->rev_dns_src
);
821 attroff(COLOR_PAIR(1));
823 printw(":%u (", n
->port_src
);
825 attron(COLOR_PAIR(4));
826 printw("%s", n
->country_src
);
827 attroff(COLOR_PAIR(4));
829 printw(", %s) => ", n
->city_src
);
832 /* Show dest information: reverse DNS, port, country, city */
833 attron(COLOR_PAIR(2));
834 mvwprintw(screen
, ++(*line
), 8, "dst: %s", n
->rev_dns_dst
);
835 attroff(COLOR_PAIR(2));
837 printw(":%u (", n
->port_dst
);
839 attron(COLOR_PAIR(4));
840 printw("%s", n
->country_dst
);
841 attroff(COLOR_PAIR(4));
843 printw(", %s)", n
->city_dst
);
846 static void presenter_screen_update(WINDOW
*screen
, struct flow_list
*fl
,
850 unsigned int line
= 3;
851 struct flow_entry
*n
;
855 maxy
= getmaxy(screen
);
858 init_pair(1, COLOR_RED
, COLOR_BLACK
);
859 init_pair(2, COLOR_BLUE
, COLOR_BLACK
);
860 init_pair(3, COLOR_YELLOW
, COLOR_BLACK
);
861 init_pair(4, COLOR_GREEN
, COLOR_BLACK
);
866 mvwprintw(screen
, 1, 2, "Kernel netfilter TCP/UDP "
867 "flow statistics, [+%d]", skip_lines
);
871 if (rcu_dereference(fl
->head
) == NULL
)
872 mvwprintw(screen
, line
, 2, "(No active sessions! "
873 "Is netfilter running?)");
876 /* Yes, that's lame :-P */
877 for (i
= 0; i
< sizeof(tcp_states
); i
++) {
878 n
= rcu_dereference(fl
->head
);
879 while (n
&& maxy
> 0) {
880 if (n
->tcp_state
!= tcp_states
[i
] ||
881 (i
!= TCP_CONNTRACK_NONE
&&
882 n
->tcp_state
== TCP_CONNTRACK_NONE
) ||
884 presenter_get_port(n
->port_src
,
885 n
->port_dst
, 0) == 53) {
886 n
= rcu_dereference(n
->next
);
889 if (skip_lines
> 0) {
890 n
= rcu_dereference(n
->next
);
895 presenter_screen_do_line(screen
, n
, &line
);
898 maxy
-= (2 + 1 * show_src
);
899 n
= rcu_dereference(n
->next
);
909 static inline void presenter_screen_end(void)
914 static void presenter(void)
917 WINDOW
*screen
= NULL
;
919 dissector_init_ethernet(0);
920 presenter_screen_init(&screen
);
922 rcu_register_thread();
939 if (skip_lines
> SCROLL_MAX
)
940 skip_lines
= SCROLL_MAX
;
947 presenter_screen_update(screen
, &flow_list
, skip_lines
);
950 rcu_unregister_thread();
952 presenter_screen_end();
953 dissector_cleanup_ethernet();
956 static int collector_cb(enum nf_conntrack_msg_type type
,
957 struct nf_conntrack
*ct
, void *data
)
963 spinlock_lock(&flow_list
.lock
);
967 flow_list_new_entry(&flow_list
, ct
);
970 flow_list_update_entry(&flow_list
, ct
);
973 flow_list_destroy_entry(&flow_list
, ct
);
979 spinlock_unlock(&flow_list
.lock
);
981 return NFCT_CB_CONTINUE
;
984 static inline GeoIP
*collector_geoip_open(const char *path
, int type
)
987 return GeoIP_open(path
, GEOIP_MMAP_CACHE
);
989 return GeoIP_open_type(type
, GEOIP_MMAP_CACHE
);
992 static void collector_load_geoip(void)
994 geo_country
.gi4
= collector_geoip_open(geo_country
.path4
,
995 GEOIP_COUNTRY_EDITION
);
996 if (geo_country
.gi4
== NULL
)
997 panic("Cannot open GeoIP4 country database!\n");
999 geo_country
.gi6
= collector_geoip_open(geo_country
.path6
,
1000 GEOIP_COUNTRY_EDITION_V6
);
1001 if (geo_country
.gi6
== NULL
)
1002 panic("Cannot open GeoIP6 country database!\n");
1004 geo_city
.gi4
= collector_geoip_open(geo_city
.path4
,
1005 GEOIP_CITY_EDITION_REV1
);
1006 if (geo_city
.gi4
== NULL
)
1007 panic("Cannot open GeoIP4 city database!\n");
1009 geo_city
.gi6
= collector_geoip_open(geo_city
.path6
,
1010 GEOIP_CITY_EDITION_REV1_V6
);
1011 if (geo_city
.gi6
== NULL
)
1012 panic("Cannot open GeoIP6 city database!\n");
1014 GeoIP_set_charset(geo_country
.gi4
, GEOIP_CHARSET_UTF8
);
1015 GeoIP_set_charset(geo_country
.gi6
, GEOIP_CHARSET_UTF8
);
1017 GeoIP_set_charset(geo_city
.gi4
, GEOIP_CHARSET_UTF8
);
1018 GeoIP_set_charset(geo_city
.gi6
, GEOIP_CHARSET_UTF8
);
1021 static void collector_destroy_geoip(void)
1023 GeoIP_delete(geo_country
.gi4
);
1024 GeoIP_delete(geo_country
.gi6
);
1026 GeoIP_delete(geo_city
.gi4
);
1027 GeoIP_delete(geo_city
.gi6
);
1030 static void *collector(void *null
)
1033 struct nfct_handle
*handle
;
1034 struct nfct_filter
*filter
;
1036 handle
= nfct_open(CONNTRACK
, NFCT_ALL_CT_GROUPS
);
1038 panic("Cannot create a nfct handle!\n");
1040 filter
= nfct_filter_create();
1042 panic("Cannot create a nfct filter!\n");
1044 if (what
& INCLUDE_UDP
)
1045 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_UDP
);
1046 if (what
& INCLUDE_TCP
)
1047 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_TCP
);
1048 if (what
& INCLUDE_DCCP
)
1049 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_DCCP
);
1050 if (what
& INCLUDE_SCTP
)
1051 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_SCTP
);
1052 if (what
& INCLUDE_ICMP
) {
1053 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_ICMP
);
1054 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_ICMPV6
);
1057 nfct_filter_set_logic(filter
, NFCT_FILTER_SRC_IPV4
, NFCT_FILTER_LOGIC_NEGATIVE
);
1058 nfct_filter_add_attr(filter
, NFCT_FILTER_SRC_IPV4
, &filter_ipv4
);
1060 nfct_filter_set_logic(filter
, NFCT_FILTER_SRC_IPV6
, NFCT_FILTER_LOGIC_NEGATIVE
);
1061 nfct_filter_add_attr(filter
, NFCT_FILTER_SRC_IPV6
, &filter_ipv6
);
1063 ret
= nfct_filter_attach(nfct_fd(handle
), filter
);
1065 panic("Cannot attach filter to handle!\n");
1067 nfct_callback_register(handle
, NFCT_T_ALL
, collector_cb
, NULL
);
1069 collector_load_geoip();
1071 flow_list_init(&flow_list
);
1073 rcu_register_thread();
1076 rcu_unregister_thread();
1078 flow_list_destroy(&flow_list
);
1080 collector_destroy_geoip();
1082 nfct_filter_destroy(filter
);
1088 int main(int argc
, char **argv
)
1091 int ret
, c
, opt_index
, what_cmd
= 0;
1093 memset(&geo_country
, 0, sizeof(geo_country
));
1094 memset(&geo_city
, 0, sizeof(geo_city
));
1096 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
1097 &opt_index
)) != EOF
) {
1100 what_cmd
|= INCLUDE_TCP
;
1103 what_cmd
|= INCLUDE_UDP
;
1106 what_cmd
|= INCLUDE_DCCP
;
1109 what_cmd
|= INCLUDE_ICMP
;
1112 what_cmd
|= INCLUDE_SCTP
;
1118 geo_city
.path4
= xstrdup(optarg
);
1121 geo_country
.path4
= xstrdup(optarg
);
1124 geo_city
.path6
= xstrdup(optarg
);
1127 geo_country
.path6
= xstrdup(optarg
);
1141 panic("Option -%c requires an argument!\n",
1144 if (isprint(optopt
))
1145 whine("Unknown option character "
1146 "`0x%X\'!\n", optopt
);
1159 register_signal(SIGINT
, signal_handler
);
1160 register_signal(SIGHUP
, signal_handler
);
1162 ret
= pthread_create(&tid
, NULL
, collector
, NULL
);
1164 panic("Cannot create phthread!\n");
1168 free(geo_country
.path4
);
1169 free(geo_country
.path6
);
1171 free(geo_city
.path4
);
1172 free(geo_city
.path6
);