nettrace stats

This commit is contained in:
netblue30 2023-08-08 16:27:18 -04:00
parent bf8229dde7
commit c4962789fc
8 changed files with 330 additions and 117 deletions

View file

@ -66,7 +66,7 @@ void print_dns(uint32_t ip_src, unsigned char *pkt) {
// filter output
char tmp[sizeof(last)];
snprintf(tmp, sizeof(last), "%02d:%02d:%02d %-15s %s (type %u)%s",
snprintf(tmp, sizeof(last), "%02d:%02d:%02d %-15s DNS %s (type %u)%s",
t->tm_hour, t->tm_min, t->tm_sec, ip, pkt + 12 + 1,
type, (nxdomain)? " NXDOMAIN": "");
if (strcmp(tmp, last)) {

View file

@ -32,16 +32,15 @@ static char last[512] = {'\0'};
static void print_tls(uint32_t ip_dest, unsigned char *pkt, unsigned len) {
assert(pkt);
// expecting a handshake packet and client hello
if (pkt[0] != 0x16 || pkt[5] != 0x01)
return;
char ip[30];
sprintf(ip, "%d.%d.%d.%d", PRINT_IP(ip_dest));
time_t seconds = time(NULL);
struct tm *t = localtime(&seconds);
// expecting a handshake packet and client hello
if (pkt[0] != 0x16 || pkt[5] != 0x01)
goto errout;
// look for server name indication
unsigned char *ptr = pkt;
unsigned int i = 0;
@ -74,7 +73,7 @@ static void print_tls(uint32_t ip_dest, unsigned char *pkt, unsigned len) {
if (name) {
// filter output
char tmp[sizeof(last)];
snprintf(tmp, sizeof(last), "%02d:%02d:%02d %-15s %s", t->tm_hour, t->tm_min, t->tm_sec, ip, name);
snprintf(tmp, sizeof(last), "%02d:%02d:%02d %-15s SNI %s", t->tm_hour, t->tm_min, t->tm_sec, ip, name);
if (strcmp(tmp, last)) {
printf("%s\n", tmp);
fflush(0);
@ -85,11 +84,6 @@ static void print_tls(uint32_t ip_dest, unsigned char *pkt, unsigned len) {
goto nosni;
return;
errout:
printf("%02d:%02d:%02d %-15s Error: invalid TLS packet\n", t->tm_hour, t->tm_min, t->tm_sec, ip);
fflush(0);
return;
nosni:
printf("%02d:%02d:%02d %-15s no SNI\n", t->tm_hour, t->tm_min, t->tm_sec, ip);
return;

105
src/fnettrace/event.c Normal file
View file

@ -0,0 +1,105 @@
/*
* 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"
typedef struct event_t {
struct event_t *next;
char *record;
} Event;
static Event *event = NULL;
static Event *last_event = NULL;
int ev_cnt = 0;
void ev_clear(void) {
ev_cnt = 0;
Event *ev = event;
while (ev) {
Event *next = ev->next;
free(ev->record);
free(ev);
ev = next;
}
event = NULL;
}
void ev_add(char *record) {
assert(record);
// braking recursivity
if (*record == '\0')
return;
char *ptr = strchr(record, '\n');
if (ptr)
*ptr = '\0';
// filter out duplicates
if (event && strcmp(event->record, record) == 0)
return;
Event *ev = malloc(sizeof(Event));
if (!ev)
errExit("malloc");
memset(ev, 0, sizeof(Event));
ev->record = strdup(record);
if (!ev->record)
errExit("strdup");
if (event == NULL) {
event = ev;
last_event = ev;
}
else {
last_event->next = ev;
last_event = ev;
}
ev_cnt++;
// recursivity
if (ptr)
ev_add(++ptr);
}
void ev_print(FILE *fp) {
assert(fp);
Event *ev = event;
while (ev) {
fprintf(fp, " ");
if (strstr(ev->record, "NXDOMAIN")) {
if (fp == stdout)
ansi_red(ev->record);
else
fprintf(fp, "%s", ev->record);
}
else if (strstr(ev->record, "SSH connection")) {
if (fp == stdout)
ansi_red(ev->record);
else
fprintf(fp, "%s", ev->record);
}
else
fprintf(fp, "%s", ev->record);
fprintf(fp, "\n");
ev = ev->next;
}
}

View file

@ -53,6 +53,27 @@ static inline void ansi_clrscr(void) {
fflush(0);
}
static inline void ansi_bold(const char *str) {
char str1[] = {0x1b, '[', '1', 'm', '\0'};
char str2[] = {0x1b, '[', '0', 'm', '\0'};
printf("%s%s%s", str1, str, str2);
fflush(0);
}
static inline void ansi_faint(const char *str) {
char str1[] = {0x1b, '[', '2', 'm', '\0'};
char str2[] = {0x1b, '[', '0', 'm', '\0'};
printf("%s%s%s", str1, str, str2);
fflush(0);
}
static inline void ansi_red(const char *str) {
char str1[] = {0x1b, '[', '9', '1', 'm', '\0'};
char str2[] = {0x1b, '[', '0', 'm', '\0'};
printf("%s%s%s", str1, str, str2);
fflush(0);
}
static inline uint8_t hash(uint32_t ip) {
uint8_t *ptr = (uint8_t *) &ip;
// simple byte xor
@ -78,4 +99,11 @@ void terminal_restore(void);
// runprog.c
int runprog(const char *program);
// event.c
extern int ev_cnt;
void ev_clear(void);
void ev_add(char *record);
void ev_print(FILE *fp);
#endif

View file

@ -27,6 +27,12 @@
static char *arg_log = NULL;
// only 0 or negative values; postive values as defiend in RFCsq
#define PROTOCOL_ICMP 0
#define PROTOCOL_SSH -1
//*****************************************************************
// packet stats
//*****************************************************************
@ -42,41 +48,18 @@ uint32_t stats_tor = 0;
uint32_t stats_http = 0;
uint32_t stats_ssh = 0;
//*****************************************************************
// sni/dns log storage
//*****************************************************************
typedef struct lognode_t {
#define LOG_RECORD_LEN 255
char record[LOG_RECORD_LEN + 1];
} LogNode;
// circular list of SNI log records
#define SNIMAX 64
LogNode sni_table[SNIMAX] = {0};
int sni_index = 0;
// circular list of SNI log records
#define DNSMAX 64
LogNode dns_table[SNIMAX] = {0};
int dns_index = 0;
static void print_sni(void) {
int i;
for (i = sni_index; i < SNIMAX; i++)
if (*sni_table[i].record)
printf(" %s", sni_table[i].record);
for (i = 0; i < sni_index; i++)
if (*sni_table[i].record)
printf(" %s", sni_table[i].record);
}
static void print_dns(void) {
int i;
for (i = dns_index; i < DNSMAX; i++)
if (*dns_table[i].record)
printf(" %s", dns_table[i].record);
for (i = 0; i < dns_index; i++)
if (*dns_table[i].record)
printf(" %s", dns_table[i].record);
static void clear_stats(void) {
stats_pkts = 0;
stats_icmp_echo = 0;
stats_dns = 0;
stats_dns_dot = 0;
stats_dns_doh = 0;
stats_dns_doq = 0;
stats_tls = 0;
stats_quic = 0;
stats_tor = 0;
stats_http = 0;
stats_ssh = 0;
}
//*****************************************************************
@ -92,7 +75,7 @@ typedef struct hnode_t {
uint32_t bytes; // number of bytes received in the last display interval
uint32_t pkts; // number of packets received in the last display interval
uint16_t port_src;
uint8_t protocol;
int protocol;
// the firewall is build based on source address, and in the linked list
// we could have elements with the same address but different ports
@ -135,7 +118,7 @@ void hfree(HNode *ptr) {
}
// using protocol 0 and port 0 for ICMP
static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) {
static void hnode_add(uint32_t ip_src, int protocol, uint16_t port_src, uint32_t bytes) {
uint8_t h = hash(ip_src);
// find
@ -383,7 +366,9 @@ 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) network\n", stats);
char faint1[] = {0x1b, '[', '2', 'm', '\0'};
char faint2[] = {0x1b, '[', '0', 'm', '\0'};
int len = snprintf(line, LINE_MAX, "%32s %saddress:port (protocol) network%s\n", stats, faint1, faint2);
adjust_line(line, len, cols);
printf("%s", line);
@ -461,10 +446,14 @@ static void hnode_print(unsigned bw) {
protocol = "UDP";
else if (ptr->protocol == 0x06)
protocol = "TCP";
else if (ptr->protocol == PROTOCOL_SSH) {
protocol = "SSH";
stats_ssh += ptr->pkts;
}
if (protocol == NULL)
protocol = "";
if (ptr->port_src == 0)
if (ptr->port_src == PROTOCOL_ICMP)
len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d (ICMP) %s\n",
bytes, bwline, PRINT_IP(ptr->ip_src), ptr->rnode->name);
else
@ -490,7 +479,7 @@ static void hnode_print(unsigned bw) {
ptr = next;
}
printf("press any key to access stats\n");
ansi_faint("(D)isplay, (S)ave, (C)lear, e(X)it\n");
#ifdef DEBUG
{
@ -505,10 +494,34 @@ static void hnode_print(unsigned bw) {
#endif
}
static void print_stats(FILE *fp) {
assert(fp);
fprintf(fp, "Stats: %u packets\n", stats_pkts);
fprintf(fp, " encrypted: TLS %u, QUIC %u, Tor %u\n",
stats_tls, stats_quic, stats_tor);
fprintf(fp, " unencrypted: HTTP %u\n", stats_http);
fprintf(fp, " C&C backchannel: SSH %u, PING %u, DNS %u, DoH %u, DoT %u, DoQ %u\n",
stats_ssh, stats_icmp_echo, stats_dns, stats_dns_doh, stats_dns_dot, stats_dns_doq);
fprintf(fp, "\n\nIP map");
if (fp == stdout)
ansi_faint(" - server-address network (packets)\n");
else
fprintf(fp, " - server-address network (packets)\n");
radix_print(fp, 1);
fprintf(fp, "\n\nEvents %d", ev_cnt);
if (fp == stdout)
ansi_faint(" - time address:port data\n");
else
fprintf(fp, " - time address:port data\n");
ev_print(fp);
void print_stats(void) {
}
// trace rx traffic coming in
static void run_trace(void) {
// trace only rx ipv4 tcp and upd
@ -523,7 +536,7 @@ static void run_trace(void) {
if (p1 != -1)
printf("loading snitrace...");
int p2 = runprog(LIBDIR "/firejail/fnettrace-dns --nolocal");
int p2 = runprog(LIBDIR "/firejail/fnettrace-dns");
if (p2 != -1)
printf("loading dnstrace...");
unsigned last_print_traces = 0;
@ -575,40 +588,67 @@ static void run_trace(void) {
int icmp = 0;
if (FD_ISSET(0, &rfds)) {
getchar();
printf("\n\nStats: %u packets\n", stats_pkts);
printf(" encrypted: TLS %u, QUIC %u, SSH %u, Tor %u\n",
stats_tls, stats_quic, stats_ssh, stats_tor);
printf(" unencrypted: HTTP %u\n", stats_http);
printf(" C&C backchannel: PING %u, DNS %u, DoH %u, DoT %u, DoQ %u\n",
stats_icmp_echo, stats_dns, stats_dns_doh, stats_dns_dot, stats_dns_doq);
printf("press any key to continue...");
fflush(0);
int c = getchar();
if (c == 'c' || c == 'C') {
clear_stats();
ev_clear();
radix_clear_data();
continue;
}
else if (c == 'd' || c == 'D') {
printf("\n\n");
ansi_bold("__________________________________________________________________________\n");
print_stats(stdout);
ansi_bold("__________________________________________________________________________\n");
ansi_faint("press any key to continue...");
fflush(0);
getchar();
printf("\n\nSNI log - time server-address SNI\n");
print_sni();
printf("press any key to continue...");
fflush(0);
getchar();
continue;
}
if (c == 's' || c == 'S') {
printf("The file is saved in /tmp directory. Please enter the file name: ");
fflush(0);
getchar();
printf("\n\nDNS log - time server-address domain\n");
print_dns();
printf("press any key to continue...");
fflush(0);
char buf[LINE_MAX + 5]; // eave some room to add /tmp/
strcpy(buf, "/tmp/");
terminal_restore();
if (fgets(buf + 5, LINE_MAX, stdin) == NULL)
errExit("fgets");
terminal_set();
getchar();
printf("\n\nIP table: %d addresses - server-address network (packets)\n", radix_nodes);
radix_print(1);
printf("press any key to continue...");
fflush(0);
// remove '\n' and open the file
char *ptr = strchr(buf, '\n');
if (!ptr) { // we should have a '\n'
printf("Error: invalid file name\n");
sleep(5);
continue;
}
*ptr = '\0';
getchar();
FILE *fp = fopen(buf, "w");
if (!fp) {
printf("Error: cannot open file %s\n", buf);
perror("fopen");
sleep(5);
continue;
}
printf("Saving stats in %s file...\n", buf);
print_stats(fp);
fclose(fp);
int rv = chmod(buf, 0600);
(void) rv;
sleep(1);
continue;
}
else if (c == 'x' || c == 'X')
break;
continue;
}
else if (FD_ISSET(p1, &rfds)) {
char buf[1024];
ssize_t sz = read(p1, buf, 1024 - 1);
char buf[LINE_MAX];
ssize_t sz = read(p1, buf, LINE_MAX - 1);
if (sz == -1)
errExit("error reading snitrace");
if (sz == 0) {
@ -618,19 +658,13 @@ static void run_trace(void) {
if (strncmp(buf, "SNI trace", 9) == 0)
continue;
if (sz > LOG_RECORD_LEN)
sz = LOG_RECORD_LEN;
buf[sz] = '\0';
strcpy(sni_table[sni_index].record, buf);
if (++sni_index >= SNIMAX) {
sni_index = 0;
*sni_table[sni_index].record = '\0';
}
ev_add(buf);
continue;
}
else if (FD_ISSET(p2, &rfds)) {
char buf[1024];
ssize_t sz = read(p2, buf, 1024 - 1);
char buf[LINE_MAX];
ssize_t sz = read(p2, buf, LINE_MAX - 1);
if (sz == -1)
errExit("error reading dnstrace");
if (sz == 0) {
@ -640,16 +674,11 @@ static void run_trace(void) {
if (strncmp(buf, "DNS trace", 9) == 0)
continue;
if (sz > LOG_RECORD_LEN)
sz = LOG_RECORD_LEN;
buf[sz] = '\0';
strcpy(dns_table[dns_index].record, buf);
if (++dns_index >= DNSMAX) {
dns_index = 0;
*dns_table[dns_index].record = '\0';
}
ev_add(buf);
continue;
}
// by default we assume TCP
else if (FD_ISSET(s2, &rfds))
sock = s2;
else if (FD_ISSET(s3, &rfds)) {
@ -658,7 +687,7 @@ static void run_trace(void) {
}
unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
if (bytes >= 20) { // size of IP header
if (bytes >= 20) { // minimum size of IP packet
#ifdef DEBUG
{
uint32_t ip_src;
@ -682,12 +711,30 @@ static void run_trace(void) {
uint8_t hlen = (buf[0] & 0x0f) * 4;
uint16_t port_src = 0;
if (icmp)
hnode_add(ip_src, 0, 0, bytes + 14);
else {
hnode_add(ip_src, PROTOCOL_ICMP, 0, bytes + 14);
else { // itcp or udp
memcpy(&port_src, buf + hlen, 2);
port_src = ntohs(port_src);
int protocol = (int) buf[9];
uint8_t protocol = buf[9];
// detect ssh on a standard or not so standard port (22)
if (protocol == 6) { // tcp
uint8_t dataoffset = *(buf + hlen + 12);
uint8_t tcphlen = (dataoffset >> 2);
if (memcmp(buf + hlen + tcphlen, "SSH-", 4) == 0) {
time_t seconds = time(NULL);
struct tm *t = localtime(&seconds);
char ip[30];
sprintf(ip, "%d.%d.%d.%d", PRINT_IP(ip_src));
char *msg;
if (asprintf(&msg, "%02d:%02d:%02d %-15s SSH connection",
t->tm_hour, t->tm_min, t->tm_sec, ip) == -1)
errExit("asprintf");
ev_add(msg);
free(msg);
protocol = PROTOCOL_SSH;
}
}
hnode_add(ip_src, protocol, port_src, bytes + 14);
}
@ -705,7 +752,10 @@ static void run_trace(void) {
close(s1);
close(s2);
close(s3);
print_stats();
if (p1 != -1)
close(p1);
if (p2 != -1)
close(p2);
}
@ -765,7 +815,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(0);
radix_print(stdout, 0);
return 0;
}
else if (strncmp(argv[i], "--squash-map=", 13) == 0) {
@ -787,7 +837,7 @@ int main(int argc, char **argv) {
printf("# License GPLv2\n");
printf("#\n");
radix_print(0);
radix_print(stdout, 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;

View file

@ -151,21 +151,22 @@ RNode *radix_longest_prefix_match(uint32_t ip) {
}
static uint32_t sum;
static void print(RNode *ptr, int level, int pkts) {
static void print(FILE *fp, RNode *ptr, int level, int pkts) {
assert(fp);
if (!ptr)
return;
if (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);
fprintf(fp, " %d.%d.%d.%d/%d ", PRINT_IP(sum << (32 - level)), level);
fprintf(fp, "%s", ptr->name);
fprintf(fp, " (%u)\n", ptr->pkts);
}
}
else {
printf("%d.%d.%d.%d/%d ", PRINT_IP(sum << (32 - level)), level);
printf("%s", ptr->name);
printf("\n");
fprintf(fp, "%d.%d.%d.%d/%d ", PRINT_IP(sum << (32 - level)), level);
fprintf(fp, "%s", ptr->name);
fprintf(fp, "\n");
}
}
@ -174,21 +175,21 @@ static void print(RNode *ptr, int level, int pkts) {
level++;
sum <<= 1;
print(ptr->zero, level, pkts);
print(fp, ptr->zero, level, pkts);
sum++;
print(ptr->one, level, pkts);
print(fp, ptr->one, level, pkts);
sum--;
sum >>= 1;
}
void radix_print(int pkts) {
void radix_print(FILE *fp, int pkts) {
if (!head)
return;
sum = 0;
print(head->zero, 1, pkts);
print(fp, head->zero, 1, pkts);
assert(sum == 0);
sum = 1;
print(head->one, 1, pkts);
print(fp, head->one, 1, pkts);
assert(sum == 1);
}
@ -241,3 +242,18 @@ void radix_squash(void) {
assert(sum == 1);
}
static void clear_data(RNode *ptr) {
if (!ptr)
return;
ptr->pkts = 0;
clear_data(ptr->zero);
clear_data(ptr->one);
}
void radix_clear_data(void) {
if (!head)
return;
clear_data(head->zero);
clear_data(head->one);
}

View file

@ -30,7 +30,8 @@ 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(int pkts);
void radix_print(FILE *fp, int pkts);
void radix_squash(void);
void radix_clear_data(void);
#endif

View file

@ -153,6 +153,7 @@
45.113.128.0/22 Twitch
47.88.0.0/14 Alibaba
52.223.192.0/18 Twitch
63.141.247.168/29 BitChute
63.245.208.0/23 Mozilla
64.63.0.0/18 Twitter
64.112.13.0/24 Dropbox
@ -168,6 +169,7 @@
66.218.64.0/19 Yahoo
66.220.144.0/20 Facebook
69.30.200.200/29 BitChute
69.30.241.48/29 BitChute
69.53.224.0/19 Netflix
69.171.224.0/19 Facebook
69.197.182.184/29 BitChute
@ -222,7 +224,10 @@
173.208.154.8/29 BitChute
173.208.154.160/29 BitChute
173.208.185.200/29 BitChute
173.208.203.248/29 BitChute
173.208.216.40/29 BitChute
173.208.219.112/29 BitChute
173.208.246.160/29 BitChute
178.154.131.0/24 Yandex
185.2.220.0/22 Netflix
185.9.188.0/22 Netflix
@ -246,6 +251,7 @@
192.151.158.136/29 BitChute
192.173.64.0/18 Netflix
192.187.97.88/29 BitChute
192.187.114.16/29 BitChute
192.187.114.96/29 BitChute
192.187.123.112/29 BitChute
192.187.126.0/29 BitChute
@ -254,13 +260,17 @@
198.38.96.0/19 Netflix
198.45.48.0/20 Netflix
198.204.226.120/29 BitChute
198.204.228.48/29 BitChute
198.204.245.32/29 BitChute
198.204.245.88/29 BitChute
198.204.250.208/29 BitChute
198.252.206.0/24 Stack Exchange
199.9.248.0/21 Twitch
199.16.156.0/22 Twitter
199.59.148.0/22 Twitter
199.168.96.24/29 BitChute
204.12.194.176/29 BitChute
204.12.220.232/29 BitChute
205.185.194.0/24 Steam
205.196.6.0/24 Steam
207.45.72.0/22 Netflix
@ -273,6 +283,15 @@
208.110.68.56/29 BitChute
209.140.128.0/18 eBay
# WholeSale Internet
69.30.192.0/18 WholeSale Internet
69.197.128.0/18 WholeSale Internet
173.208.128.0/17 WholeSale Internet
204.12.192.0/18 WholeSale Internet
208.67.0.0/21 WholeSale Internet
208.110.64.0/19 WholeSale Internet
208.110.91.0/24 WholeSale Internet
# Imperva
199.83.128.0/21 Imperva
198.143.32.0/19 Imperva