2 * netsniff-ng - the packet sniffing beast
3 * Copyright 2011 - 2013 Daniel Borkmann.
4 * Copyright 2011 Emmanuel Roullit.
5 * Subject to the GPL, version 2.
18 #include <netinet/in.h>
23 #include <sys/fsuid.h>
29 #include <arpa/inet.h>
33 #include "conntrack.h"
47 #define NSEC_PER_SEC 1000000000L
51 #define USEC_PER_SEC 1000000L
55 uint32_t flow_id
, use
, status
;
56 uint8_t l3_proto
, l4_proto
;
57 uint32_t ip4_src_addr
, ip4_dst_addr
;
58 uint32_t ip6_src_addr
[4], ip6_dst_addr
[4];
59 uint16_t port_src
, port_dst
;
60 uint8_t tcp_state
, tcp_flags
, sctp_state
, dccp_state
;
61 uint64_t pkts_src
, bytes_src
;
62 uint64_t pkts_dst
, bytes_dst
;
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];
68 struct flow_entry
*next
;
72 struct nf_conntrack
*ct
;
73 struct timeval last_update
;
74 double rate_bytes_src
;
75 double rate_bytes_dst
;
81 struct flow_entry
*head
;
90 #ifndef ATTR_TIMESTAMP_START
91 # define ATTR_TIMESTAMP_START 63
93 #ifndef ATTR_TIMESTAMP_STOP
94 # define ATTR_TIMESTAMP_STOP 64
97 #define SCROLL_MAX 1000
99 #define INCLUDE_IPV4 (1 << 0)
100 #define INCLUDE_IPV6 (1 << 1)
101 #define INCLUDE_UDP (1 << 2)
102 #define INCLUDE_TCP (1 << 3)
103 #define INCLUDE_DCCP (1 << 4)
104 #define INCLUDE_ICMP (1 << 5)
105 #define INCLUDE_SCTP (1 << 6)
107 struct sysctl_params_ctx
{
117 static volatile bool is_flow_collecting
;
118 static volatile sig_atomic_t sigint
= 0;
119 static int what
= INCLUDE_IPV4
| INCLUDE_IPV6
| INCLUDE_TCP
;
120 static struct flow_list flow_list
;
121 static struct sysctl_params_ctx sysctl
= { -1, -1 };
123 static unsigned int cols
, rows
;
125 static unsigned int interval
= 1;
126 static bool show_src
= false;
127 static bool resolve_dns
= true;
128 static bool resolve_geoip
= true;
129 static enum rate_units rate_type
= RATE_BYTES
;
130 static bool show_active_only
= false;
132 static const char *short_options
= "vhTUsDIS46ut:nGb";
133 static const struct option long_options
[] = {
134 {"ipv4", no_argument
, NULL
, '4'},
135 {"ipv6", no_argument
, NULL
, '6'},
136 {"tcp", no_argument
, NULL
, 'T'},
137 {"udp", no_argument
, NULL
, 'U'},
138 {"dccp", no_argument
, NULL
, 'D'},
139 {"icmp", no_argument
, NULL
, 'I'},
140 {"sctp", no_argument
, NULL
, 'S'},
141 {"no-dns", no_argument
, NULL
, 'n'},
142 {"no-geoip", no_argument
, NULL
, 'G'},
143 {"show-src", no_argument
, NULL
, 's'},
144 {"bits", no_argument
, NULL
, 'b'},
145 {"update", no_argument
, NULL
, 'u'},
146 {"interval", required_argument
, NULL
, 't'},
147 {"version", no_argument
, NULL
, 'v'},
148 {"help", no_argument
, NULL
, 'h'},
152 static const char *copyright
= "Please report bugs to <netsniff-ng@googlegroups.com>\n"
153 "Copyright (C) 2011-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>\n"
154 "Copyright (C) 2011-2012 Emmanuel Roullit <emmanuel.roullit@gmail.com>\n"
155 "Swiss federal institute of technology (ETH Zurich)\n"
156 "License: GNU GPL version 2.0\n"
157 "This is free software: you are free to change and redistribute it.\n"
158 "There is NO WARRANTY, to the extent permitted by law.";
160 static const char *const l3proto2str
[AF_MAX
] = {
165 static const char *const l4proto2str
[IPPROTO_MAX
] = {
166 [IPPROTO_TCP
] = "tcp",
167 [IPPROTO_UDP
] = "udp",
168 [IPPROTO_UDPLITE
] = "udplite",
169 [IPPROTO_ICMP
] = "icmp",
170 [IPPROTO_ICMPV6
] = "icmpv6",
171 [IPPROTO_SCTP
] = "sctp",
172 [IPPROTO_GRE
] = "gre",
173 [IPPROTO_DCCP
] = "dccp",
174 [IPPROTO_IGMP
] = "igmp",
175 [IPPROTO_IPIP
] = "ipip",
176 [IPPROTO_EGP
] = "egp",
177 [IPPROTO_PUP
] = "pup",
178 [IPPROTO_IDP
] = "idp",
179 [IPPROTO_RSVP
] = "rsvp",
180 [IPPROTO_IPV6
] = "ip6tun",
181 [IPPROTO_ESP
] = "esp",
183 [IPPROTO_PIM
] = "pim",
184 [IPPROTO_COMP
] = "comp",
187 static const char *const tcp_state2str
[TCP_CONNTRACK_MAX
] = {
188 [TCP_CONNTRACK_NONE
] = "NOSTATE",
189 [TCP_CONNTRACK_SYN_SENT
] = "SYN_SENT",
190 [TCP_CONNTRACK_SYN_RECV
] = "SYN_RECV",
191 [TCP_CONNTRACK_ESTABLISHED
] = "ESTABLISHED",
192 [TCP_CONNTRACK_FIN_WAIT
] = "FIN_WAIT",
193 [TCP_CONNTRACK_CLOSE_WAIT
] = "CLOSE_WAIT",
194 [TCP_CONNTRACK_LAST_ACK
] = "LAST_ACK",
195 [TCP_CONNTRACK_TIME_WAIT
] = "TIME_WAIT",
196 [TCP_CONNTRACK_CLOSE
] = "CLOSE",
197 [TCP_CONNTRACK_SYN_SENT2
] = "SYN_SENT2",
200 static const char *const dccp_state2str
[DCCP_CONNTRACK_MAX
] = {
201 [DCCP_CONNTRACK_NONE
] = "NOSTATE",
202 [DCCP_CONNTRACK_REQUEST
] = "REQUEST",
203 [DCCP_CONNTRACK_RESPOND
] = "RESPOND",
204 [DCCP_CONNTRACK_PARTOPEN
] = "PARTOPEN",
205 [DCCP_CONNTRACK_OPEN
] = "OPEN",
206 [DCCP_CONNTRACK_CLOSEREQ
] = "CLOSEREQ",
207 [DCCP_CONNTRACK_CLOSING
] = "CLOSING",
208 [DCCP_CONNTRACK_TIMEWAIT
] = "TIMEWAIT",
209 [DCCP_CONNTRACK_IGNORE
] = "IGNORE",
210 [DCCP_CONNTRACK_INVALID
] = "INVALID",
213 static const char *const sctp_state2str
[SCTP_CONNTRACK_MAX
] = {
214 [SCTP_CONNTRACK_NONE
] = "NOSTATE",
215 [SCTP_CONNTRACK_CLOSED
] = "CLOSED",
216 [SCTP_CONNTRACK_COOKIE_WAIT
] = "COOKIE_WAIT",
217 [SCTP_CONNTRACK_COOKIE_ECHOED
] = "COOKIE_ECHOED",
218 [SCTP_CONNTRACK_ESTABLISHED
] = "ESTABLISHED",
219 [SCTP_CONNTRACK_SHUTDOWN_SENT
] = "SHUTDOWN_SENT",
220 [SCTP_CONNTRACK_SHUTDOWN_RECD
] = "SHUTDOWN_RECD",
221 [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
] = "SHUTDOWN_ACK_SENT",
224 static const struct nfct_filter_ipv4 filter_ipv4
= {
225 .addr
= __constant_htonl(INADDR_LOOPBACK
),
229 static const struct nfct_filter_ipv6 filter_ipv6
= {
230 .addr
= { 0x0, 0x0, 0x0, 0x1 },
231 .mask
= { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
234 static int64_t time_after_us(struct timeval
*tv
)
238 bug_on(gettimeofday(&now
, NULL
));
240 now
.tv_sec
-= tv
->tv_sec
;
241 now
.tv_usec
-= tv
->tv_usec
;
243 return now
.tv_sec
* USEC_PER_SEC
+ now
.tv_usec
;
246 static void signal_handler(int number
)
260 static void flow_entry_from_ct(struct flow_entry
*n
, const struct nf_conntrack
*ct
);
261 static void flow_entry_get_extended(struct flow_entry
*n
);
263 static void help(void)
265 printf("flowtop %s, top-like netfilter TCP/UDP/SCTP/.. flow tracking\n",
267 puts("http://www.netsniff-ng.org\n\n"
268 "Usage: flowtop [options]\n"
270 " -4|--ipv4 Show only IPv4 flows (default)\n"
271 " -6|--ipv6 Show only IPv6 flows (default)\n"
272 " -T|--tcp Show only TCP flows (default)\n"
273 " -U|--udp Show only UDP flows\n"
274 " -D|--dccp Show only DCCP flows\n"
275 " -I|--icmp Show only ICMP/ICMPv6 flows\n"
276 " -S|--sctp Show only SCTP flows\n"
277 " -n|--no-dns Don't perform hostname lookup\n"
278 " -G|--no-geoip Don't perform GeoIP lookup\n"
279 " -s|--show-src Also show source, not only dest\n"
280 " -b|--bits Show rates in bits/s instead of bytes/s\n"
281 " -u|--update Update GeoIP databases\n"
282 " -t|--interval <time> Refresh time in seconds (default 1s)\n"
283 " -v|--version Print version and exit\n"
284 " -h|--help Print this help and exit\n\n"
287 " flowtop -46UTDISs\n\n"
289 " If netfilter is not running, you can activate it with e.g.:\n"
290 " iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT\n"
291 " iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT\n");
296 static void version(void)
298 printf("flowtop %s, Git id: %s\n", VERSION_LONG
, GITVERSION
);
299 puts("top-like netfilter TCP/UDP/SCTP/.. flow tracking\n"
300 "http://www.netsniff-ng.org\n");
305 static void flow_entry_update_time(struct flow_entry
*n
)
307 bug_on(gettimeofday(&n
->last_update
, NULL
));
310 #define CALC_RATE(fld) do { \
311 n->rate_##fld = (((fld) > n->fld) ? (((fld) - n->fld) / sec) : 0); \
314 static void flow_entry_calc_rate(struct flow_entry
*n
, const struct nf_conntrack
*ct
)
316 uint64_t bytes_src
= nfct_get_attr_u64(ct
, ATTR_ORIG_COUNTER_BYTES
);
317 uint64_t bytes_dst
= nfct_get_attr_u64(ct
, ATTR_REPL_COUNTER_BYTES
);
318 uint64_t pkts_src
= nfct_get_attr_u64(ct
, ATTR_ORIG_COUNTER_PACKETS
);
319 uint64_t pkts_dst
= nfct_get_attr_u64(ct
, ATTR_REPL_COUNTER_PACKETS
);
320 double sec
= (double)time_after_us(&n
->last_update
) / USEC_PER_SEC
;
325 CALC_RATE(bytes_src
);
326 CALC_RATE(bytes_dst
);
331 static inline struct flow_entry
*flow_entry_xalloc(void)
333 return xzmalloc(sizeof(struct flow_entry
));
336 static inline void flow_entry_xfree(struct flow_entry
*n
)
344 static inline void flow_list_init(struct flow_list
*fl
)
347 spinlock_init(&fl
->lock
);
350 static inline bool nfct_is_dns(const struct nf_conntrack
*ct
)
352 uint16_t port_src
= nfct_get_attr_u16(ct
, ATTR_ORIG_PORT_SRC
);
353 uint16_t port_dst
= nfct_get_attr_u16(ct
, ATTR_ORIG_PORT_DST
);
355 return ntohs(port_src
) == 53 || ntohs(port_dst
) == 53;
358 static void flow_list_new_entry(struct flow_list
*fl
, const struct nf_conntrack
*ct
)
360 struct flow_entry
*n
;
362 /* We don't want to analyze / display DNS itself, since we
363 * use it to resolve reverse dns.
368 n
= flow_entry_xalloc();
370 n
->ct
= nfct_clone(ct
);
372 flow_entry_update_time(n
);
373 flow_entry_from_ct(n
, ct
);
374 flow_entry_get_extended(n
);
376 rcu_assign_pointer(n
->next
, fl
->head
);
377 rcu_assign_pointer(fl
->head
, n
);
379 n
->is_visible
= true;
382 static struct flow_entry
*flow_list_find_id(struct flow_list
*fl
,
385 struct flow_entry
*n
= rcu_dereference(fl
->head
);
388 if (n
->flow_id
== id
)
391 n
= rcu_dereference(n
->next
);
397 static struct flow_entry
*flow_list_find_prev_id(const struct flow_list
*fl
,
400 struct flow_entry
*prev
= rcu_dereference(fl
->head
), *next
;
402 if (prev
->flow_id
== id
)
405 while ((next
= rcu_dereference(prev
->next
)) != NULL
) {
406 if (next
->flow_id
== id
)
415 static void flow_list_update_entry(struct flow_list
*fl
,
416 const struct nf_conntrack
*ct
)
418 struct flow_entry
*n
;
420 n
= flow_list_find_id(fl
, nfct_get_attr_u32(ct
, ATTR_ID
));
422 flow_list_new_entry(fl
, ct
);
426 flow_entry_from_ct(n
, ct
);
429 static void flow_list_destroy_entry(struct flow_list
*fl
,
430 const struct nf_conntrack
*ct
)
432 struct flow_entry
*n1
, *n2
;
433 uint32_t id
= nfct_get_attr_u32(ct
, ATTR_ID
);
435 n1
= flow_list_find_id(fl
, id
);
437 n2
= flow_list_find_prev_id(fl
, id
);
439 rcu_assign_pointer(n2
->next
, n1
->next
);
442 flow_entry_xfree(n1
);
444 struct flow_entry
*next
= fl
->head
->next
;
446 flow_entry_xfree(fl
->head
);
452 static void flow_list_destroy(struct flow_list
*fl
)
454 struct flow_entry
*n
;
456 while (fl
->head
!= NULL
) {
457 n
= rcu_dereference(fl
->head
->next
);
458 fl
->head
->next
= NULL
;
460 flow_entry_xfree(fl
->head
);
461 rcu_assign_pointer(fl
->head
, n
);
465 spinlock_destroy(&fl
->lock
);
468 static int walk_process(unsigned int pid
, struct flow_entry
*n
)
475 if (snprintf(path
, sizeof(path
), "/proc/%u/fd", pid
) == -1)
476 panic("giant process name! %u\n", pid
);
482 while ((ent
= readdir(dir
))) {
485 if (snprintf(path
, sizeof(path
), "/proc/%u/fd/%s",
486 pid
, ent
->d_name
) < 0)
489 if (stat(path
, &statbuf
) < 0)
492 if (S_ISSOCK(statbuf
.st_mode
) && (ino_t
) n
->inode
== statbuf
.st_ino
) {
495 ret
= proc_get_cmdline(pid
, cmdline
, sizeof(cmdline
));
497 panic("Failed to get process cmdline: %s\n", strerror(errno
));
499 if (snprintf(n
->procname
, sizeof(n
->procname
), "%s", basename(cmdline
)) < 0)
500 n
->procname
[0] = '\0';
511 static void walk_processes(struct flow_entry
*n
)
517 /* n->inode must be set */
519 n
->procname
[0] = '\0';
523 dir
= opendir("/proc");
525 panic("Cannot open /proc: %s\n", strerror(errno
));
527 while ((ent
= readdir(dir
))) {
528 const char *name
= ent
->d_name
;
530 unsigned int pid
= strtoul(name
, &end
, 10);
533 if (pid
== 0 && end
== name
)
536 ret
= walk_process(pid
, n
);
544 static int get_port_inode(uint16_t port
, int proto
, bool is_ip6
)
547 char path
[128], buff
[1024];
550 memset(path
, 0, sizeof(path
));
551 snprintf(path
, sizeof(path
), "/proc/net/%s%s",
552 l4proto2str
[proto
], is_ip6
? "6" : "");
554 proc
= fopen(path
, "r");
558 memset(buff
, 0, sizeof(buff
));
560 while (fgets(buff
, sizeof(buff
), proc
) != NULL
) {
562 unsigned int lport
= 0;
564 buff
[sizeof(buff
) - 1] = 0;
565 if (sscanf(buff
, "%*u: %*X:%X %*X:%*X %*X %*X:%*X %*X:%*X "
566 "%*X %*u %*u %u", &lport
, &inode
) == 2) {
567 if ((uint16_t) lport
== port
) {
573 memset(buff
, 0, sizeof(buff
));
580 #define CP_NFCT(elem, attr, x) \
581 do { n->elem = nfct_get_attr_u##x(ct,(attr)); } while (0)
582 #define CP_NFCT_BUFF(elem, attr) do { \
583 const uint8_t *buff = nfct_get_attr(ct,(attr)); \
585 memcpy(n->elem, buff, sizeof(n->elem)); \
588 static void flow_entry_from_ct(struct flow_entry
*n
, const struct nf_conntrack
*ct
)
590 CP_NFCT(l3_proto
, ATTR_ORIG_L3PROTO
, 8);
591 CP_NFCT(l4_proto
, ATTR_ORIG_L4PROTO
, 8);
593 CP_NFCT(ip4_src_addr
, ATTR_ORIG_IPV4_SRC
, 32);
594 CP_NFCT(ip4_dst_addr
, ATTR_ORIG_IPV4_DST
, 32);
596 CP_NFCT(port_src
, ATTR_ORIG_PORT_SRC
, 16);
597 CP_NFCT(port_dst
, ATTR_ORIG_PORT_DST
, 16);
599 CP_NFCT(status
, ATTR_STATUS
, 32);
601 CP_NFCT(tcp_state
, ATTR_TCP_STATE
, 8);
602 CP_NFCT(tcp_flags
, ATTR_TCP_FLAGS_ORIG
, 8);
603 CP_NFCT(sctp_state
, ATTR_SCTP_STATE
, 8);
604 CP_NFCT(dccp_state
, ATTR_DCCP_STATE
, 8);
606 CP_NFCT(pkts_src
, ATTR_ORIG_COUNTER_PACKETS
, 64);
607 CP_NFCT(bytes_src
, ATTR_ORIG_COUNTER_BYTES
, 64);
609 CP_NFCT(pkts_dst
, ATTR_REPL_COUNTER_PACKETS
, 64);
610 CP_NFCT(bytes_dst
, ATTR_REPL_COUNTER_BYTES
, 64);
612 CP_NFCT(timestamp_start
, ATTR_TIMESTAMP_START
, 64);
613 CP_NFCT(timestamp_stop
, ATTR_TIMESTAMP_STOP
, 64);
615 CP_NFCT(flow_id
, ATTR_ID
, 32);
616 CP_NFCT(use
, ATTR_USE
, 32);
618 CP_NFCT_BUFF(ip6_src_addr
, ATTR_ORIG_IPV6_SRC
);
619 CP_NFCT_BUFF(ip6_dst_addr
, ATTR_ORIG_IPV6_DST
);
621 n
->port_src
= ntohs(n
->port_src
);
622 n
->port_dst
= ntohs(n
->port_dst
);
624 n
->ip4_src_addr
= ntohl(n
->ip4_src_addr
);
625 n
->ip4_dst_addr
= ntohl(n
->ip4_dst_addr
);
628 #define SELFLD(dir,src_member,dst_member) \
629 (((dir) == FLOW_DIR_SRC) ? n->src_member : n->dst_member)
631 static void flow_entry_get_sain4_obj(const struct flow_entry
*n
,
632 enum flow_direction dir
,
633 struct sockaddr_in
*sa
)
635 memset(sa
, 0, sizeof(*sa
));
636 sa
->sin_family
= PF_INET
;
637 sa
->sin_addr
.s_addr
= htonl(SELFLD(dir
, ip4_src_addr
, ip4_dst_addr
));
640 static void flow_entry_get_sain6_obj(const struct flow_entry
*n
,
641 enum flow_direction dir
,
642 struct sockaddr_in6
*sa
)
644 memset(sa
, 0, sizeof(*sa
));
645 sa
->sin6_family
= PF_INET6
;
647 memcpy(&sa
->sin6_addr
, SELFLD(dir
, ip6_src_addr
, ip6_dst_addr
),
648 sizeof(sa
->sin6_addr
));
652 flow_entry_geo_city_lookup_generic(struct flow_entry
*n
,
653 enum flow_direction dir
)
655 struct sockaddr_in sa4
;
656 struct sockaddr_in6 sa6
;
657 const char *city
= NULL
;
659 switch (n
->l3_proto
) {
664 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
665 city
= geoip4_city_name(&sa4
);
669 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
670 city
= geoip6_city_name(&sa6
);
674 build_bug_on(sizeof(n
->city_src
) != sizeof(n
->city_dst
));
677 strlcpy(SELFLD(dir
, city_src
, city_dst
), city
,
678 sizeof(n
->city_src
));
680 SELFLD(dir
, city_src
, city_dst
)[0] = '\0';
684 flow_entry_geo_country_lookup_generic(struct flow_entry
*n
,
685 enum flow_direction dir
)
687 struct sockaddr_in sa4
;
688 struct sockaddr_in6 sa6
;
689 const char *country
= NULL
;
691 switch (n
->l3_proto
) {
696 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
697 country
= geoip4_country_name(&sa4
);
701 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
702 country
= geoip6_country_name(&sa6
);
706 build_bug_on(sizeof(n
->country_src
) != sizeof(n
->country_dst
));
709 strlcpy(SELFLD(dir
, country_src
, country_dst
), country
,
710 sizeof(n
->country_src
));
712 SELFLD(dir
, country_src
, country_dst
)[0] = '\0';
715 static void flow_entry_get_extended_geo(struct flow_entry
*n
,
716 enum flow_direction dir
)
719 flow_entry_geo_city_lookup_generic(n
, dir
);
720 flow_entry_geo_country_lookup_generic(n
, dir
);
724 static void flow_entry_get_extended_revdns(struct flow_entry
*n
,
725 enum flow_direction dir
)
728 struct sockaddr_in sa4
;
729 struct sockaddr_in6 sa6
;
731 struct hostent
*hent
;
733 build_bug_on(sizeof(n
->rev_dns_src
) != sizeof(n
->rev_dns_dst
));
735 switch (n
->l3_proto
) {
740 flow_entry_get_sain4_obj(n
, dir
, &sa4
);
743 inet_ntop(AF_INET
, &sa4
.sin_addr
,
744 SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
745 sizeof(n
->rev_dns_src
));
749 sa
= (struct sockaddr
*) &sa4
;
750 sa_len
= sizeof(sa4
);
751 hent
= gethostbyaddr(&sa4
.sin_addr
, sizeof(sa4
.sin_addr
), AF_INET
);
755 flow_entry_get_sain6_obj(n
, dir
, &sa6
);
758 inet_ntop(AF_INET6
, &sa6
.sin6_addr
,
759 SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
760 sizeof(n
->rev_dns_src
));
764 sa
= (struct sockaddr
*) &sa6
;
765 sa_len
= sizeof(sa6
);
766 hent
= gethostbyaddr(&sa6
.sin6_addr
, sizeof(sa6
.sin6_addr
), AF_INET6
);
770 getnameinfo(sa
, sa_len
, SELFLD(dir
, rev_dns_src
, rev_dns_dst
),
771 sizeof(n
->rev_dns_src
), NULL
, 0, NI_NUMERICHOST
);
774 strlcpy(SELFLD(dir
, rev_dns_src
, rev_dns_dst
), hent
->h_name
,
775 sizeof(n
->rev_dns_src
));
778 static void flow_entry_get_extended(struct flow_entry
*n
)
784 flow_entry_get_extended_revdns(n
, FLOW_DIR_SRC
);
785 flow_entry_get_extended_geo(n
, FLOW_DIR_SRC
);
788 flow_entry_get_extended_revdns(n
, FLOW_DIR_DST
);
789 flow_entry_get_extended_geo(n
, FLOW_DIR_DST
);
791 /* Lookup application */
792 n
->inode
= get_port_inode(n
->port_src
, n
->l4_proto
,
793 n
->l3_proto
== AF_INET6
);
798 static uint16_t presenter_get_port(uint16_t src
, uint16_t dst
, bool is_tcp
)
800 if (src
< dst
&& src
< 1024) {
802 } else if (dst
< src
&& dst
< 1024) {
805 const char *tmp1
, *tmp2
;
807 tmp1
= lookup_port_tcp(src
);
808 tmp2
= lookup_port_tcp(dst
);
810 tmp1
= lookup_port_udp(src
);
811 tmp2
= lookup_port_udp(dst
);
815 } else if (!tmp1
&& tmp2
) {
826 static char *bandw2str(double bytes
, char *buf
, size_t len
)
828 if (bytes
> 1000000000.)
829 snprintf(buf
, len
, "%.1fGB", bytes
/ 1000000000.);
830 else if (bytes
> 1000000.)
831 snprintf(buf
, len
, "%.1fMB", bytes
/ 1000000.);
832 else if (bytes
> 1000.)
833 snprintf(buf
, len
, "%.1fkB", bytes
/ 1000.);
835 snprintf(buf
, len
, "%g bytes", bytes
);
840 static char *rate2str(double rate
, char *buf
, size_t len
)
842 const char * const unit_fmt
[2][4] = {
843 { "%.1fGbit/s", "%.1fMbit/s", "%.1fkbit/s", "%gbit/s" },
844 { "%.1fGB/s", "%.1fMB/s", "%.1fkB/s", "%gB/s" }
847 if (rate_type
== RATE_BITS
)
850 if (rate
> 1000000000.)
851 snprintf(buf
, len
, unit_fmt
[rate_type
][0], rate
/ 1000000000.);
852 else if (rate
> 1000000.)
853 snprintf(buf
, len
, unit_fmt
[rate_type
][1], rate
/ 1000000.);
854 else if (rate
> 1000.)
855 snprintf(buf
, len
, unit_fmt
[rate_type
][2], rate
/ 1000.);
857 snprintf(buf
, len
, unit_fmt
[rate_type
][3], rate
);
862 static void presenter_print_counters(uint64_t bytes
, uint64_t pkts
,
863 double rate_bytes
, double rate_pkts
,
869 attron(COLOR_PAIR(color
));
870 printw("%"PRIu64
" pkts", pkts
);
872 attron(COLOR_PAIR(3));
873 printw("(%.1fpps)", rate_pkts
);
874 attron(COLOR_PAIR(color
));
877 printw(", %s", bandw2str(bytes
, bytes_str
, sizeof(bytes_str
) - 1));
879 attron(COLOR_PAIR(3));
880 printw("(%s)", rate2str(rate_bytes
, bytes_str
,
881 sizeof(bytes_str
) - 1));
882 attron(COLOR_PAIR(color
));
884 attroff(COLOR_PAIR(color
));
888 static void presenter_print_flow_entry_time(const struct flow_entry
*n
)
895 s
= now
- (n
->timestamp_start
/ NSEC_PER_SEC
);
914 static void draw_flow_entry(WINDOW
*screen
, const struct flow_entry
*n
,
917 char tmp
[128], *pname
= NULL
;
920 mvwprintw(screen
, *line
, 2, "");
922 /* PID, application name */
923 if (n
->procnum
> 0) {
924 slprintf(tmp
, sizeof(tmp
), "%s(%d)", n
->procname
, n
->procnum
);
927 attron(COLOR_PAIR(3));
929 attroff(COLOR_PAIR(3));
933 /* L3 protocol, L4 protocol, states */
934 printw("%s:%s", l3proto2str
[n
->l3_proto
], l4proto2str
[n
->l4_proto
]);
936 attron(COLOR_PAIR(3));
937 switch (n
->l4_proto
) {
939 printw("%s", tcp_state2str
[n
->tcp_state
]);
942 printw("%s", sctp_state2str
[n
->sctp_state
]);
945 printw("%s", dccp_state2str
[n
->dccp_state
]);
948 case IPPROTO_UDPLITE
:
954 attroff(COLOR_PAIR(3));
957 /* Guess application port */
958 switch (n
->l4_proto
) {
960 port
= presenter_get_port(n
->port_src
, n
->port_dst
, true);
961 pname
= lookup_port_tcp(port
);
964 case IPPROTO_UDPLITE
:
965 port
= presenter_get_port(n
->port_src
, n
->port_dst
, false);
966 pname
= lookup_port_udp(port
);
971 printw(":%s", pname
);
975 if (n
->timestamp_start
> 0)
976 presenter_print_flow_entry_time(n
);
978 /* Show source information: reverse DNS, port, country, city, counters */
980 attron(COLOR_PAIR(1));
981 mvwprintw(screen
, ++(*line
), 8, "src: %s", n
->rev_dns_src
);
982 attroff(COLOR_PAIR(1));
984 printw(":%"PRIu16
, n
->port_src
);
986 if (n
->country_src
[0]) {
989 attron(COLOR_PAIR(4));
990 printw("%s", n
->country_src
);
991 attroff(COLOR_PAIR(4));
994 printw(", %s", n
->city_src
);
999 if (n
->pkts_src
> 0 && n
->bytes_src
> 0)
1000 presenter_print_counters(n
->bytes_src
, n
->pkts_src
,
1002 n
->rate_pkts_src
, 1);
1007 /* Show dest information: reverse DNS, port, country, city, counters */
1008 attron(COLOR_PAIR(2));
1009 mvwprintw(screen
, ++(*line
), 8, "dst: %s", n
->rev_dns_dst
);
1010 attroff(COLOR_PAIR(2));
1012 printw(":%"PRIu16
, n
->port_dst
);
1014 if (n
->country_dst
[0]) {
1017 attron(COLOR_PAIR(4));
1018 printw("%s", n
->country_dst
);
1019 attroff(COLOR_PAIR(4));
1022 printw(", %s", n
->city_dst
);
1027 if (n
->pkts_dst
> 0 && n
->bytes_dst
> 0)
1028 presenter_print_counters(n
->bytes_dst
, n
->pkts_dst
,
1030 n
->rate_pkts_dst
, 2);
1033 static inline bool presenter_flow_wrong_state(struct flow_entry
*n
)
1035 switch (n
->l4_proto
) {
1037 switch (n
->tcp_state
) {
1038 case TCP_CONNTRACK_SYN_SENT
:
1039 case TCP_CONNTRACK_SYN_RECV
:
1040 case TCP_CONNTRACK_ESTABLISHED
:
1041 case TCP_CONNTRACK_FIN_WAIT
:
1042 case TCP_CONNTRACK_CLOSE_WAIT
:
1043 case TCP_CONNTRACK_LAST_ACK
:
1044 case TCP_CONNTRACK_TIME_WAIT
:
1045 case TCP_CONNTRACK_CLOSE
:
1046 case TCP_CONNTRACK_SYN_SENT2
:
1047 case TCP_CONNTRACK_NONE
:
1053 switch (n
->sctp_state
) {
1054 case SCTP_CONNTRACK_NONE
:
1055 case SCTP_CONNTRACK_CLOSED
:
1056 case SCTP_CONNTRACK_COOKIE_WAIT
:
1057 case SCTP_CONNTRACK_COOKIE_ECHOED
:
1058 case SCTP_CONNTRACK_ESTABLISHED
:
1059 case SCTP_CONNTRACK_SHUTDOWN_SENT
:
1060 case SCTP_CONNTRACK_SHUTDOWN_RECD
:
1061 case SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
:
1067 switch (n
->dccp_state
) {
1068 case DCCP_CONNTRACK_NONE
:
1069 case DCCP_CONNTRACK_REQUEST
:
1070 case DCCP_CONNTRACK_RESPOND
:
1071 case DCCP_CONNTRACK_PARTOPEN
:
1072 case DCCP_CONNTRACK_OPEN
:
1073 case DCCP_CONNTRACK_CLOSEREQ
:
1074 case DCCP_CONNTRACK_CLOSING
:
1075 case DCCP_CONNTRACK_TIMEWAIT
:
1076 case DCCP_CONNTRACK_IGNORE
:
1077 case DCCP_CONNTRACK_INVALID
:
1083 case IPPROTO_UDPLITE
:
1085 case IPPROTO_ICMPV6
:
1093 static void draw_flows(WINDOW
*screen
, struct flow_list
*fl
,
1096 int skip_left
= skip_lines
;
1097 unsigned int flows
= 0;
1098 unsigned int line
= 3;
1099 struct flow_entry
*n
;
1100 int maxy
= rows
- 6;
1107 n
= rcu_dereference(fl
->head
);
1109 mvwprintw(screen
, line
, 2, "(No sessions! "
1110 "Is netfilter running?)");
1112 for (; n
; n
= rcu_dereference(n
->next
)) {
1116 if (presenter_flow_wrong_state(n
))
1119 /* count only flows which might be showed */
1125 if (skip_left
> 0) {
1130 draw_flow_entry(screen
, n
, &line
);
1133 maxy
-= (2 + (show_src
? 1 : 0));
1136 mvwprintw(screen
, 1, 2,
1137 "Kernel netfilter flows(%u) for %s%s%s%s%s%s"
1138 "[+%d]", flows
, what
& INCLUDE_TCP
? "TCP, " : "",
1139 what
& INCLUDE_UDP
? "UDP, " : "",
1140 what
& INCLUDE_SCTP
? "SCTP, " : "",
1141 what
& INCLUDE_DCCP
? "DCCP, " : "",
1142 what
& INCLUDE_ICMP
&& what
& INCLUDE_IPV4
? "ICMP, " : "",
1143 what
& INCLUDE_ICMP
&& what
& INCLUDE_IPV6
? "ICMP6, " : "",
1146 if (is_flow_collecting
)
1147 printw(" [Collecting flows ...]");
1152 static void draw_help(WINDOW
*screen
)
1158 mvaddch(row
, col
, ACS_ULCORNER
);
1159 mvaddch(rows
- row
- 2, col
, ACS_LLCORNER
);
1161 mvaddch(row
, cols
- 1, ACS_URCORNER
);
1162 mvaddch(rows
- row
- 2, cols
- col
- 1, ACS_LRCORNER
);
1164 for (i
= 1; i
< rows
- row
- 2; i
++) {
1165 mvaddch(row
+ i
, 0, ACS_VLINE
);
1166 mvaddch(row
+ i
, cols
- col
- 1, ACS_VLINE
);
1168 for (i
= 1; i
< cols
- col
- 1; i
++) {
1169 mvaddch(0, col
+ i
, ACS_HLINE
);
1170 mvaddch(rows
- row
- 2, col
+ i
, ACS_HLINE
);
1174 mvaddnstr(row
, cols
/ 2 - 2, "| Help |", -1);
1176 attron(A_UNDERLINE
);
1177 mvaddnstr(row
+ 2, col
+ 2, "Navigation", -1);
1178 attroff(A_BOLD
| A_UNDERLINE
);
1180 mvaddnstr(row
+ 4, col
+ 3, "Up, u, k Move up", -1);
1181 mvaddnstr(row
+ 5, col
+ 3, "Down, d, j Move down", -1);
1182 mvaddnstr(row
+ 6, col
+ 3, "? Toggle help window", -1);
1183 mvaddnstr(row
+ 7, col
+ 3, "q, Ctrl+C Quit", -1);
1185 attron(A_BOLD
| A_UNDERLINE
);
1186 mvaddnstr(row
+ 9, col
+ 2, "Display Settings", -1);
1187 attroff(A_BOLD
| A_UNDERLINE
);
1189 mvaddnstr(row
+ 11, col
+ 3, "b Toggle rate units (bits/bytes)", -1);
1190 mvaddnstr(row
+ 12, col
+ 3, "a Toggle display of active flows (rate > 0) only", -1);
1193 static void draw_footer(WINDOW
*screen
)
1199 for (i
= 0; i
< cols
; i
++)
1200 mvaddch(rows
- 1, i
, ' ');
1202 mvaddnstr(rows
- 1, 1, "Press '?' for help", -1);
1204 attroff(A_STANDOUT
);
1207 static void presenter(void)
1209 int time_sleep_us
= 200000;
1210 int time_passed_us
= 0;
1211 bool show_help
= false;
1215 lookup_init_ports(PORTS_TCP
);
1216 lookup_init_ports(PORTS_UDP
);
1217 screen
= screen_init(false);
1220 init_pair(1, COLOR_RED
, COLOR_BLACK
);
1221 init_pair(2, COLOR_BLUE
, COLOR_BLACK
);
1222 init_pair(3, COLOR_YELLOW
, COLOR_BLACK
);
1223 init_pair(4, COLOR_GREEN
, COLOR_BLACK
);
1225 rcu_register_thread();
1227 bool redraw_flows
= true;
1230 getmaxyx(screen
, rows
, cols
);
1247 if (skip_lines
> SCROLL_MAX
)
1248 skip_lines
= SCROLL_MAX
;
1251 if (rate_type
== RATE_BYTES
)
1252 rate_type
= RATE_BITS
;
1254 rate_type
= RATE_BYTES
;
1257 show_active_only
= !show_active_only
;
1260 show_help
= !show_help
;
1266 redraw_flows
= false;
1271 redraw_flows
= time_passed_us
>= 1 * USEC_PER_SEC
;
1274 redraw_flows
= false;
1277 draw_flows(screen
, &flow_list
, skip_lines
);
1280 time_passed_us
+= time_sleep_us
;
1286 draw_footer(screen
);
1290 usleep(time_sleep_us
);
1292 rcu_unregister_thread();
1295 lookup_cleanup_ports(PORTS_UDP
);
1296 lookup_cleanup_ports(PORTS_TCP
);
1299 static int flow_event_cb(enum nf_conntrack_msg_type type
,
1300 struct nf_conntrack
*ct
, void *data __maybe_unused
)
1303 return NFCT_CB_STOP
;
1306 spinlock_lock(&flow_list
.lock
);
1310 flow_list_new_entry(&flow_list
, ct
);
1313 flow_list_update_entry(&flow_list
, ct
);
1315 case NFCT_T_DESTROY
:
1316 flow_list_destroy_entry(&flow_list
, ct
);
1322 spinlock_unlock(&flow_list
.lock
);
1324 return NFCT_CB_CONTINUE
;
1327 static void restore_sysctl(void *obj
)
1329 struct sysctl_params_ctx
*sysctl_ctx
= obj
;
1331 if (sysctl_ctx
->nfct_acct
== 0)
1332 sysctl_set_int("net/netfilter/nf_conntrack_acct",
1333 sysctl_ctx
->nfct_acct
);
1335 if (sysctl_ctx
->nfct_tstamp
== 0)
1336 sysctl_set_int("net/netfilter/nf_conntrack_timestamp",
1337 sysctl_ctx
->nfct_tstamp
);
1340 static void on_panic_handler(void *arg
)
1342 restore_sysctl(arg
);
1346 static void conntrack_acct_enable(void)
1348 /* We can still work w/o traffic accounting so just warn about error */
1349 if (sysctl_get_int("net/netfilter/nf_conntrack_acct", &sysctl
.nfct_acct
)) {
1350 fprintf(stderr
, "Can't read net/netfilter/nf_conntrack_acct: %s\n",
1355 if (sysctl
.nfct_acct
== 1)
1358 if (sysctl_set_int("net/netfilter/nf_conntrack_acct", 1)) {
1359 fprintf(stderr
, "Can't write net/netfilter/nf_conntrack_acct: %s\n",
1364 static void conntrack_tstamp_enable(void)
1366 if (sysctl_get_int("net/netfilter/nf_conntrack_timestamp", &sysctl
.nfct_tstamp
)) {
1367 fprintf(stderr
, "Can't read net/netfilter/nf_conntrack_timestamp: %s\n",
1372 if (sysctl
.nfct_tstamp
== 1)
1375 if (sysctl_set_int("net/netfilter/nf_conntrack_timestamp", 1)) {
1376 fprintf(stderr
, "Can't write net/netfilter/nf_conntrack_timestamp: %s\n",
1381 static void flow_entry_filter(struct flow_entry
*n
)
1383 if (show_active_only
&& !n
->rate_bytes_src
&& !n
->rate_bytes_dst
)
1384 n
->is_visible
= false;
1386 n
->is_visible
= true;
1389 static int flow_update_cb(enum nf_conntrack_msg_type type
,
1390 struct nf_conntrack
*ct
, void *data __maybe_unused
)
1392 struct flow_entry
*n
;
1394 if (type
!= NFCT_T_UPDATE
)
1395 return NFCT_CB_CONTINUE
;
1398 return NFCT_CB_STOP
;
1400 n
= flow_list_find_id(&flow_list
, nfct_get_attr_u32(ct
, ATTR_ID
));
1402 return NFCT_CB_CONTINUE
;
1404 flow_entry_calc_rate(n
, ct
);
1405 flow_entry_update_time(n
);
1406 flow_entry_from_ct(n
, ct
);
1407 flow_entry_filter(n
);
1409 return NFCT_CB_CONTINUE
;
1412 static void collector_refresh_flows(struct nfct_handle
*handle
)
1414 struct flow_entry
*n
;
1416 n
= rcu_dereference(flow_list
.head
);
1417 for (; n
; n
= rcu_dereference(n
->next
))
1418 nfct_query(handle
, NFCT_Q_GET
, n
->ct
);
1421 static void collector_create_filter(struct nfct_handle
*nfct
)
1423 struct nfct_filter
*filter
;
1426 filter
= nfct_filter_create();
1428 panic("Cannot create a nfct filter: %s\n", strerror(errno
));
1430 if (what
& INCLUDE_UDP
) {
1431 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_UDP
);
1432 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_UDPLITE
);
1434 if (what
& INCLUDE_TCP
)
1435 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_TCP
);
1436 if (what
& INCLUDE_DCCP
)
1437 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_DCCP
);
1438 if (what
& INCLUDE_SCTP
)
1439 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_SCTP
);
1440 if (what
& INCLUDE_ICMP
&& what
& INCLUDE_IPV4
)
1441 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_ICMP
);
1442 if (what
& INCLUDE_ICMP
&& what
& INCLUDE_IPV6
)
1443 nfct_filter_add_attr_u32(filter
, NFCT_FILTER_L4PROTO
, IPPROTO_ICMPV6
);
1444 if (what
& INCLUDE_IPV4
) {
1445 nfct_filter_set_logic(filter
, NFCT_FILTER_SRC_IPV4
, NFCT_FILTER_LOGIC_NEGATIVE
);
1446 nfct_filter_add_attr(filter
, NFCT_FILTER_SRC_IPV4
, &filter_ipv4
);
1448 if (what
& INCLUDE_IPV6
) {
1449 nfct_filter_set_logic(filter
, NFCT_FILTER_SRC_IPV6
, NFCT_FILTER_LOGIC_NEGATIVE
);
1450 nfct_filter_add_attr(filter
, NFCT_FILTER_SRC_IPV6
, &filter_ipv6
);
1453 ret
= nfct_filter_attach(nfct_fd(nfct
), filter
);
1455 panic("Cannot attach filter to handle: %s\n", strerror(errno
));
1457 nfct_filter_destroy(filter
);
1460 /* This hand-crafted filter looks ugly but it allows to do not
1461 * flush nfct connections & filter them by user specified filter.
1462 * May be it is better to replace this one by nfct_cmp. */
1463 static int flow_dump_cb(enum nf_conntrack_msg_type type
,
1464 struct nf_conntrack
*ct
, void *data __maybe_unused
)
1466 struct flow_entry fl
;
1467 struct flow_entry
*n
= &fl
;
1470 return NFCT_CB_STOP
;
1473 spinlock_lock(&flow_list
.lock
);
1475 if (!(what
& ~(INCLUDE_IPV4
| INCLUDE_IPV6
)))
1478 CP_NFCT(l4_proto
, ATTR_ORIG_L4PROTO
, 8);
1480 if (what
& INCLUDE_UDP
) {
1481 if (n
->l4_proto
== IPPROTO_UDP
)
1484 if (n
->l4_proto
== IPPROTO_UDPLITE
)
1488 if ((what
& INCLUDE_TCP
) && n
->l4_proto
== IPPROTO_TCP
)
1491 if ((what
& INCLUDE_DCCP
) && n
->l4_proto
== IPPROTO_DCCP
)
1494 if ((what
& INCLUDE_SCTP
) && n
->l4_proto
== IPPROTO_SCTP
)
1497 if ((what
& INCLUDE_ICMP
) && (what
& INCLUDE_IPV4
) &&
1498 n
->l4_proto
== IPPROTO_ICMP
) {
1502 if ((what
& INCLUDE_ICMP
) && (what
& INCLUDE_IPV6
) &&
1503 n
->l4_proto
== IPPROTO_ICMPV6
) {
1510 /* filter loopback addresses */
1511 if (what
& INCLUDE_IPV4
) {
1512 CP_NFCT(ip4_src_addr
, ATTR_ORIG_IPV4_SRC
, 32);
1514 if (n
->ip4_src_addr
== filter_ipv4
.addr
)
1517 if (what
& INCLUDE_IPV6
) {
1518 CP_NFCT_BUFF(ip6_src_addr
, ATTR_ORIG_IPV6_SRC
);
1520 if (n
->ip6_src_addr
[0] == 0x0 &&
1521 n
->ip6_src_addr
[1] == 0x0 &&
1522 n
->ip6_src_addr
[2] == 0x0 &&
1523 n
->ip6_src_addr
[3] == 0x1)
1527 flow_list_new_entry(&flow_list
, ct
);
1530 spinlock_unlock(&flow_list
.lock
);
1531 return NFCT_CB_CONTINUE
;
1534 static void collector_dump_flows(void)
1536 struct nfct_handle
*nfct
= nfct_open(CONNTRACK
, 0);
1539 panic("Cannot create a nfct handle: %s\n", strerror(errno
));
1541 nfct_callback_register(nfct
, NFCT_T_ALL
, flow_dump_cb
, NULL
);
1543 is_flow_collecting
= true;
1544 if (what
& INCLUDE_IPV4
) {
1545 int family
= AF_INET
;
1546 nfct_query(nfct
, NFCT_Q_DUMP
, &family
);
1548 if (what
& INCLUDE_IPV6
) {
1549 int family
= AF_INET6
;
1550 nfct_query(nfct
, NFCT_Q_DUMP
, &family
);
1552 is_flow_collecting
= false;
1557 static void *collector(void *null __maybe_unused
)
1559 struct nfct_handle
*ct_update
;
1560 struct nfct_handle
*ct_event
;
1561 struct pollfd poll_fd
[1];
1563 flow_list_init(&flow_list
);
1565 ct_event
= nfct_open(CONNTRACK
, NF_NETLINK_CONNTRACK_NEW
|
1566 NF_NETLINK_CONNTRACK_UPDATE
|
1567 NF_NETLINK_CONNTRACK_DESTROY
);
1569 panic("Cannot create a nfct handle: %s\n", strerror(errno
));
1571 collector_create_filter(ct_event
);
1573 nfct_callback_register(ct_event
, NFCT_T_ALL
, flow_event_cb
, NULL
);
1575 ct_update
= nfct_open(CONNTRACK
, NF_NETLINK_CONNTRACK_UPDATE
);
1577 panic("Cannot create a nfct handle: %s\n", strerror(errno
));
1579 nfct_callback_register(ct_update
, NFCT_T_ALL
, flow_update_cb
, NULL
);
1581 poll_fd
[0].fd
= nfct_fd(ct_event
);
1582 poll_fd
[0].events
= POLLIN
;
1584 if (fcntl(nfct_fd(ct_event
), F_SETFL
, O_NONBLOCK
) == -1)
1585 panic("Cannot set non-blocking socket: fcntl(): %s\n",
1588 if (fcntl(nfct_fd(ct_update
), F_SETFL
, O_NONBLOCK
) == -1)
1589 panic("Cannot set non-blocking socket: fcntl(): %s\n",
1592 rcu_register_thread();
1594 collector_dump_flows();
1599 usleep(USEC_PER_SEC
* interval
);
1601 collector_refresh_flows(ct_update
);
1603 status
= poll(poll_fd
, 1, 0);
1605 if (errno
== EAGAIN
|| errno
== EINTR
)
1608 panic("Error while polling: %s\n", strerror(errno
));
1609 } else if (status
== 0) {
1613 if (poll_fd
[0].revents
& POLLIN
)
1614 nfct_catch(ct_event
);
1617 rcu_unregister_thread();
1619 flow_list_destroy(&flow_list
);
1620 nfct_close(ct_event
);
1621 nfct_close(ct_update
);
1626 int main(int argc
, char **argv
)
1629 int ret
, c
, opt_index
, what_cmd
= 0;
1634 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
1635 &opt_index
)) != EOF
) {
1638 what_cmd
|= INCLUDE_IPV4
;
1641 what_cmd
|= INCLUDE_IPV6
;
1644 what_cmd
|= INCLUDE_TCP
;
1647 what_cmd
|= INCLUDE_UDP
;
1650 what_cmd
|= INCLUDE_DCCP
;
1653 what_cmd
|= INCLUDE_ICMP
;
1656 what_cmd
|= INCLUDE_SCTP
;
1662 rate_type
= RATE_BITS
;
1669 interval
= strtoul(optarg
, NULL
, 10);
1672 resolve_dns
= false;
1675 resolve_geoip
= false;
1691 if (!(what
& (INCLUDE_IPV4
| INCLUDE_IPV6
)))
1692 what
|= INCLUDE_IPV4
| INCLUDE_IPV6
;
1697 register_signal(SIGINT
, signal_handler
);
1698 register_signal(SIGQUIT
, signal_handler
);
1699 register_signal(SIGTERM
, signal_handler
);
1700 register_signal(SIGHUP
, signal_handler
);
1702 panic_handler_add(on_panic_handler
, &sysctl
);
1704 conntrack_acct_enable();
1705 conntrack_tstamp_enable();
1710 ret
= pthread_create(&tid
, NULL
, collector
, NULL
);
1712 panic("Cannot create phthread!\n");
1719 restore_sysctl(&sysctl
);