/* * netsniff-ng - the packet sniffing beast * Copyright 2011 - 2013 Daniel Borkmann. * Copyright 2011 Emmanuel Roullit. * Subject to the GPL, version 2. */ #define _LGPL_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui.h" #include "die.h" #include "xmalloc.h" #include "conntrack.h" #include "config.h" #include "str.h" #include "sig.h" #include "lookup.h" #include "geoip.h" #include "built_in.h" #include "pkt_buff.h" #include "screen.h" #include "proc.h" #include "sysctl.h" #ifndef NSEC_PER_SEC #define NSEC_PER_SEC 1000000000L #endif #ifndef USEC_PER_SEC #define USEC_PER_SEC 1000000L #endif struct flow_stat { uint64_t pkts_src, bytes_src; uint64_t pkts_dst, bytes_dst; double rate_bytes_src; double rate_bytes_dst; double rate_pkts_src; double rate_pkts_dst; }; struct proc_entry { struct cds_list_head entry; struct cds_list_head flows; struct rcu_head rcu; struct timeval last_update; struct flow_stat stat; unsigned int pid; char name[256]; int flows_count; }; struct flow_entry { struct cds_list_head proc_head; struct cds_list_head entry; struct rcu_head rcu; uint32_t flow_id, use, status; uint8_t l3_proto, l4_proto; uint32_t ip4_src_addr, ip4_dst_addr; uint32_t ip6_src_addr[4], ip6_dst_addr[4]; uint16_t port_src, port_dst; uint8_t tcp_state, tcp_flags, sctp_state, dccp_state; uint64_t timestamp_start, timestamp_stop; char country_src[128], country_dst[128]; char country_code_src[4], country_code_dst[4]; char city_src[128], city_dst[128]; char rev_dns_src[256], rev_dns_dst[256]; struct proc_entry *proc; int inode; bool is_visible; struct nf_conntrack *ct; struct timeval last_update; struct flow_stat stat; }; struct flow_list { struct cds_list_head head; }; struct proc_list { struct cds_list_head head; }; enum flow_direction { FLOW_DIR_SRC, FLOW_DIR_DST, }; #ifndef ATTR_TIMESTAMP_START # define ATTR_TIMESTAMP_START 63 #endif #ifndef ATTR_TIMESTAMP_STOP # define ATTR_TIMESTAMP_STOP 64 #endif #define INCLUDE_IPV4 (1 << 0) #define INCLUDE_IPV6 (1 << 1) #define INCLUDE_UDP (1 << 2) #define INCLUDE_TCP (1 << 3) #define INCLUDE_DCCP (1 << 4) #define INCLUDE_ICMP (1 << 5) #define INCLUDE_SCTP (1 << 6) #define TOGGLE_FLAG(what, flag) \ do { \ if (what & flag) \ what &= ~flag; \ else \ what |= flag; \ } while (0) struct sysctl_params_ctx { int nfct_acct; int nfct_tstamp; }; enum rate_units { RATE_BITS, RATE_BYTES }; static volatile bool do_reload_flows; static volatile bool is_flow_collecting; static volatile sig_atomic_t sigint = 0; static int what = INCLUDE_IPV4 | INCLUDE_IPV6 | INCLUDE_TCP; static struct proc_list proc_list; static struct flow_list flow_list; static struct sysctl_params_ctx sysctl = { -1, -1 }; static unsigned int cols, rows; static WINDOW *screen; static unsigned int interval = 1; static bool show_src = false; static bool resolve_dns = true; static bool resolve_geoip = true; static enum rate_units rate_type = RATE_BYTES; static bool show_active_only = false; enum tbl_flow_col { TBL_FLOW_PROCESS, TBL_FLOW_PID, TBL_FLOW_PROTO, TBL_FLOW_STATE, TBL_FLOW_TIME, TBL_FLOW_ADDRESS, TBL_FLOW_PORT, TBL_FLOW_GEO, TBL_FLOW_BYTES, TBL_FLOW_RATE, }; enum tbl_proc_col { TBL_PROC_NAME, TBL_PROC_PID, TBL_PROC_FLOWS, TBL_PROC_BYTES_SRC, TBL_PROC_RATE_SRC, TBL_PROC_BYTES_DST, TBL_PROC_RATE_DST, }; static struct ui_table flows_tbl; static struct ui_table procs_tbl; static struct ui_table *curr_tbl; enum tab_entry { TAB_FLOWS, TAB_PROCS, }; #define list_first_or_next(__ptr, __head, __entry) \ ({ \ struct cds_list_head *h; \ if (!__ptr) \ h = rcu_dereference((__head)->next); \ else if (rcu_dereference(__ptr->__entry.next) == (__head)) \ return NULL; \ else \ h = rcu_dereference(__ptr->__entry.next); \ cds_list_entry(h, __typeof(* (__ptr)), __entry); \ }) static const char *short_options = "vhTUsDIS46ut:nGb"; static const struct option long_options[] = { {"ipv4", no_argument, NULL, '4'}, {"ipv6", no_argument, NULL, '6'}, {"tcp", no_argument, NULL, 'T'}, {"udp", no_argument, NULL, 'U'}, {"dccp", no_argument, NULL, 'D'}, {"icmp", no_argument, NULL, 'I'}, {"sctp", no_argument, NULL, 'S'}, {"no-dns", no_argument, NULL, 'n'}, {"no-geoip", no_argument, NULL, 'G'}, {"show-src", no_argument, NULL, 's'}, {"bits", no_argument, NULL, 'b'}, {"update", no_argument, NULL, 'u'}, {"interval", required_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; static const char *copyright = "Please report bugs at https://github.com/netsniff-ng/netsniff-ng/issues\n" "Copyright (C) 2011-2013 Daniel Borkmann \n" "Copyright (C) 2011-2012 Emmanuel Roullit \n" "Swiss federal institute of technology (ETH Zurich)\n" "License: GNU GPL version 2.0\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law."; static const char *const l4proto2str[IPPROTO_MAX] = { [IPPROTO_TCP] = "tcp", [IPPROTO_UDP] = "udp", [IPPROTO_UDPLITE] = "udplite", [IPPROTO_ICMP] = "icmp", [IPPROTO_ICMPV6] = "icmpv6", [IPPROTO_SCTP] = "sctp", [IPPROTO_GRE] = "gre", [IPPROTO_DCCP] = "dccp", [IPPROTO_IGMP] = "igmp", [IPPROTO_IPIP] = "ipip", [IPPROTO_EGP] = "egp", [IPPROTO_PUP] = "pup", [IPPROTO_IDP] = "idp", [IPPROTO_RSVP] = "rsvp", [IPPROTO_IPV6] = "ip6tun", [IPPROTO_ESP] = "esp", [IPPROTO_AH] = "ah", [IPPROTO_PIM] = "pim", [IPPROTO_COMP] = "comp", }; static const char *const tcp_state2str[TCP_CONNTRACK_MAX] = { [TCP_CONNTRACK_NONE] = "NONE", [TCP_CONNTRACK_SYN_SENT] = "SYN-SENT", [TCP_CONNTRACK_SYN_RECV] = "SYN-RECV", [TCP_CONNTRACK_ESTABLISHED] = "ESTABLISHED", [TCP_CONNTRACK_FIN_WAIT] = "FIN-WAIT", [TCP_CONNTRACK_CLOSE_WAIT] = "CLOSE-WAIT", [TCP_CONNTRACK_LAST_ACK] = "LAST-ACK", [TCP_CONNTRACK_TIME_WAIT] = "TIME-WAIT", [TCP_CONNTRACK_CLOSE] = "CLOSE", [TCP_CONNTRACK_SYN_SENT2] = "SYN-SENT2", }; static const char *const dccp_state2str[DCCP_CONNTRACK_MAX] = { [DCCP_CONNTRACK_NONE] = "NONE", [DCCP_CONNTRACK_REQUEST] = "REQUEST", [DCCP_CONNTRACK_RESPOND] = "RESPOND", [DCCP_CONNTRACK_PARTOPEN] = "PARTOPEN", [DCCP_CONNTRACK_OPEN] = "OPEN", [DCCP_CONNTRACK_CLOSEREQ] = "CLOSE-REQ", [DCCP_CONNTRACK_CLOSING] = "CLOSING", [DCCP_CONNTRACK_TIMEWAIT] = "TIME-WAIT", [DCCP_CONNTRACK_IGNORE] = "IGNORE", [DCCP_CONNTRACK_INVALID] = "INVALID", }; static const char *const sctp_state2str[SCTP_CONNTRACK_MAX] = { [SCTP_CONNTRACK_NONE] = "NONE", [SCTP_CONNTRACK_CLOSED] = "CLOSED", [SCTP_CONNTRACK_COOKIE_WAIT] = "COOKIE-WAIT", [SCTP_CONNTRACK_COOKIE_ECHOED] = "COOKIE-ECHO", [SCTP_CONNTRACK_ESTABLISHED] = "ESTABLISHED", [SCTP_CONNTRACK_SHUTDOWN_SENT] = "SHUTD-SENT", [SCTP_CONNTRACK_SHUTDOWN_RECD] = "SHUTD-RCVD", [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT] = "SHUTD-ACK", }; static const struct nfct_filter_ipv4 filter_ipv4 = { .addr = __constant_htonl(INADDR_LOOPBACK), .mask = 0xffffffff, }; static const struct nfct_filter_ipv6 filter_ipv6 = { .addr = { 0x0, 0x0, 0x0, 0x1 }, .mask = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }, }; static int64_t time_after_us(struct timeval *tv) { struct timeval now; bug_on(gettimeofday(&now, NULL)); now.tv_sec -= tv->tv_sec; now.tv_usec -= tv->tv_usec; return now.tv_sec * USEC_PER_SEC + now.tv_usec; } static void signal_handler(int number) { switch (number) { case SIGINT: case SIGQUIT: case SIGTERM: sigint = 1; break; case SIGHUP: default: break; } } static void flow_entry_from_ct(struct flow_entry *n, const struct nf_conntrack *ct); static void flow_entry_get_extended(struct flow_entry *n); static void help(void) { printf("flowtop %s, top-like netfilter TCP/UDP/SCTP/.. flow tracking\n", VERSION_STRING); puts("http://www.netsniff-ng.org\n\n" "Usage: flowtop [options]\n" "Options:\n" " -4|--ipv4 Show only IPv4 flows (default)\n" " -6|--ipv6 Show only IPv6 flows (default)\n" " -T|--tcp Show only TCP flows (default)\n" " -U|--udp Show only UDP flows\n" " -D|--dccp Show only DCCP flows\n" " -I|--icmp Show only ICMP/ICMPv6 flows\n" " -S|--sctp Show only SCTP flows\n" " -n|--no-dns Don't perform hostname lookup\n" " -G|--no-geoip Don't perform GeoIP lookup\n" " -s|--show-src Also show source, not only dest\n" " -b|--bits Show rates in bits/s instead of bytes/s\n" " -u|--update Update GeoIP databases\n" " -t|--interval