diff options
author | 2023-08-08 16:27:18 -0400 | |
---|---|---|
committer | 2023-08-08 16:27:18 -0400 | |
commit | c4962789fc88e349a0c6a6c51af4a7cfc09fcc99 (patch) | |
tree | 83e69a67a17f9962846175e3f5cd0856d5e50de0 /src/fnettrace/main.c | |
parent | Merge branch 'master' of ssh://github.com/netblue30/firejail (diff) | |
download | firejail-c4962789fc88e349a0c6a6c51af4a7cfc09fcc99.tar.gz firejail-c4962789fc88e349a0c6a6c51af4a7cfc09fcc99.tar.zst firejail-c4962789fc88e349a0c6a6c51af4a7cfc09fcc99.zip |
nettrace stats
Diffstat (limited to 'src/fnettrace/main.c')
-rw-r--r-- | src/fnettrace/main.c | 244 |
1 files changed, 147 insertions, 97 deletions
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c index 3bafd9090..a1e46f307 100644 --- a/src/fnettrace/main.c +++ b/src/fnettrace/main.c | |||
@@ -27,6 +27,12 @@ | |||
27 | 27 | ||
28 | static char *arg_log = NULL; | 28 | static char *arg_log = NULL; |
29 | 29 | ||
30 | // only 0 or negative values; postive values as defiend in RFCsq | ||
31 | #define PROTOCOL_ICMP 0 | ||
32 | #define PROTOCOL_SSH -1 | ||
33 | |||
34 | |||
35 | |||
30 | //***************************************************************** | 36 | //***************************************************************** |
31 | // packet stats | 37 | // packet stats |
32 | //***************************************************************** | 38 | //***************************************************************** |
@@ -42,41 +48,18 @@ uint32_t stats_tor = 0; | |||
42 | uint32_t stats_http = 0; | 48 | uint32_t stats_http = 0; |
43 | uint32_t stats_ssh = 0; | 49 | uint32_t stats_ssh = 0; |
44 | 50 | ||
45 | //***************************************************************** | 51 | static void clear_stats(void) { |
46 | // sni/dns log storage | 52 | stats_pkts = 0; |
47 | //***************************************************************** | 53 | stats_icmp_echo = 0; |
48 | typedef struct lognode_t { | 54 | stats_dns = 0; |
49 | #define LOG_RECORD_LEN 255 | 55 | stats_dns_dot = 0; |
50 | char record[LOG_RECORD_LEN + 1]; | 56 | stats_dns_doh = 0; |
51 | } LogNode; | 57 | stats_dns_doq = 0; |
52 | // circular list of SNI log records | 58 | stats_tls = 0; |
53 | #define SNIMAX 64 | 59 | stats_quic = 0; |
54 | LogNode sni_table[SNIMAX] = {0}; | 60 | stats_tor = 0; |
55 | int sni_index = 0; | 61 | stats_http = 0; |
56 | 62 | stats_ssh = 0; | |
57 | // circular list of SNI log records | ||
58 | #define DNSMAX 64 | ||
59 | LogNode dns_table[SNIMAX] = {0}; | ||
60 | int dns_index = 0; | ||
61 | |||
62 | static void print_sni(void) { | ||
63 | int i; | ||
64 | for (i = sni_index; i < SNIMAX; i++) | ||
65 | if (*sni_table[i].record) | ||
66 | printf(" %s", sni_table[i].record); | ||
67 | for (i = 0; i < sni_index; i++) | ||
68 | if (*sni_table[i].record) | ||
69 | printf(" %s", sni_table[i].record); | ||
70 | } | ||
71 | |||
72 | static void print_dns(void) { | ||
73 | int i; | ||
74 | for (i = dns_index; i < DNSMAX; i++) | ||
75 | if (*dns_table[i].record) | ||
76 | printf(" %s", dns_table[i].record); | ||
77 | for (i = 0; i < dns_index; i++) | ||
78 | if (*dns_table[i].record) | ||
79 | printf(" %s", dns_table[i].record); | ||
80 | } | 63 | } |
81 | 64 | ||
82 | //***************************************************************** | 65 | //***************************************************************** |
@@ -92,7 +75,7 @@ typedef struct hnode_t { | |||
92 | uint32_t bytes; // number of bytes received in the last display interval | 75 | uint32_t bytes; // number of bytes received in the last display interval |
93 | uint32_t pkts; // number of packets received in the last display interval | 76 | uint32_t pkts; // number of packets received in the last display interval |
94 | uint16_t port_src; | 77 | uint16_t port_src; |
95 | uint8_t protocol; | 78 | int protocol; |
96 | 79 | ||
97 | // the firewall is build based on source address, and in the linked list | 80 | // the firewall is build based on source address, and in the linked list |
98 | // we could have elements with the same address but different ports | 81 | // we could have elements with the same address but different ports |
@@ -135,7 +118,7 @@ void hfree(HNode *ptr) { | |||
135 | } | 118 | } |
136 | 119 | ||
137 | // using protocol 0 and port 0 for ICMP | 120 | // using protocol 0 and port 0 for ICMP |
138 | static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) { | 121 | static void hnode_add(uint32_t ip_src, int protocol, uint16_t port_src, uint32_t bytes) { |
139 | uint8_t h = hash(ip_src); | 122 | uint8_t h = hash(ip_src); |
140 | 123 | ||
141 | // find | 124 | // find |
@@ -383,7 +366,9 @@ static void hnode_print(unsigned bw) { | |||
383 | else | 366 | else |
384 | sprintf(stats, "%u KB/s ", bw / (1024 * DISPLAY_INTERVAL)); | 367 | sprintf(stats, "%u KB/s ", bw / (1024 * DISPLAY_INTERVAL)); |
385 | // int len = snprintf(line, LINE_MAX, "%32s geoip %d, IP database %d\n", stats, geoip_calls, radix_nodes); | 368 | // int len = snprintf(line, LINE_MAX, "%32s geoip %d, IP database %d\n", stats, geoip_calls, radix_nodes); |
386 | int len = snprintf(line, LINE_MAX, "%32s address:port (protocol) network\n", stats); | 369 | char faint1[] = {0x1b, '[', '2', 'm', '\0'}; |
370 | char faint2[] = {0x1b, '[', '0', 'm', '\0'}; | ||
371 | int len = snprintf(line, LINE_MAX, "%32s %saddress:port (protocol) network%s\n", stats, faint1, faint2); | ||
387 | adjust_line(line, len, cols); | 372 | adjust_line(line, len, cols); |
388 | printf("%s", line); | 373 | printf("%s", line); |
389 | 374 | ||
@@ -461,10 +446,14 @@ static void hnode_print(unsigned bw) { | |||
461 | protocol = "UDP"; | 446 | protocol = "UDP"; |
462 | else if (ptr->protocol == 0x06) | 447 | else if (ptr->protocol == 0x06) |
463 | protocol = "TCP"; | 448 | protocol = "TCP"; |
449 | else if (ptr->protocol == PROTOCOL_SSH) { | ||
450 | protocol = "SSH"; | ||
451 | stats_ssh += ptr->pkts; | ||
452 | } | ||
464 | 453 | ||
465 | if (protocol == NULL) | 454 | if (protocol == NULL) |
466 | protocol = ""; | 455 | protocol = ""; |
467 | if (ptr->port_src == 0) | 456 | if (ptr->port_src == PROTOCOL_ICMP) |
468 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d (ICMP) %s\n", | 457 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d (ICMP) %s\n", |
469 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->rnode->name); | 458 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->rnode->name); |
470 | else | 459 | else |
@@ -490,7 +479,7 @@ static void hnode_print(unsigned bw) { | |||
490 | 479 | ||
491 | ptr = next; | 480 | ptr = next; |
492 | } | 481 | } |
493 | printf("press any key to access stats\n"); | 482 | ansi_faint("(D)isplay, (S)ave, (C)lear, e(X)it\n"); |
494 | 483 | ||
495 | #ifdef DEBUG | 484 | #ifdef DEBUG |
496 | { | 485 | { |
@@ -505,10 +494,34 @@ static void hnode_print(unsigned bw) { | |||
505 | #endif | 494 | #endif |
506 | } | 495 | } |
507 | 496 | ||
497 | static void print_stats(FILE *fp) { | ||
498 | assert(fp); | ||
499 | |||
500 | fprintf(fp, "Stats: %u packets\n", stats_pkts); | ||
501 | fprintf(fp, " encrypted: TLS %u, QUIC %u, Tor %u\n", | ||
502 | stats_tls, stats_quic, stats_tor); | ||
503 | fprintf(fp, " unencrypted: HTTP %u\n", stats_http); | ||
504 | fprintf(fp, " C&C backchannel: SSH %u, PING %u, DNS %u, DoH %u, DoT %u, DoQ %u\n", | ||
505 | stats_ssh, stats_icmp_echo, stats_dns, stats_dns_doh, stats_dns_dot, stats_dns_doq); | ||
506 | |||
507 | fprintf(fp, "\n\nIP map"); | ||
508 | if (fp == stdout) | ||
509 | ansi_faint(" - server-address network (packets)\n"); | ||
510 | else | ||
511 | fprintf(fp, " - server-address network (packets)\n"); | ||
512 | radix_print(fp, 1); | ||
513 | |||
514 | fprintf(fp, "\n\nEvents %d", ev_cnt); | ||
515 | if (fp == stdout) | ||
516 | ansi_faint(" - time address:port data\n"); | ||
517 | else | ||
518 | fprintf(fp, " - time address:port data\n"); | ||
519 | ev_print(fp); | ||
508 | 520 | ||
509 | void print_stats(void) { | ||
510 | } | 521 | } |
511 | 522 | ||
523 | |||
524 | |||
512 | // trace rx traffic coming in | 525 | // trace rx traffic coming in |
513 | static void run_trace(void) { | 526 | static void run_trace(void) { |
514 | // trace only rx ipv4 tcp and upd | 527 | // trace only rx ipv4 tcp and upd |
@@ -523,7 +536,7 @@ static void run_trace(void) { | |||
523 | if (p1 != -1) | 536 | if (p1 != -1) |
524 | printf("loading snitrace..."); | 537 | printf("loading snitrace..."); |
525 | 538 | ||
526 | int p2 = runprog(LIBDIR "/firejail/fnettrace-dns --nolocal"); | 539 | int p2 = runprog(LIBDIR "/firejail/fnettrace-dns"); |
527 | if (p2 != -1) | 540 | if (p2 != -1) |
528 | printf("loading dnstrace..."); | 541 | printf("loading dnstrace..."); |
529 | unsigned last_print_traces = 0; | 542 | unsigned last_print_traces = 0; |
@@ -575,40 +588,67 @@ static void run_trace(void) { | |||
575 | int icmp = 0; | 588 | int icmp = 0; |
576 | 589 | ||
577 | if (FD_ISSET(0, &rfds)) { | 590 | if (FD_ISSET(0, &rfds)) { |
578 | getchar(); | 591 | int c = getchar(); |
579 | printf("\n\nStats: %u packets\n", stats_pkts); | 592 | if (c == 'c' || c == 'C') { |
580 | printf(" encrypted: TLS %u, QUIC %u, SSH %u, Tor %u\n", | 593 | clear_stats(); |
581 | stats_tls, stats_quic, stats_ssh, stats_tor); | 594 | ev_clear(); |
582 | printf(" unencrypted: HTTP %u\n", stats_http); | 595 | radix_clear_data(); |
583 | printf(" C&C backchannel: PING %u, DNS %u, DoH %u, DoT %u, DoQ %u\n", | 596 | continue; |
584 | stats_icmp_echo, stats_dns, stats_dns_doh, stats_dns_dot, stats_dns_doq); | 597 | } |
585 | printf("press any key to continue..."); | 598 | else if (c == 'd' || c == 'D') { |
586 | fflush(0); | 599 | printf("\n\n"); |
587 | 600 | ansi_bold("__________________________________________________________________________\n"); | |
588 | getchar(); | 601 | print_stats(stdout); |
589 | printf("\n\nSNI log - time server-address SNI\n"); | 602 | ansi_bold("__________________________________________________________________________\n"); |
590 | print_sni(); | 603 | ansi_faint("press any key to continue..."); |
591 | printf("press any key to continue..."); | 604 | fflush(0); |
592 | fflush(0); | 605 | |
593 | 606 | getchar(); | |
594 | getchar(); | 607 | continue; |
595 | printf("\n\nDNS log - time server-address domain\n"); | 608 | } |
596 | print_dns(); | 609 | if (c == 's' || c == 'S') { |
597 | printf("press any key to continue..."); | 610 | printf("The file is saved in /tmp directory. Please enter the file name: "); |
598 | fflush(0); | 611 | fflush(0); |
599 | 612 | ||
600 | getchar(); | 613 | char buf[LINE_MAX + 5]; // eave some room to add /tmp/ |
601 | printf("\n\nIP table: %d addresses - server-address network (packets)\n", radix_nodes); | 614 | strcpy(buf, "/tmp/"); |
602 | radix_print(1); | 615 | terminal_restore(); |
603 | printf("press any key to continue..."); | 616 | if (fgets(buf + 5, LINE_MAX, stdin) == NULL) |
604 | fflush(0); | 617 | errExit("fgets"); |
605 | 618 | terminal_set(); | |
606 | getchar(); | 619 | |
620 | // remove '\n' and open the file | ||
621 | char *ptr = strchr(buf, '\n'); | ||
622 | if (!ptr) { // we should have a '\n' | ||
623 | printf("Error: invalid file name\n"); | ||
624 | sleep(5); | ||
625 | continue; | ||
626 | } | ||
627 | *ptr = '\0'; | ||
628 | |||
629 | FILE *fp = fopen(buf, "w"); | ||
630 | if (!fp) { | ||
631 | printf("Error: cannot open file %s\n", buf); | ||
632 | perror("fopen"); | ||
633 | sleep(5); | ||
634 | continue; | ||
635 | } | ||
636 | |||
637 | printf("Saving stats in %s file...\n", buf); | ||
638 | print_stats(fp); | ||
639 | fclose(fp); | ||
640 | int rv = chmod(buf, 0600); | ||
641 | (void) rv; | ||
642 | sleep(1); | ||
643 | continue; | ||
644 | } | ||
645 | else if (c == 'x' || c == 'X') | ||
646 | break; | ||
607 | continue; | 647 | continue; |
608 | } | 648 | } |
609 | else if (FD_ISSET(p1, &rfds)) { | 649 | else if (FD_ISSET(p1, &rfds)) { |
610 | char buf[1024]; | 650 | char buf[LINE_MAX]; |
611 | ssize_t sz = read(p1, buf, 1024 - 1); | 651 | ssize_t sz = read(p1, buf, LINE_MAX - 1); |
612 | if (sz == -1) | 652 | if (sz == -1) |
613 | errExit("error reading snitrace"); | 653 | errExit("error reading snitrace"); |
614 | if (sz == 0) { | 654 | if (sz == 0) { |
@@ -618,19 +658,13 @@ static void run_trace(void) { | |||
618 | if (strncmp(buf, "SNI trace", 9) == 0) | 658 | if (strncmp(buf, "SNI trace", 9) == 0) |
619 | continue; | 659 | continue; |
620 | 660 | ||
621 | if (sz > LOG_RECORD_LEN) | ||
622 | sz = LOG_RECORD_LEN; | ||
623 | buf[sz] = '\0'; | 661 | buf[sz] = '\0'; |
624 | strcpy(sni_table[sni_index].record, buf); | 662 | ev_add(buf); |
625 | if (++sni_index >= SNIMAX) { | ||
626 | sni_index = 0; | ||
627 | *sni_table[sni_index].record = '\0'; | ||
628 | } | ||
629 | continue; | 663 | continue; |
630 | } | 664 | } |
631 | else if (FD_ISSET(p2, &rfds)) { | 665 | else if (FD_ISSET(p2, &rfds)) { |
632 | char buf[1024]; | 666 | char buf[LINE_MAX]; |
633 | ssize_t sz = read(p2, buf, 1024 - 1); | 667 | ssize_t sz = read(p2, buf, LINE_MAX - 1); |
634 | if (sz == -1) | 668 | if (sz == -1) |
635 | errExit("error reading dnstrace"); | 669 | errExit("error reading dnstrace"); |
636 | if (sz == 0) { | 670 | if (sz == 0) { |
@@ -640,16 +674,11 @@ static void run_trace(void) { | |||
640 | if (strncmp(buf, "DNS trace", 9) == 0) | 674 | if (strncmp(buf, "DNS trace", 9) == 0) |
641 | continue; | 675 | continue; |
642 | 676 | ||
643 | if (sz > LOG_RECORD_LEN) | ||
644 | sz = LOG_RECORD_LEN; | ||
645 | buf[sz] = '\0'; | 677 | buf[sz] = '\0'; |
646 | strcpy(dns_table[dns_index].record, buf); | 678 | ev_add(buf); |
647 | if (++dns_index >= DNSMAX) { | ||
648 | dns_index = 0; | ||
649 | *dns_table[dns_index].record = '\0'; | ||
650 | } | ||
651 | continue; | 679 | continue; |
652 | } | 680 | } |
681 | // by default we assume TCP | ||
653 | else if (FD_ISSET(s2, &rfds)) | 682 | else if (FD_ISSET(s2, &rfds)) |
654 | sock = s2; | 683 | sock = s2; |
655 | else if (FD_ISSET(s3, &rfds)) { | 684 | else if (FD_ISSET(s3, &rfds)) { |
@@ -658,7 +687,7 @@ static void run_trace(void) { | |||
658 | } | 687 | } |
659 | 688 | ||
660 | unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL); | 689 | unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL); |
661 | if (bytes >= 20) { // size of IP header | 690 | if (bytes >= 20) { // minimum size of IP packet |
662 | #ifdef DEBUG | 691 | #ifdef DEBUG |
663 | { | 692 | { |
664 | uint32_t ip_src; | 693 | uint32_t ip_src; |
@@ -682,12 +711,30 @@ static void run_trace(void) { | |||
682 | uint8_t hlen = (buf[0] & 0x0f) * 4; | 711 | uint8_t hlen = (buf[0] & 0x0f) * 4; |
683 | uint16_t port_src = 0; | 712 | uint16_t port_src = 0; |
684 | if (icmp) | 713 | if (icmp) |
685 | hnode_add(ip_src, 0, 0, bytes + 14); | 714 | hnode_add(ip_src, PROTOCOL_ICMP, 0, bytes + 14); |
686 | else { | 715 | else { // itcp or udp |
687 | memcpy(&port_src, buf + hlen, 2); | 716 | memcpy(&port_src, buf + hlen, 2); |
688 | port_src = ntohs(port_src); | 717 | port_src = ntohs(port_src); |
689 | 718 | int protocol = (int) buf[9]; | |
690 | uint8_t protocol = buf[9]; | 719 | |
720 | // detect ssh on a standard or not so standard port (22) | ||
721 | if (protocol == 6) { // tcp | ||
722 | uint8_t dataoffset = *(buf + hlen + 12); | ||
723 | uint8_t tcphlen = (dataoffset >> 2); | ||
724 | if (memcmp(buf + hlen + tcphlen, "SSH-", 4) == 0) { | ||
725 | time_t seconds = time(NULL); | ||
726 | struct tm *t = localtime(&seconds); | ||
727 | char ip[30]; | ||
728 | sprintf(ip, "%d.%d.%d.%d", PRINT_IP(ip_src)); | ||
729 | char *msg; | ||
730 | if (asprintf(&msg, "%02d:%02d:%02d %-15s SSH connection", | ||
731 | t->tm_hour, t->tm_min, t->tm_sec, ip) == -1) | ||
732 | errExit("asprintf"); | ||
733 | ev_add(msg); | ||
734 | free(msg); | ||
735 | protocol = PROTOCOL_SSH; | ||
736 | } | ||
737 | } | ||
691 | hnode_add(ip_src, protocol, port_src, bytes + 14); | 738 | hnode_add(ip_src, protocol, port_src, bytes + 14); |
692 | } | 739 | } |
693 | 740 | ||
@@ -705,7 +752,10 @@ static void run_trace(void) { | |||
705 | close(s1); | 752 | close(s1); |
706 | close(s2); | 753 | close(s2); |
707 | close(s3); | 754 | close(s3); |
708 | print_stats(); | 755 | if (p1 != -1) |
756 | close(p1); | ||
757 | if (p2 != -1) | ||
758 | close(p2); | ||
709 | } | 759 | } |
710 | 760 | ||
711 | 761 | ||
@@ -765,7 +815,7 @@ int main(int argc, char **argv) { | |||
765 | else if (strcmp(argv[i], "--print-map") == 0) { | 815 | else if (strcmp(argv[i], "--print-map") == 0) { |
766 | char *fname = "static-ip-map.txt"; | 816 | char *fname = "static-ip-map.txt"; |
767 | load_hostnames(fname); | 817 | load_hostnames(fname); |
768 | radix_print(0); | 818 | radix_print(stdout, 0); |
769 | return 0; | 819 | return 0; |
770 | } | 820 | } |
771 | else if (strncmp(argv[i], "--squash-map=", 13) == 0) { | 821 | else if (strncmp(argv[i], "--squash-map=", 13) == 0) { |
@@ -787,7 +837,7 @@ int main(int argc, char **argv) { | |||
787 | printf("# License GPLv2\n"); | 837 | printf("# License GPLv2\n"); |
788 | printf("#\n"); | 838 | printf("#\n"); |
789 | 839 | ||
790 | radix_print(0); | 840 | radix_print(stdout, 0); |
791 | printf("\n#\n#\n# input %d, output %d\n#\n#\n", in, radix_nodes); | 841 | printf("\n#\n#\n# input %d, output %d\n#\n#\n", in, radix_nodes); |
792 | fprintf(stderr, "static ip map: input %d, output %d\n", in, radix_nodes); | 842 | fprintf(stderr, "static ip map: input %d, output %d\n", in, radix_nodes); |
793 | return 0; | 843 | return 0; |