Skip to content

Commit

Permalink
nettrace fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
netblue30 committed Jan 18, 2022
1 parent 604459a commit 4dd1e92
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 43 deletions.
34 changes: 28 additions & 6 deletions src/firejail/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,22 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
}
#endif
#ifdef HAVE_NETWORK
else if (strcmp(argv[i], "--nettrace") == 0) {
if (checkcfg(CFG_NETWORK)) {
netfilter_trace(0);
}
else
exit_err_feature("networking");
exit(0);
}
else if (strncmp(argv[i], "--nettrace=", 11) == 0) {
pid_t pid = require_pid(argv[i] + 11);
netfilter_trace(pid);
if (checkcfg(CFG_NETWORK)) {
pid_t pid = require_pid(argv[i] + 11);
netfilter_trace(pid);
}
else
exit_err_feature("networking");
exit(0);
}
else if (strncmp(argv[i], "--bandwidth=", 12) == 0) {
if (checkcfg(CFG_NETWORK)) {
Expand Down Expand Up @@ -2316,11 +2329,20 @@ int main(int argc, char **argv, char **envp) {
continue;
}
#ifdef HAVE_NETWORK
else if (strcmp(argv[i], "--netlock") == 0)
arg_netlock = 1;
else if (strcmp(argv[i], "--netlock") == 0) {
if (checkcfg(CFG_NETWORK))
arg_netlock = 1;
else
exit_err_feature("networking");
}
else if (strncmp(argv[i], "--netlock=", 10) == 0) {
pid_t pid = require_pid(argv[i] + 10);
netfilter_netlock(pid);
if (checkcfg(CFG_NETWORK)) {
pid_t pid = require_pid(argv[i] + 10);
netfilter_netlock(pid);
}
else
exit_err_feature("networking");
exit(0);
}
else if (strncmp(argv[i], "--interface=", 12) == 0) {
if (checkcfg(CFG_NETWORK)) {
Expand Down
5 changes: 4 additions & 1 deletion src/firejail/netfilter.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ void netfilter_netlock(pid_t pid) {
void netfilter_trace(pid_t pid) {
EUID_ASSERT();

enter_network_namespace(pid);
// a pid of 0 means the main system network namespace
if (pid)
enter_network_namespace(pid);

char *cmd;
if (asprintf(&cmd, "%s/firejail/fnettrace", LIBDIR) == -1)
errExit("asprintf");
Expand Down
9 changes: 4 additions & 5 deletions src/fnettrace/fnettrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@
#include <stdarg.h>
//#define DEBUG 1

//#define NETLOCK_INTERVAL 15
#define NETLOCK_INTERVAL 60
#define DISPLAY_INTERVAL 2
#define DISPLAY_TTL 4
#define DISPLAY_BW_UNITS 20
#define NETLOCK_INTERVAL 60 // seconds
#define DISPLAY_INTERVAL 2 // seconds
#define DISPLAY_TTL 4 // display intervals (4 * 2 seconds)
#define DISPLAY_BW_UNITS 20 // length of the bandwidth bar


static inline void ansi_topleft(void) {
Expand Down
2 changes: 1 addition & 1 deletion src/fnettrace/hostnames
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#
# Static Internet Map
#
# Unfortunately, we cannot do a hostname lookup. This will leak a lot of
# Unfortunately we cannot do a hostname lookup. This will leak a lot of
# information about what network resources we access.
# A static map, helped out by geoip package available on all Linux distros,
# will have to do it for now!
Expand Down
72 changes: 63 additions & 9 deletions src/fnettrace/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static int arg_netfilter = 0;
static char *arg_log = NULL;

typedef struct hnode_t {
struct hnode_t *hnext; // used for hash table
struct hnode_t *hnext; // used for hash table and unused linked list
struct hnode_t *dnext; // used to display stremas on the screen
uint32_t ip_src;
uint32_t bytes; // number of bytes received in the last display interval
Expand All @@ -45,6 +45,36 @@ HNode *htable[HMAX] = {NULL};
// display linked list
HNode *dlist = NULL;


// speed up malloc/free
#define HNODE_MAX_MALLOC 16
static HNode *hnode_unused = NULL;
static int hnode_malloc_cnt = 0;
HNode *hmalloc(void) {
if (hnode_unused == NULL) {
hnode_unused = malloc(sizeof(HNode) * HNODE_MAX_MALLOC);
if (!hnode_unused)
errExit("malloc");
memset(hnode_unused, 0, sizeof(HNode) * HNODE_MAX_MALLOC);
HNode *ptr = hnode_unused;
int i;
for ( i = 1; i < HNODE_MAX_MALLOC; i++, ptr++)
ptr->hnext = hnode_unused + i;
}

HNode *rv = hnode_unused;
hnode_unused = hnode_unused->hnext;
return rv;
}

void hfree(HNode *ptr) {
assert(ptr);
memset(ptr, 0, sizeof(HNode));
ptr->hnext = hnode_unused;
hnode_unused = ptr;
}


static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) {
uint8_t h = hash(ip_src);

Expand All @@ -65,9 +95,8 @@ static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint
#ifdef DEBUG
printf("malloc %d.%d.%d.%d\n", PRINT_IP(ip_src));
#endif
HNode *hnew = malloc(sizeof(HNode));
if (!hnew)
errExit("malloc");
HNode *hnew = hmalloc();
assert(hnew);
hnew->hostname = NULL;
hnew->ip_src = ip_src;
hnew->port_src = port_src;
Expand Down Expand Up @@ -117,7 +146,7 @@ static void hnode_free(HNode *elem) {
htable[h] = elem->hnext;
else
prev->hnext = elem->hnext;
free(elem);
hfree(elem);
}

#ifdef DEBUG
Expand Down Expand Up @@ -194,14 +223,15 @@ static unsigned adjust_bandwidth(unsigned bw) {

static void hnode_print(unsigned bw) {
assert(!arg_netfilter);
ansi_clrscr();

bw = (bw < 1024 * DISPLAY_INTERVAL)? 1024 * DISPLAY_INTERVAL: bw;
#ifdef DEBUG
printf("*********************\n");
debug_dlist();
printf("-----------------------------\n");
debug_hnode();
printf("*********************\n");
#else
ansi_clrscr();
#endif

// get terminal size
Expand Down Expand Up @@ -291,6 +321,17 @@ static void hnode_print(unsigned bw) {
ptr = next;
}

#ifdef DEBUG
{
int cnt = 0;
HNode *ptr = hnode_unused;
while (ptr) {
cnt++;
ptr = ptr->hnext;
}
printf("hnode unused %d\n", cnt);
}
#endif
}

static void run_trace(void) {
Expand Down Expand Up @@ -343,9 +384,22 @@ static void run_trace(void) {

unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
if (bytes >= 20) { // size of IP header
bw += bytes + 14; // assume a 14 byte Ethernet layer
#ifdef DEBUG
{
uint32_t ip_src;
memcpy(&ip_src, buf + 12, 4);
ip_src = ntohl(ip_src);

uint32_t ip_dst;
memcpy(&ip_dst, buf + 16, 4);
ip_dst = ntohl(ip_dst);
printf("%d.%d.%d.%d -> %d.%d.%d.%d, %u bytes\n", PRINT_IP(ip_src), PRINT_IP(ip_dst), bytes);
}
#endif
// filter out loopback traffic
if (buf[12] != 127) {
if (buf[12] != 127 && buf[16] != 127) {
bw += bytes + 14; // assume a 14 byte Ethernet layer

uint32_t ip_src;
memcpy(&ip_src, buf + 12, 4);
ip_src = ntohl(ip_src);
Expand Down
52 changes: 38 additions & 14 deletions src/fnettrace/radix.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,44 @@ typedef struct rnode_t {
RNode *head = 0;
int radix_nodes = 0;

// get rid of the malloc overhead
#define RNODE_MAX_MALLOC 128
static RNode *rnode_unused = NULL;
static int rnode_malloc_cnt = 0;
static RNode *rmalloc(void) {
if (rnode_unused == NULL || rnode_malloc_cnt >= RNODE_MAX_MALLOC) {
rnode_unused = malloc(sizeof(RNode) * RNODE_MAX_MALLOC);
if (!rnode_unused)
errExit("malloc");
memset(rnode_unused, 0, sizeof(RNode) * RNODE_MAX_MALLOC);
rnode_malloc_cnt = 0;
}

rnode_malloc_cnt++;
return rnode_unused + rnode_malloc_cnt - 1;
}


static inline char *duplicate_name(const char *name) {
assert(name);

if (strcmp(name, "United States") == 0)
return "United States";
else if (strcmp(name, "Amazon") == 0)
return "Amazon";
return strdup(name);
}

static inline RNode *addOne(RNode *ptr, char *name) {
assert(ptr);
if (ptr->one)
return ptr->one;
RNode *node = malloc(sizeof(RNode));
if (!node)
errExit("malloc");
memset(node, 0, sizeof(RNode));
RNode *node = rmalloc();
assert(node);
if (name) {
node->name = strdup(name);
node->name = duplicate_name(name);
if (!node->name)
errExit("strdup");
errExit("duplicate name");
}

ptr->one = node;
Expand All @@ -56,14 +82,12 @@ static inline RNode *addZero(RNode *ptr, char *name) {
assert(ptr);
if (ptr->zero)
return ptr->zero;
RNode *node = malloc(sizeof(RNode));
if (!node)
errExit("malloc");
memset(node, 0, sizeof(RNode));
RNode *node = rmalloc();
assert(node);
if (name) {
node->name = strdup(name);
node->name = duplicate_name(name);
if (!node->name)
errExit("strdup");
errExit("duplicate name");
}

ptr->zero = node;
Expand Down Expand Up @@ -97,9 +121,9 @@ char *radix_add(uint32_t ip, uint32_t mask, char *name) {
}
assert(ptr);
if (!ptr->name) {
ptr->name = strdup(name);
ptr->name = duplicate_name(name);
if (!ptr->name)
errExit("strdup");
errExit("duplicate_name");
}

return ptr->name;
Expand Down
46 changes: 39 additions & 7 deletions src/man/firejail.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,28 @@ $ firejail --name=browser --net=eth0 --netfilter firefox &
.br
$ firejail --netfilter6.print=browser

.TP
\fB\-\-netlock=name/pid
Several type of programs (email clients, multiplayer games etc.) talk to a very small
number of IP addresses. But the best example is tor browser. It only talks to a guard node,
and there are two or three more on standby in case the main one fails.
During startup, the browser contacts all of them, after that it keeps talking to the main
one... for weeks!

Use the network locking feature to build and deploy a network firewall in your sandbox.
The firewall allows only the network traffic to the IP addresses detected during the program
startup. Traffic to any other address is quietly dropped. By default the startup monitoring
time is one minute. Example:
.br

.br
$ firejail --net=eth0 --netlock \\
.br
--private=~/tor-browser_en-US ./start-tor-browser.desktop
.br

.br

.TP
\fB\-\-netmask=address
Use this option when you want to assign an IP address in a new namespace and
Expand Down Expand Up @@ -1500,25 +1522,35 @@ PID User RX(KB/s) TX(KB/s) Command
.br
7383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission
.TP
\fB\-\-nettrace=name|pid
\fB\-\-nettrace[=name|pid]
Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes
created with \-\-net are supported.
.br

.br
$ firejail --nettrace=browser
Without a name/pid, Firejail will monitor the main system network namespace.
.br

.br
$ firejail --nettrace=browser
.br

.br
95 KB/s geoip 457, IP database 4436
.br
52 KB/s *********** 64.222.84.207:443 United States
.br
86 KB/s ********* 64.222.84.207:443 United States
33 KB/s ******* 89.147.74.105:63930 Hungary
.br
76 KB/s ******** 192.229.210.163:443 MCI
0 B/s 45.90.28.0:443 NextDNS
.br
111 B/s 9.9.9.9:53 Quad9 DNS
0 B/s 94.70.122.176:52309(UDP) Greece
.br
32 KB/s *** 142.250.179.182:443 Google
339 B/s 104.26.7.35:443 Cloudflare
.br

.br
If /usr/bin/geoiplookup is installed (geoip-bin packet in Debian),
If /usr/bin/geoiplookup is installed (geoip-bin package in Debian),
the country the IP address originates from is added to the trace.
We also use the static IP map in /etc/firejail/hostnames
to print the domain names for some of the more common websites and cloud platforms.
Expand Down

0 comments on commit 4dd1e92

Please sign in to comment.