From f3e428e6fab326c993e17eeae1c223dcb5f8dc2b Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sun, 16 Jul 2023 11:32:00 -0400 Subject: [PATCH] feature: stats support for --nettrace --- RELNOTES | 1 + src/firejail/netfilter.c | 2 +- src/fnettrace/fnettrace.h | 5 +++ src/fnettrace/main.c | 56 +++++++++++++++++++++++++++++---- src/fnettrace/radix.c | 27 ++++++++++------ src/fnettrace/radix.h | 2 +- src/fnettrace/static-ip-map.txt | 14 +++++++++ src/fnettrace/terminal.c | 52 ++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 src/fnettrace/terminal.c diff --git a/RELNOTES b/RELNOTES index dfa62a7c0..365641094 100644 --- a/RELNOTES +++ b/RELNOTES @@ -7,6 +7,7 @@ firejail (0.9.73) baseline; urgency=low * feature: add IPv6 support for --net.print option * feature: QUIC (HTTP/3) support in --nettrace * feature: use seccomp filters build at install time for --restrict-namespaces + * feature: stats support for --nettrace * modif: Stop forwarding own double-dash to the shell (#5599 #5600) * modif: Prevent sandbox name (--name=) and host name (--hostname=) from containing only digits (#5578 #5741) diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c index 32fdd6218..458fb0dd1 100644 --- a/src/firejail/netfilter.c +++ b/src/firejail/netfilter.c @@ -108,7 +108,7 @@ void netfilter_trace(pid_t pid, const char *cmd) { arg[3] = NULL; clearenv(); - sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg); + sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP | SBOX_ALLOW_STDIN, arg); // it will never get here!! } diff --git a/src/fnettrace/fnettrace.h b/src/fnettrace/fnettrace.h index 629b8ce5b..b1a2f5b6c 100644 --- a/src/fnettrace/fnettrace.h +++ b/src/fnettrace/fnettrace.h @@ -70,4 +70,9 @@ char* retrieve_hostname(uint32_t ip); // tail.c void tail(const char *logfile); +// terminal.c +void terminal_handler(int s); +void terminal_set(void); +void terminal_restore(void); + #endif diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c index 136a16e6d..932afff61 100644 --- a/src/fnettrace/main.c +++ b/src/fnettrace/main.c @@ -29,6 +29,11 @@ static int arg_netfilter = 0; static int arg_tail = 0; static char *arg_log = NULL; +uint32_t stats_pkts = 0; +uint32_t stats_icmp = 0; +uint32_t stats_dns = 0; + + typedef struct hnode_t { struct hnode_t *hnext; // used for hash table and unused linked list struct hnode_t *dnext; // used to display streams on the screen @@ -331,7 +336,7 @@ static void hnode_print(unsigned bw) { else sprintf(stats, "%u KB/s ", bw / (1024 * DISPLAY_INTERVAL)); // int len = snprintf(line, LINE_MAX, "%32s geoip %d, IP database %d\n", stats, geoip_calls, radix_nodes); - int len = snprintf(line, LINE_MAX, "%32s address:port (protocol) host (packets)\n", stats); + int len = snprintf(line, LINE_MAX, "%32s address:port (protocol) network (packets)\n", stats); adjust_line(line, len, cols); printf("%s", line); @@ -418,6 +423,7 @@ static void hnode_print(unsigned bw) { ptr = next; } + printf("press any key to access stats\n"); #ifdef DEBUG { @@ -432,6 +438,14 @@ static void hnode_print(unsigned bw) { #endif } + +void print_stats(void) { + printf("\nIP table: %d entries, %d unknown\n", radix_nodes, geoip_calls); + printf(" address network (packets)\n"); + radix_print(1); + printf("Packets: %u total, ICMP %u, DNS %u\n", stats_pkts, stats_icmp, stats_dns); +} + // trace rx traffic coming in static void run_trace(void) { if (arg_netfilter) @@ -449,6 +463,7 @@ static void run_trace(void) { unsigned last_print_remaining = 0; unsigned char buf[MAX_BUF_SIZE]; unsigned bw = 0; // bandwidth calculations + while (1) { unsigned end = time(NULL); if (arg_netfilter && end - start >= NETLOCK_INTERVAL) @@ -470,6 +485,8 @@ static void run_trace(void) { FD_SET(s1, &rfds); FD_SET(s2, &rfds); FD_SET(s3, &rfds); + if (!arg_netfilter) + FD_SET(0, &rfds); int maxfd = (s1 > s2) ? s1 : s2; maxfd = (s3 > maxfd) ? s3 : maxfd; maxfd++; @@ -484,9 +501,20 @@ static void run_trace(void) { else if (rv == 0) continue; - int icmp = 0; + + // rx tcp traffic by default int sock = s1; - if (FD_ISSET(s2, &rfds)) + int icmp = 0; + + if (FD_ISSET(0, &rfds)) { + getchar(); + print_stats(); + printf("press any key to continue..."); + fflush(0); + getchar(); + continue; + } + else if (FD_ISSET(s2, &rfds)) sock = s2; else if (FD_ISSET(s3, &rfds)) { sock = s3; @@ -516,22 +544,32 @@ static void run_trace(void) { ip_src = ntohl(ip_src); uint8_t hlen = (buf[0] & 0x0f) * 4; + uint16_t port_src = 0; if (icmp) hnode_add(ip_src, 0, 0, bytes + 14); else { - uint16_t port_src; memcpy(&port_src, buf + hlen, 2); port_src = ntohs(port_src); uint8_t protocol = buf[9]; hnode_add(ip_src, protocol, port_src, bytes + 14); } + + // stats + stats_pkts++; + if (icmp) + stats_icmp++; + if (port_src == 53) + stats_dns++; + } } } close(s1); close(s2); + close(s3); + print_stats(); } static char *filter_start = @@ -733,7 +771,7 @@ int main(int argc, char **argv) { else if (strcmp(argv[i], "--print-map") == 0) { char *fname = "static-ip-map.txt"; load_hostnames(fname); - radix_print(); + radix_print(0); return 0; } else if (strncmp(argv[i], "--squash-map=", 13) == 0) { @@ -755,7 +793,7 @@ int main(int argc, char **argv) { printf("# License GPLv2\n"); printf("#\n"); - radix_print(); + radix_print(0); printf("\n#\n#\n# input %d, output %d\n#\n#\n", in, radix_nodes); fprintf(stderr, "static ip map: input %d, output %d\n", in, radix_nodes); return 0; @@ -790,6 +828,12 @@ int main(int argc, char **argv) { return 1; } + terminal_set(); + // handle CTRL-C + signal (SIGINT, terminal_handler); + signal (SIGTERM, terminal_handler); + atexit(terminal_restore); + // kill the process if the parent died prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); diff --git a/src/fnettrace/radix.c b/src/fnettrace/radix.c index f0ac4c094..322ee2643 100644 --- a/src/fnettrace/radix.c +++ b/src/fnettrace/radix.c @@ -151,12 +151,22 @@ RNode *radix_longest_prefix_match(uint32_t ip) { } static uint32_t sum; -static void print(RNode *ptr, int level) { +static void print(RNode *ptr, int level, int pkts) { if (!ptr) return; if (ptr->name) { - printf("%d.%d.%d.%d/%d ", PRINT_IP(sum << (32 - level)), level); - printf("%s\n", ptr->name); + if (pkts) { + if (ptr->pkts) { + printf(" %d.%d.%d.%d/%d ", PRINT_IP(sum << (32 - level)), level); + printf("%s", ptr->name); + printf(" (%u)\n", ptr->pkts); + } + } + else { + printf("%d.%d.%d.%d/%d ", PRINT_IP(sum << (32 - level)), level); + printf("%s", ptr->name); + printf("\n"); + } } if (ptr->zero == NULL && ptr->one == NULL) @@ -164,22 +174,21 @@ static void print(RNode *ptr, int level) { level++; sum <<= 1; - print(ptr->zero, level); + print(ptr->zero, level, pkts); sum++; - print(ptr->one, level); + print(ptr->one, level, pkts); sum--; sum >>= 1; } -void radix_print(void) { +void radix_print(int pkts) { if (!head) return; - printf("\n"); sum = 0; - print(head->zero, 1); + print(head->zero, 1, pkts); assert(sum == 0); sum = 1; - print(head->one, 1); + print(head->one, 1, pkts); assert(sum == 1); } diff --git a/src/fnettrace/radix.h b/src/fnettrace/radix.h index 60a64f18f..358524723 100644 --- a/src/fnettrace/radix.h +++ b/src/fnettrace/radix.h @@ -30,7 +30,7 @@ typedef struct rnode_t { extern int radix_nodes; RNode *radix_longest_prefix_match(uint32_t ip); RNode*radix_add(uint32_t ip, uint32_t mask, char *name); -void radix_print(void); +void radix_print(int pkts); void radix_squash(void); #endif diff --git a/src/fnettrace/static-ip-map.txt b/src/fnettrace/static-ip-map.txt index 52eb307d8..756658562 100644 --- a/src/fnettrace/static-ip-map.txt +++ b/src/fnettrace/static-ip-map.txt @@ -202,6 +202,7 @@ 199.9.248.0/21 Twitch 199.16.156.0/22 Twitter 199.59.148.0/22 Twitter +199.168.96.24/29 BitChute 205.185.194.0/24 Steam 205.196.6.0/24 Steam 207.45.72.0/22 Netflix @@ -212,6 +213,19 @@ 208.80.152.0/22 Wikipedia 209.140.128.0/18 eBay +# Imperva +199.83.128.0/21 Imperva +198.143.32.0/19 Imperva +149.126.72.0/21 Imperva +103.28.248.0/22 Imperva +45.64.64.0/22 Imperva +185.11.124.0/22 Imperva +192.230.64.0/18 Imperva +107.154.0.0/16 Imperva +45.60.0.0/16 Imperva +45.223.0.0/16 Imperva +131.125.128.0/17 Imperva + # Level 3 66.114.192.0/18 Level 3 66.147.128.0/18 Level 3 diff --git a/src/fnettrace/terminal.c b/src/fnettrace/terminal.c new file mode 100644 index 000000000..0ca307bad --- /dev/null +++ b/src/fnettrace/terminal.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014-2023 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "fnettrace.h" +#include + +static struct termios tlocal; // startup terminal setting +static struct termios twait; // no wait on key press +static int tset = 0; + +void terminal_restore(void) { + if (tset) + tcsetattr(0, TCSANOW, &tlocal); +} + +void terminal_handler(int s) { + // Remove unused parameter warning + (void)s; + terminal_restore(); + _exit(0); +} + +void terminal_set(void) { + if (tset == 0) { + tcgetattr(0, &twait); // get current terminal attributes; 0 is the file descriptor for stdin + memcpy(&tlocal, &twait, sizeof(tlocal)); + twait.c_lflag &= ~ICANON; // disable canonical mode + twait.c_lflag &= ~ECHO; // no echo + twait.c_cc[VMIN] = 1; // wait until at least one keystroke available + twait.c_cc[VTIME] = 0; // no timeout + tset = 1; + } + tcsetattr(0, TCSANOW, &twait); +} + +