From f4430f9164346b7d2973ffbba66712e533b113c0 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 28 Nov 2012 22:00:38 +0100 Subject: [PATCH] flowtop: refactor major parts of the code (more on todo) Signed-off-by: Daniel Borkmann --- src/built_in.h | 25 ++ src/flowtop.c | 725 ++++++++++++++++++++++++++++----------------------------- src/list.h | 120 ---------- 3 files changed, 386 insertions(+), 484 deletions(-) delete mode 100644 src/list.h diff --git a/src/built_in.h b/src/built_in.h index 1cfa8b1e..ad0ca9b0 100644 --- a/src/built_in.h +++ b/src/built_in.h @@ -2,6 +2,7 @@ * netsniff-ng - the packet sniffing beast * By Daniel Borkmann * Copyright 2009-2012 Daniel Borkmann. + * Parts taken from the Linux kernel, GPL, version 2. * Subject to the GPL, version 2. */ @@ -152,6 +153,10 @@ typedef uint8_t u8; # define __pure __attribute__ ((pure)) #endif +#ifndef __force +# define __force /* unimplemented */ +#endif + #ifndef force_cast # define force_cast(type, arg) ((type) (arg)) #endif @@ -244,6 +249,16 @@ static inline uint64_t ntohll(uint64_t x) # error __BYTE_ORDER is neither __LITTLE_ENDIAN nor __BIG_ENDIAN #endif +#define ___constant_swab16(x) ((__u16)( \ + (((__u16)(x) & (__u16)0x00ffU) << 8) | \ + (((__u16)(x) & (__u16)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ + (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ + (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ + (((__u32)(x) & (__u32)0xff000000UL) >> 24))) + #if __BYTE_ORDER == __LITTLE_ENDIAN static inline u16 cpu_to_be16(u16 val) { @@ -274,6 +289,11 @@ static inline u64 cpu_to_le64(u64 val) { return val; } + +# define __constant_htonl(x) ((__force __be32)___constant_swab32((x))) +# define __constant_ntohl(x) ___constant_swab32((__force __be32)(x)) +# define __constant_htons(x) ((__force __be16)___constant_swab16((x))) +# define __constant_ntohs(x) ___constant_swab16((__force __be16)(x)) #elif __BYTE_ORDER == __BIG_ENDIAN static inline u16 cpu_to_be16(u16 val) { @@ -304,6 +324,11 @@ static inline u64 cpu_to_le64(u64 val) { return bswap_64(val); } + +# define __constant_htonl(x) ((__force __be32)(__u32)(x)) +# define __constant_ntohl(x) ((__force __u32)(__be32)(x)) +# define __constant_htons(x) ((__force __be16)(__u16)(x)) +# define __constant_ntohs(x) ((__force __u16)(__be16)(x)) #else # error __BYTE_ORDER is neither __LITTLE_ENDIAN nor __BIG_ENDIAN #endif diff --git a/src/flowtop.c b/src/flowtop.c index b9aafe0a..49e12e5d 100644 --- a/src/flowtop.c +++ b/src/flowtop.c @@ -1,7 +1,7 @@ /* * netsniff-ng - the packet sniffing beast * By Daniel Borkmann - * Copyright 2011 Daniel Borkmann. + * Copyright 2011 - 2012 Daniel Borkmann. * Copyright 2011 Emmanuel Roullit. * Subject to the GPL, version 2. * @@ -45,8 +45,27 @@ #include "dissector_eth.h" #include "pkt_buff.h" -#define INCLUDE_UDP (1 << 0) -#define INCLUDE_TCP (1 << 1) +struct flow_entry { + 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; + uint64_t counter_pkts, counter_bytes; + uint64_t timestamp_start, timestamp_stop; + char country_src[128], country_dst[128]; + char city_src[128], city_dst[128]; + char rev_dns_src[256], rev_dns_dst[256]; + int first, procnum, inode; + char cmdline[256]; + struct flow_entry *next; +}; + +struct flow_list { + struct flow_entry *head; + struct spinlock lock; +}; #ifndef ATTR_TIMESTAMP_START # define ATTR_TIMESTAMP_START 63 @@ -57,54 +76,17 @@ #define SCROLL_MAX 1000 -struct flow_entry { - uint32_t flow_id; - int first; - struct flow_entry *next; - uint32_t use; - uint32_t status; - uint8_t l3_proto; - uint8_t l4_proto; - uint32_t ip4_src_addr; - uint32_t ip4_dst_addr; - uint32_t ip6_src_addr[4]; - uint32_t ip6_dst_addr[4]; - uint16_t port_src; - uint16_t port_dst; - uint8_t tcp_state; - uint8_t tcp_flags; - uint64_t counter_pkts; - uint64_t counter_bytes; - uint64_t timestamp_start; - uint64_t timestamp_stop; - char country_src[128]; - char city_src[128]; - char rev_dns_src[256]; - char country_dst[128]; - char city_dst[128]; - char rev_dns_dst[256]; - int procnum; - int inode; - char cmdline[256]; -}; - -struct flow_list { - struct flow_entry *head; - struct spinlock lock; -}; - volatile sig_atomic_t sigint = 0; -static int what = INCLUDE_TCP; +#define INCLUDE_UDP (1 << 0) +#define INCLUDE_TCP (1 << 1) -static int show_src = 0; +static int what = INCLUDE_TCP, show_src = 0; static struct flow_list flow_list; -static GeoIP *gi_country = NULL; -static GeoIP *gi_city = NULL; - -static char *path_city_db = NULL, *path_country_db = NULL; +static GeoIP *gi_country = NULL, *gi_city = NULL; +static char *path_country_db = NULL, *path_city_db = NULL; static const char *short_options = "vhTULKs"; static const struct option long_options[] = { @@ -118,12 +100,12 @@ static const struct option long_options[] = { {NULL, 0, NULL, 0} }; -const char *const l3proto2str[AF_MAX] = { +static const char *const l3proto2str[AF_MAX] = { [AF_INET] = "ipv4", [AF_INET6] = "ipv6", }; -const char *const proto2str[IPPROTO_MAX] = { +static const char *const proto2str[IPPROTO_MAX] = { [IPPROTO_TCP] = "tcp", [IPPROTO_UDP] = "udp", [IPPROTO_UDPLITE] = "udplite", @@ -145,7 +127,7 @@ const char *const proto2str[IPPROTO_MAX] = { [IPPROTO_COMP] = "comp", }; -const char *const state2str[TCP_CONNTRACK_MAX] = { +static const char *const state2str[TCP_CONNTRACK_MAX] = { [TCP_CONNTRACK_NONE] = "NOSTATE", [TCP_CONNTRACK_SYN_SENT] = "SYN_SENT", [TCP_CONNTRACK_SYN_RECV] = "SYN_RECV", @@ -158,7 +140,7 @@ const char *const state2str[TCP_CONNTRACK_MAX] = { [TCP_CONNTRACK_SYN_SENT2] = "SYN_SENT2", }; -const uint8_t states[] = { +static const uint8_t states[] = { TCP_CONNTRACK_SYN_SENT, TCP_CONNTRACK_SYN_RECV, TCP_CONNTRACK_ESTABLISHED, @@ -171,6 +153,16 @@ const uint8_t states[] = { TCP_CONNTRACK_NONE, }; +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 void signal_handler(int number) { switch (number) { @@ -183,6 +175,11 @@ static void signal_handler(int number) } } +static inline const char *make_n_a(const char *p) +{ + return p ? : "N/A"; +} + static void help(void) { printf("\nflowtop %s, top-like netfilter TCP/UDP flow tracking\n", @@ -199,6 +196,7 @@ static void help(void) " -h|--help Print this help\n\n" "Examples:\n" " flowtop\n" + " flowtop -UT\n" " flowtop -s\n\n" "Note:\n" " If netfilter is not running, you can activate it with i.e.:\n" @@ -227,207 +225,9 @@ static void version(void) die(); } -static void screen_init(WINDOW **screen) -{ - (*screen) = initscr(); - noecho(); - cbreak(); - keypad(stdscr, TRUE); - nodelay(*screen, TRUE); - refresh(); - wrefresh(*screen); -} - -static inline uint16_t get_port(uint16_t src, uint16_t dst) -{ - char *tmp1, *tmp2; - - src = ntohs(src); - dst = ntohs(dst); - - /* XXX: Is there a better way to determine? */ - if (src < dst && src < 1024) { - return src; - } else if (dst < src && dst < 1024) { - return dst; - } else { - tmp1 = lookup_port_tcp(src); - tmp2 = lookup_port_tcp(dst); - if (tmp1 && !tmp2) { - return src; - } else if (!tmp1 && tmp2) { - return dst; - } else { - if (src < dst) - return src; - else - return dst; - } - } -} - -static void screen_update(WINDOW *screen, struct flow_list *fl, int skip_lines) +static int walk_process(char *process, struct flow_entry *n) { - int i, line = 3; - int maxy; - struct flow_entry *n; - - curs_set(0); - maxy = getmaxy(screen); - - start_color(); - init_pair(1, COLOR_RED, COLOR_BLACK); - init_pair(2, COLOR_BLUE, COLOR_BLACK); - init_pair(3, COLOR_YELLOW, COLOR_BLACK); - init_pair(4, COLOR_GREEN, COLOR_BLACK); - - wclear(screen); - clear(); - - rcu_read_lock(); - - mvwprintw(screen, 1, 2, "Kernel netfilter TCP/UDP flow statistics, [+%d]", - skip_lines); - - if (rcu_dereference(fl->head) == NULL) - mvwprintw(screen, line, 2, "(No active sessions! Is netfilter running?)"); - - maxy -= 4; - /* Yes, that's lame :-P */ - for (i = 0; i < sizeof(states); i++) { - n = rcu_dereference(fl->head); - - while (n && maxy > 0) { - char tmp[128]; - - if (n->tcp_state != states[i] || - (i != TCP_CONNTRACK_NONE && - n->tcp_state == TCP_CONNTRACK_NONE) || - /* Filter out DNS */ - get_port(n->port_src, n->port_dst) == 53) { - n = rcu_dereference(n->next); - continue; - } - - if (skip_lines > 0) { - n = rcu_dereference(n->next); - skip_lines--; - continue; - } - - snprintf(tmp, sizeof(tmp), "%u/%s", n->procnum, - basename(n->cmdline)); - tmp[sizeof(tmp) - 1] = 0; - - mvwprintw(screen, line, 2, "["); - attron(COLOR_PAIR(3)); - printw("%s", n->procnum > 0 ? tmp : "bridged(?)"); - attroff(COLOR_PAIR(3)); - printw("]:%s:%s[", l3proto2str[n->l3_proto], - proto2str[n->l4_proto]); - attron(COLOR_PAIR(3)); - printw("%s", state2str[n->tcp_state]); - attroff(COLOR_PAIR(3)); - printw("]:"); - attron(A_BOLD); - if (n->tcp_state != TCP_CONNTRACK_NONE) { - printw("%s -> ", lookup_port_tcp(get_port(n->port_src, - n->port_dst))); - } else { - printw("%s -> ", lookup_port_udp(get_port(n->port_src, - n->port_dst))); - } - attroff(A_BOLD); - if (show_src) { - attron(COLOR_PAIR(1)); - mvwprintw(screen, ++line, 8, "src: %s", n->rev_dns_src); - attroff(COLOR_PAIR(1)); - printw(":%u (", ntohs(n->port_src)); - attron(COLOR_PAIR(4)); - printw("%s", (strlen(n->country_src) > 0 ? - n->country_src : "N/A")); - attroff(COLOR_PAIR(4)); - printw(", %s) => ", (strlen(n->city_src) > 0 ? - n->city_src : "N/A")); - } - attron(COLOR_PAIR(2)); - mvwprintw(screen, ++line, 8, "dst: %s", n->rev_dns_dst); - attroff(COLOR_PAIR(2)); - printw(":%u (", ntohs(n->port_dst)); - attron(COLOR_PAIR(4)); - printw("%s", strlen(n->country_dst) > 0 ? - n->country_dst : "N/A"); - attroff(COLOR_PAIR(4)); - printw(", %s)", strlen(n->city_dst) > 0 ? - n->city_dst : "N/A"); - - line++; - maxy--; - n = rcu_dereference(n->next); - } - } - - rcu_read_unlock(); - - wrefresh(screen); - refresh(); -} - -static void screen_end(void) -{ - endwin(); -} - -static void presenter(void) -{ - int skip_lines = 0; - WINDOW *screen = NULL; - - dissector_init_ethernet(0); - screen_init(&screen); - rcu_register_thread(); - - while (!sigint) { - switch (getch()) { - case 'q': - sigint = 1; - break; - case KEY_UP: - case 'u': - case 'k': - skip_lines--; - if (skip_lines < 0) - skip_lines = 0; - break; - case KEY_DOWN: - case 'd': - case 'j': - skip_lines++; - if (skip_lines > SCROLL_MAX) - skip_lines = SCROLL_MAX; - break; - default: - fflush(stdin); - break; - } - - screen_update(screen, &flow_list, skip_lines); - usleep(100000); - } - - rcu_unregister_thread(); - screen_end(); - dissector_cleanup_ethernet(); -} - -static inline const char *make_n_a(const char *p) -{ - return p ? : "N/A"; -} - -static void walk_process(char *process, struct flow_entry *n) -{ - int rc; + int ret; DIR *dir; struct dirent *ent; char path[1024]; @@ -437,7 +237,7 @@ static void walk_process(char *process, struct flow_entry *n) dir = opendir(path); if (!dir) - return; + return 0; while ((ent = readdir(dir))) { struct stat statbuf; @@ -445,30 +245,36 @@ static void walk_process(char *process, struct flow_entry *n) if (snprintf(path, sizeof(path), "/proc/%s/fd/%s", process, ent->d_name) < 0) continue; + if (stat(path, &statbuf) < 0) continue; + if (S_ISSOCK(statbuf.st_mode) && n->inode == statbuf.st_ino) { memset(n->cmdline, 0, sizeof(n->cmdline)); + snprintf(path, sizeof(path), "/proc/%s/exe", process); - rc = readlink(path, n->cmdline, sizeof(n->cmdline) - 1); - if (rc < 0) + ret = readlink(path, n->cmdline, + sizeof(n->cmdline) - 1); + if (ret < 0) panic("readlink error: %s\n", strerror(errno)); n->procnum = atoi(process); + return 1; } } closedir(dir); + return 0; } -/* Derived from ifpromisc, Fred N. van Kempen, GPL v2.0 */ -/* n->inode must be set */ static void walk_processes(struct flow_entry *n) { + int ret; DIR *dir; struct dirent *ent; + /* n->inode must be set */ if (n->inode <= 0) { memset(n->cmdline, 0, sizeof(n->cmdline)); return; @@ -478,80 +284,88 @@ static void walk_processes(struct flow_entry *n) if (!dir) panic("Cannot open /proc!\n"); - while ((ent = readdir(dir))) - if (strspn(ent->d_name, "0123456789") == strlen(ent->d_name)) - walk_process(ent->d_name, n); + while ((ent = readdir(dir))) { + if (strspn(ent->d_name, "0123456789") == strlen(ent->d_name)) { + ret = walk_process(ent->d_name, n); + if (ret > 0) + break; + } + } closedir(dir); } -static int get_inode_from_local_port(int port, const char *proto, int ip6) +static int get_port_inode(uint16_t port, int proto, int is_ip6) { int ret = -ENOENT; - char path[128]; - char buff[1024]; + char path[128], buff[1024]; FILE *proc; memset(path, 0, sizeof(path)); - snprintf(path, sizeof(path), "/proc/net/%s%s", proto, ip6 ? "6" : ""); + snprintf(path, sizeof(path), "/proc/net/%s%s", + proto2str[proto], is_ip6 ? "6" : ""); + proc = fopen(path, "r"); if (!proc) return -EIO; + memset(buff, 0, sizeof(buff)); + while (fgets(buff, sizeof(buff), proc) != NULL) { - int lport = 0, inode = 0; + int inode = 0; + unsigned int lport = 0; + buff[sizeof(buff) - 1] = 0; if (sscanf(buff, "%*u: %*X:%X %*X:%*X %*X %*X:%*X %*X:%*X " "%*X %*u %*u %u", &lport, &inode) == 2) { - if (lport == port) { + if ((uint16_t) lport == port) { ret = inode; break; } } + memset(buff, 0, sizeof(buff)); } - fclose(proc); + fclose(proc); return ret; } +#define CP_NFCT(elem, attr, x) do { n->elem = nfct_get_attr_u##x(ct,(attr)); } while (0) +#define CP_NFCT_BUFF(elem, attr) do { \ + const uint8_t *buff = nfct_get_attr(ct,(attr)); \ + if (buff != NULL) \ + memcpy(n->elem, buff, sizeof(n->elem)); \ +} while (0) + static void flow_entry_from_ct(struct flow_entry *n, struct nf_conntrack *ct) { - const uint8_t *ipv6_src; - const uint8_t *ipv6_dst; - - n->flow_id = nfct_get_attr_u32(ct, ATTR_ID); - n->use = nfct_get_attr_u32(ct, ATTR_USE); - n->status = nfct_get_attr_u32(ct, ATTR_STATUS); - n->l3_proto = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO); - n->l4_proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); - n->ip4_src_addr = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC); - n->ip4_dst_addr = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST); - - ipv6_src = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC); - if (ipv6_src) - memcpy(n->ip6_src_addr, ipv6_src, sizeof(n->ip6_src_addr)); - ipv6_dst = nfct_get_attr(ct, ATTR_ORIG_IPV6_DST); - if (ipv6_dst) - memcpy(n->ip6_dst_addr, ipv6_dst, sizeof(n->ip6_dst_addr)); - - n->port_src = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC); - n->port_dst = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST); - n->tcp_state = nfct_get_attr_u8(ct, ATTR_TCP_STATE); - n->tcp_flags = nfct_get_attr_u8(ct, ATTR_TCP_FLAGS_ORIG); - n->counter_pkts = nfct_get_attr_u64(ct, ATTR_ORIG_COUNTER_PACKETS); - n->counter_bytes = nfct_get_attr_u64(ct, ATTR_ORIG_COUNTER_BYTES); - n->timestamp_start = nfct_get_attr_u64(ct, ATTR_TIMESTAMP_START); - n->timestamp_stop = nfct_get_attr_u64(ct, ATTR_TIMESTAMP_STOP); + CP_NFCT(l3_proto, ATTR_ORIG_L3PROTO, 8); + CP_NFCT(l4_proto, ATTR_ORIG_L4PROTO, 8); + CP_NFCT(ip4_src_addr, ATTR_ORIG_IPV4_SRC, 32); + CP_NFCT(ip4_dst_addr, ATTR_ORIG_IPV4_DST, 32); + CP_NFCT(port_src, ATTR_ORIG_PORT_SRC, 16); + CP_NFCT(port_dst, ATTR_ORIG_PORT_DST, 16); + CP_NFCT(status, ATTR_STATUS, 32); + CP_NFCT(tcp_state, ATTR_TCP_STATE, 8); + CP_NFCT(tcp_flags, ATTR_TCP_FLAGS_ORIG, 8); + CP_NFCT(counter_pkts, ATTR_ORIG_COUNTER_PACKETS, 64); + CP_NFCT(counter_bytes, ATTR_ORIG_COUNTER_BYTES, 64); + CP_NFCT(timestamp_start, ATTR_TIMESTAMP_START, 64); + CP_NFCT(timestamp_stop, ATTR_TIMESTAMP_STOP, 64); + CP_NFCT(flow_id, ATTR_ID, 32); + CP_NFCT(use, ATTR_USE, 32); + + CP_NFCT_BUFF(ip6_src_addr, ATTR_ORIG_IPV6_SRC); + CP_NFCT_BUFF(ip6_dst_addr, ATTR_ORIG_IPV6_DST); if (n->first) { - n->inode = get_inode_from_local_port(ntohs(n->port_src), - proto2str[n->l4_proto], - !!(ipv6_src)); + n->inode = get_port_inode(ntohs(n->port_src), n->l4_proto, + n->l3_proto == AF_INET6); if (n->inode > 0) walk_processes(n); } - /* if this really runs on a router, we try it once and then let it be */ + n->first = 0; } @@ -618,12 +432,25 @@ static void flow_entry_get_extended(struct flow_entry *n) } } -static void flow_list_init(struct flow_list *fl) +static inline void flow_list_init(struct flow_list *fl) { fl->head = NULL; spinlock_init(&fl->lock); } +static void flow_list_new_entry(struct flow_list *fl, struct nf_conntrack *ct) +{ + struct flow_entry *n = xzmalloc(sizeof(*n)); + + n->first = 1; + + rcu_assign_pointer(n->next, fl->head); + rcu_assign_pointer(fl->head, n); + + flow_entry_from_ct(n, ct); + flow_entry_get_extended(n); +} + static struct flow_entry *__flow_list_find_by_id(struct flow_list *fl, uint32_t id) { struct flow_entry *n = rcu_dereference(fl->head); @@ -648,16 +475,6 @@ static struct flow_entry *__flow_list_find_prev_by_id(struct flow_list *fl, uint return NULL; } -static void flow_list_new_entry(struct flow_list *fl, struct nf_conntrack *ct) -{ - struct flow_entry *n = xzmalloc(sizeof(*n)); - n->first = 1; - rcu_assign_pointer(n->next, fl->head); - rcu_assign_pointer(fl->head, n); - flow_entry_from_ct(n, ct); - flow_entry_get_extended(n); -} - static void flow_list_update_entry(struct flow_list *fl, struct nf_conntrack *ct) { int do_ext = 0; @@ -710,16 +527,207 @@ static void flow_list_destroy(struct flow_list *fl) spinlock_destroy(&fl->lock); } +static uint16_t get_port(uint16_t src, uint16_t dst) +{ + char *tmp1, *tmp2; + + src = ntohs(src); + dst = ntohs(dst); + + /* XXX: Is there a better way to determine? */ + if (src < dst && src < 1024) { + return src; + } else if (dst < src && dst < 1024) { + return dst; + } else { + tmp1 = lookup_port_tcp(src); + tmp2 = lookup_port_tcp(dst); + if (tmp1 && !tmp2) { + return src; + } else if (!tmp1 && tmp2) { + return dst; + } else { + if (src < dst) + return src; + else + return dst; + } + } +} + +static void screen_init(WINDOW **screen) +{ + (*screen) = initscr(); + noecho(); + cbreak(); + keypad(stdscr, TRUE); + nodelay(*screen, TRUE); + refresh(); + wrefresh(*screen); +} + +static void screen_update(WINDOW *screen, struct flow_list *fl, int skip_lines) +{ + int i, line = 3, maxy; + struct flow_entry *n; + + curs_set(0); + maxy = getmaxy(screen); + + start_color(); + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_BLUE, COLOR_BLACK); + init_pair(3, COLOR_YELLOW, COLOR_BLACK); + init_pair(4, COLOR_GREEN, COLOR_BLACK); + + wclear(screen); + clear(); + + rcu_read_lock(); + + mvwprintw(screen, 1, 2, "Kernel netfilter TCP/UDP flow statistics, [+%d]", + skip_lines); + + if (rcu_dereference(fl->head) == NULL) + mvwprintw(screen, line, 2, "(No active sessions! Is netfilter running?)"); + + maxy -= 4; + /* Yes, that's lame :-P */ + for (i = 0; i < sizeof(states); i++) { + n = rcu_dereference(fl->head); + + while (n && maxy > 0) { + char tmp[128]; + + if (n->tcp_state != states[i] || + (i != TCP_CONNTRACK_NONE && + n->tcp_state == TCP_CONNTRACK_NONE) || + /* Filter out DNS */ + get_port(n->port_src, n->port_dst) == 53) { + n = rcu_dereference(n->next); + continue; + } + + if (skip_lines > 0) { + n = rcu_dereference(n->next); + skip_lines--; + continue; + } + + snprintf(tmp, sizeof(tmp), "%u/%s", n->procnum, + basename(n->cmdline)); + tmp[sizeof(tmp) - 1] = 0; + + mvwprintw(screen, line, 2, "["); + attron(COLOR_PAIR(3)); + printw("%s", n->procnum > 0 ? tmp : "bridged(?)"); + attroff(COLOR_PAIR(3)); + printw("]:%s:%s[", l3proto2str[n->l3_proto], + proto2str[n->l4_proto]); + attron(COLOR_PAIR(3)); + printw("%s", state2str[n->tcp_state]); + attroff(COLOR_PAIR(3)); + printw("]:"); + attron(A_BOLD); + if (n->tcp_state != TCP_CONNTRACK_NONE) { + printw("%s -> ", lookup_port_tcp(get_port(n->port_src, + n->port_dst))); + } else { + printw("%s -> ", lookup_port_udp(get_port(n->port_src, + n->port_dst))); + } + attroff(A_BOLD); + if (show_src) { + attron(COLOR_PAIR(1)); + mvwprintw(screen, ++line, 8, "src: %s", n->rev_dns_src); + attroff(COLOR_PAIR(1)); + printw(":%u (", ntohs(n->port_src)); + attron(COLOR_PAIR(4)); + printw("%s", (strlen(n->country_src) > 0 ? + n->country_src : "N/A")); + attroff(COLOR_PAIR(4)); + printw(", %s) => ", (strlen(n->city_src) > 0 ? + n->city_src : "N/A")); + } + attron(COLOR_PAIR(2)); + mvwprintw(screen, ++line, 8, "dst: %s", n->rev_dns_dst); + attroff(COLOR_PAIR(2)); + printw(":%u (", ntohs(n->port_dst)); + attron(COLOR_PAIR(4)); + printw("%s", strlen(n->country_dst) > 0 ? + n->country_dst : "N/A"); + attroff(COLOR_PAIR(4)); + printw(", %s)", strlen(n->city_dst) > 0 ? + n->city_dst : "N/A"); + + line++; + maxy--; + n = rcu_dereference(n->next); + } + } + + rcu_read_unlock(); + + wrefresh(screen); + refresh(); +} + +static inline void screen_end(void) +{ + endwin(); +} + +static void presenter(void) +{ + int skip_lines = 0; + WINDOW *screen = NULL; + + dissector_init_ethernet(0); + screen_init(&screen); + + rcu_register_thread(); + while (!sigint) { + switch (getch()) { + case 'q': + sigint = 1; + break; + case KEY_UP: + case 'u': + case 'k': + skip_lines--; + if (skip_lines < 0) + skip_lines = 0; + break; + case KEY_DOWN: + case 'd': + case 'j': + skip_lines++; + if (skip_lines > SCROLL_MAX) + skip_lines = SCROLL_MAX; + break; + default: + fflush(stdin); + break; + } + + screen_update(screen, &flow_list, skip_lines); + usleep(100000); + } + rcu_unregister_thread(); + + screen_end(); + dissector_cleanup_ethernet(); +} + static int collector_cb(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) + struct nf_conntrack *ct, void *data) { if (sigint) return NFCT_CB_STOP; synchronize_rcu(); - spinlock_lock(&flow_list.lock); + switch (type) { case NFCT_T_NEW: flow_list_new_entry(&flow_list, ct); @@ -733,109 +741,95 @@ static int collector_cb(enum nf_conntrack_msg_type type, default: break; } + spinlock_unlock(&flow_list.lock); return NFCT_CB_CONTINUE; } -static int dummy_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, - void *data) +static inline GeoIP *collector_geoip_open(const char *path, int type) +{ + if (path != NULL) + return GeoIP_open(path, GEOIP_MMAP_CACHE); + else + return GeoIP_open_type(type, GEOIP_MMAP_CACHE); +} + +static void collector_load_geoip(void) +{ + gi_country = collector_geoip_open(path_country_db, + GEOIP_COUNTRY_EDITION); + if (gi_country == NULL) + panic("Cannot open GeoIP country database!\n"); + + gi_city = collector_geoip_open(path_city_db, + GEOIP_CITY_EDITION_REV1); + if (gi_city == NULL) + panic("Cannot open GeoIP city database!\n"); + + GeoIP_set_charset(gi_country, GEOIP_CHARSET_UTF8); + GeoIP_set_charset(gi_city, GEOIP_CHARSET_UTF8); +} + +static void collector_destroy_geoip(void) { - return NFCT_CB_STOP; + GeoIP_delete(gi_city); + GeoIP_delete(gi_country); } static void *collector(void *null) { int ret; - u_int32_t family = AF_INET; struct nfct_handle *handle; struct nfct_filter *filter; - struct nfct_filter_ipv4 filter_ipv4 = { - .addr = ntohl(INADDR_LOOPBACK), - .mask = 0xffffffff, - }; - struct nfct_filter_ipv6 filter_ipv6 = { - .addr = { 0x0, 0x0, 0x0, 0x1 }, - .mask = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }, - }; - - handle = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS); - if (!handle) - panic("Cannot create a nfct handle!\n"); - - /* Hack: inits ct */ - nfct_callback_register(handle, NFCT_T_ALL, dummy_cb, NULL); - nfct_query(handle, NFCT_Q_DUMP, &family); - nfct_close(handle); handle = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS); if (!handle) panic("Cannot create a nfct handle!\n"); - nfct_query(handle, NFCT_Q_FLUSH, &family); - filter = nfct_filter_create(); if (!filter) panic("Cannot create a nfct filter!\n"); + if (what & INCLUDE_UDP) - nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, IPPROTO_UDP); + nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, + IPPROTO_UDP); if (what & INCLUDE_TCP) - nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, IPPROTO_TCP); + nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, + IPPROTO_TCP); nfct_filter_set_logic(filter, NFCT_FILTER_SRC_IPV4, NFCT_FILTER_LOGIC_NEGATIVE); - nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, &filter_ipv4); + nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, + &filter_ipv4); nfct_filter_set_logic(filter, NFCT_FILTER_SRC_IPV6, NFCT_FILTER_LOGIC_NEGATIVE); - nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV6, &filter_ipv6); + nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV6, + &filter_ipv6); ret = nfct_filter_attach(nfct_fd(handle), filter); if (ret < 0) panic("Cannot attach filter to handle!\n"); - nfct_filter_destroy(filter); - - if (path_country_db) - gi_country = GeoIP_open(path_country_db, GEOIP_MMAP_CACHE); - else - gi_country = GeoIP_open_type(GEOIP_COUNTRY_EDITION, - GEOIP_MMAP_CACHE); - - if (path_city_db) - gi_city = GeoIP_open(path_city_db, GEOIP_MMAP_CACHE); - else - gi_city = GeoIP_open_type(GEOIP_CITY_EDITION_REV1, - GEOIP_MMAP_CACHE); - if (!gi_country || !gi_city) - panic("Cannot open GeoIP database!\n"); + nfct_callback_register(handle, NFCT_T_ALL, collector_cb, NULL); - GeoIP_set_charset(gi_country, GEOIP_CHARSET_UTF8); - GeoIP_set_charset(gi_city, GEOIP_CHARSET_UTF8); + collector_load_geoip(); flow_list_init(&flow_list); rcu_register_thread(); - - nfct_callback_register(handle, NFCT_T_ALL, collector_cb, NULL); - while (!sigint) nfct_catch(handle); - rcu_unregister_thread(); flow_list_destroy(&flow_list); - GeoIP_delete(gi_city); - GeoIP_delete(gi_country); + collector_destroy_geoip(); + nfct_filter_destroy(filter); nfct_close(handle); - if (path_city_db) - xfree(path_city_db); - if (path_country_db) - xfree(path_country_db); - pthread_exit(0); } @@ -898,6 +892,9 @@ int main(int argc, char **argv) panic("Cannot create phthread!\n"); presenter(); + + free(path_city_db); + free(path_country_db); + return 0; } - diff --git a/src/list.h b/src/list.h deleted file mode 100644 index bda62b4b..00000000 --- a/src/list.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * netsniff-ng - the packet sniffing beast - * list.c - Doubly linked list implementation - * Copyright (C) 2011 Jiri Pirko - * Subject to the GPL, version 2. - */ - -#ifndef LIST_H -#define LIST_H - -#include - -struct list_item { - struct list_item *prev; - struct list_item *next; -}; - -static inline void list_init(struct list_item *head) -{ - head->prev = head; - head->next = head; -} - -static inline bool list_empty(struct list_item *head) -{ - return head->next == head; -} - -static inline void __list_add(struct list_item *new_node, - struct list_item *prev_node, - struct list_item *next_node) -{ - new_node->prev = prev_node; - new_node->next = next_node; - prev_node->next = new_node; - next_node->prev = new_node; -} - -static inline void list_add(struct list_item *head, struct list_item *node) -{ - __list_add(node, head, head->next); -} - -static inline void list_add_tail(struct list_item *head, struct list_item *node) -{ - __list_add(node, head->prev, head); -} - -static inline void list_del(struct list_item *node) -{ - node->prev->next = node->next; - node->next->prev = node->prev; -} - -static inline void list_move_nodes(struct list_item *dst_head, - struct list_item *src_head) -{ - if (list_empty(src_head)) - return; - dst_head->prev->next = src_head->next; - src_head->next->prev = dst_head->prev; - dst_head->prev = src_head->prev; - src_head->prev->next = dst_head; - list_init(src_head); -} - -static inline struct list_item *list_get_next_node(struct list_item *head, - struct list_item *node) -{ - if (node->next == head) - return NULL; - return node->next; -} - -#define list_for_each_node(node, head) \ - for (node = list_get_next_node(head, head); \ - node; \ - node = list_get_next_node(head, node)) - -#define in_struct_offset(struct_type, struct_member) \ - ((size_t) (&((struct_type *) 0)->struct_member)) - -#define get_container(ptr, struct_type, struct_member) \ - ((struct_type *) ( \ - ((size_t) ptr) - \ - in_struct_offset(struct_type, struct_member))) - -#define list_get_node_entry(node, struct_type, struct_member) \ - get_container(node, struct_type, struct_member) - -#define list_for_each_node_entry(entry, head, struct_member) \ - for (entry = list_get_node_entry((head)->next, typeof(*entry), \ - struct_member); \ - &entry->struct_member != (head); \ - entry = list_get_node_entry(entry->struct_member.next, \ - typeof(*entry), struct_member)) - -#define list_for_each_node_entry_continue_reverse(entry, head, struct_member) \ - for (entry = list_get_node_entry(entry->struct_member.prev, \ - typeof(*entry), struct_member); \ - &entry->struct_member != (head); \ - entry = list_get_node_entry(entry->struct_member.prev, \ - typeof(*entry), struct_member)) - -#define list_for_each_node_entry_safe(entry, tmp, head, struct_member) \ - for (entry = list_get_node_entry((head)->next, \ - typeof(*entry), struct_member), \ - tmp = list_get_node_entry(entry->struct_member.next, \ - typeof(*entry), struct_member); \ - &entry->struct_member != (head); \ - entry = tmp, \ - tmp = list_get_node_entry(entry->struct_member.next, \ - typeof(*entry), struct_member)) - -#define list_get_next_node_entry(head, entry, struct_member) ({ \ - struct list_item *next = (entry ? &entry->struct_member : (head))->next;\ - (next == (head)) ? NULL : list_get_node_entry(next, typeof(*entry), \ - struct_member);}) - -#endif /* LIST_H */ -- 2.11.4.GIT