diff options
Diffstat (limited to 'src/fnettrace/main.c')
-rw-r--r-- | src/fnettrace/main.c | 189 |
1 files changed, 142 insertions, 47 deletions
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c index 658b90eb3..804a9cd80 100644 --- a/src/fnettrace/main.c +++ b/src/fnettrace/main.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2014-2021 Firejail Authors | 2 | * Copyright (C) 2014-2022 Firejail Authors |
3 | * | 3 | * |
4 | * This file is part of firejail project | 4 | * This file is part of firejail project |
5 | * | 5 | * |
@@ -18,37 +18,42 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #include "fnettrace.h" | 20 | #include "fnettrace.h" |
21 | //#define DEBUG 1 | 21 | #include "radix.h" |
22 | #include <sys/ioctl.h> | ||
22 | #define MAX_BUF_SIZE (64 * 1024) | 23 | #define MAX_BUF_SIZE (64 * 1024) |
23 | 24 | ||
24 | static int arg_netfilter = 0; | 25 | static int arg_netfilter = 0; |
25 | static char *arg_log = NULL; | 26 | static char *arg_log = NULL; |
26 | 27 | ||
27 | typedef struct hlist_t { | 28 | typedef struct hnode_t { |
28 | struct hlist_t *hnext; // used for hash table | 29 | struct hnode_t *hnext; // used for hash table |
29 | struct hlist_t *dnext; // used to display stremas on the screen | 30 | struct hnode_t *dnext; // used to display stremas on the screen |
30 | uint32_t ip_src; | 31 | uint32_t ip_src; |
31 | uint32_t ip_dst; | 32 | uint32_t ip_dst; |
32 | uint64_t bytes; // number of bytes received in the last display interval | 33 | uint32_t bytes; // number of bytes received in the last display interval |
33 | uint16_t port_src; | 34 | uint16_t port_src; |
34 | uint16_t ip_instance; | ||
35 | // the firewall is build based on source address, and in the linked list | ||
36 | // we have elements with the same address but different ports | ||
37 | uint8_t protocol; | 35 | uint8_t protocol; |
38 | } HList; | 36 | // the firewall is build based on source address, and in the linked list |
37 | // we have elements with the same address but different ports | ||
38 | uint8_t ip_instance; | ||
39 | char *hostname; | ||
40 | int ttl; | ||
41 | } HNode; | ||
39 | 42 | ||
40 | // hash table | 43 | // hash table |
41 | #define HMAX 256 | 44 | #define HMAX 256 |
42 | HList *htable[HMAX] = {NULL}; | 45 | HNode *htable[HMAX] = {NULL}; |
43 | // display linked list | 46 | // display linked list |
44 | HList *dlist = NULL; | 47 | HNode *dlist = NULL; |
45 | 48 | ||
46 | static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16_t port_src, uint64_t bytes) { | 49 | static unsigned bwmax = 0; // max bytes received in a display interval |
50 | |||
51 | static void hnode_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16_t port_src, uint32_t bytes) { | ||
47 | uint8_t h = hash(ip_src); | 52 | uint8_t h = hash(ip_src); |
48 | 53 | ||
49 | // find | 54 | // find |
50 | int ip_instance = 0; | 55 | int ip_instance = 0; |
51 | HList *ptr = htable[h]; | 56 | HNode *ptr = htable[h]; |
52 | while (ptr) { | 57 | while (ptr) { |
53 | if (ptr->ip_src == ip_src) { | 58 | if (ptr->ip_src == ip_src) { |
54 | ip_instance++; | 59 | ip_instance++; |
@@ -63,9 +68,10 @@ static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16 | |||
63 | #ifdef DEBUG | 68 | #ifdef DEBUG |
64 | printf("malloc %d.%d.%d.%d\n", PRINT_IP(ip_src)); | 69 | printf("malloc %d.%d.%d.%d\n", PRINT_IP(ip_src)); |
65 | #endif | 70 | #endif |
66 | HList *hnew = malloc(sizeof(HList)); | 71 | HNode *hnew = malloc(sizeof(HNode)); |
67 | if (!hnew) | 72 | if (!hnew) |
68 | errExit("malloc"); | 73 | errExit("malloc"); |
74 | hnew->hostname = NULL; | ||
69 | hnew->ip_src = ip_src; | 75 | hnew->ip_src = ip_src; |
70 | hnew->ip_dst = ip_dst; | 76 | hnew->ip_dst = ip_dst; |
71 | hnew->port_src = port_src; | 77 | hnew->port_src = port_src; |
@@ -73,6 +79,7 @@ static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16 | |||
73 | hnew->hnext = NULL; | 79 | hnew->hnext = NULL; |
74 | hnew->bytes = bytes; | 80 | hnew->bytes = bytes; |
75 | hnew->ip_instance = ip_instance + 1; | 81 | hnew->ip_instance = ip_instance + 1; |
82 | hnew->ttl = DISPLAY_TTL; | ||
76 | if (htable[h] == NULL) | 83 | if (htable[h] == NULL) |
77 | htable[h] = hnew; | 84 | htable[h] = hnew; |
78 | else { | 85 | else { |
@@ -95,17 +102,17 @@ static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16 | |||
95 | logprintf(" %d.%d.%d.%d ", PRINT_IP(hnew->ip_src)); | 102 | logprintf(" %d.%d.%d.%d ", PRINT_IP(hnew->ip_src)); |
96 | } | 103 | } |
97 | 104 | ||
98 | static void hlist_free(HList *elem) { | 105 | static void hnode_free(HNode *elem) { |
99 | assert(elem); | 106 | assert(elem); |
100 | #ifdef DEBUG | 107 | #ifdef DEBUG |
101 | printf("free %d.%d.%d.%d\n", PRINT_IP(elem->ip_src)); | 108 | printf("free %d.%d.%d.%d\n", PRINT_IP(elem->ip_src)); |
102 | #endif | 109 | #endif |
103 | 110 | ||
104 | uint8_t h = hash(elem->ip_src); | 111 | uint8_t h = hash(elem->ip_src); |
105 | HList *ptr = htable[h]; | 112 | HNode *ptr = htable[h]; |
106 | assert(ptr); | 113 | assert(ptr); |
107 | 114 | ||
108 | HList *prev = NULL; | 115 | HNode *prev = NULL; |
109 | while (ptr != elem) { | 116 | while (ptr != elem) { |
110 | prev = ptr; | 117 | prev = ptr; |
111 | ptr = ptr->hnext; | 118 | ptr = ptr->hnext; |
@@ -119,25 +126,45 @@ static void hlist_free(HList *elem) { | |||
119 | 126 | ||
120 | #ifdef DEBUG | 127 | #ifdef DEBUG |
121 | static void debug_dlist(void) { | 128 | static void debug_dlist(void) { |
122 | HList *ptr = dlist; | 129 | HNode *ptr = dlist; |
123 | while (ptr) { | 130 | while (ptr) { |
124 | printf("dlist %d.%d.%d.%d:%d\n", PRINT_IP(ptr->ip_src), ptr->port_src); | 131 | printf("dlist %d.%d.%d.%d:%d\n", PRINT_IP(ptr->ip_src), ptr->port_src); |
125 | ptr = ptr->dnext; | 132 | ptr = ptr->dnext; |
126 | } | 133 | } |
127 | } | 134 | } |
128 | static void debug_hlist(void) { | 135 | static void debug_hnode(void) { |
129 | int i; | 136 | int i; |
130 | for (i = 0; i < HMAX; i++) { | 137 | for (i = 0; i < HMAX; i++) { |
131 | HList *ptr = htable[i]; | 138 | HNode *ptr = htable[i]; |
132 | while (ptr) { | 139 | while (ptr) { |
133 | printf("hlist (%d) %d.%d.%d.%d:%d\n", i, PRINT_IP(ptr->ip_src), ptr->port_src); | 140 | printf("hnode (%d) %d.%d.%d.%d:%d\n", i, PRINT_IP(ptr->ip_src), ptr->port_src); |
134 | ptr = ptr->hnext; | 141 | ptr = ptr->hnext; |
135 | } | 142 | } |
136 | } | 143 | } |
137 | } | 144 | } |
138 | #endif | 145 | #endif |
139 | 146 | ||
140 | static void hlist_print(void) { | 147 | static char *bw_line[DISPLAY_BW_UNITS + 1] = { NULL }; |
148 | |||
149 | static char *print_bw(unsigned units) { | ||
150 | if (units > DISPLAY_BW_UNITS) | ||
151 | units = DISPLAY_BW_UNITS ; | ||
152 | |||
153 | if (bw_line[units] == NULL) { | ||
154 | char *ptr = malloc(DISPLAY_BW_UNITS + 1); | ||
155 | if (!ptr) | ||
156 | errExit("malloc"); | ||
157 | bw_line[units] = ptr; | ||
158 | |||
159 | unsigned i; | ||
160 | for (i = 0; i < DISPLAY_BW_UNITS; i++, ptr++) | ||
161 | sprintf(ptr, "%s", (i < units)? "*": " "); | ||
162 | } | ||
163 | |||
164 | return bw_line[units]; | ||
165 | } | ||
166 | |||
167 | static void hnode_print(void) { | ||
141 | assert(!arg_netfilter); | 168 | assert(!arg_netfilter); |
142 | ansi_clrscr(); | 169 | ansi_clrscr(); |
143 | 170 | ||
@@ -145,30 +172,66 @@ static void hlist_print(void) { | |||
145 | printf("*********************\n"); | 172 | printf("*********************\n"); |
146 | debug_dlist(); | 173 | debug_dlist(); |
147 | printf("-----------------------------\n"); | 174 | printf("-----------------------------\n"); |
148 | debug_hlist(); | 175 | debug_hnode(); |
149 | printf("*********************\n"); | 176 | printf("*********************\n"); |
150 | #endif | 177 | #endif |
151 | 178 | ||
152 | HList *ptr = dlist; | 179 | // get terminal size |
153 | HList *prev = NULL; | 180 | struct winsize sz; |
181 | int col = 80; | ||
182 | if (isatty(STDIN_FILENO)) { | ||
183 | if (!ioctl(0, TIOCGWINSZ, &sz)) | ||
184 | col = sz.ws_col; | ||
185 | } | ||
186 | #define LINE_MAX 200 | ||
187 | char line[LINE_MAX + 1]; | ||
188 | if (col > LINE_MAX) | ||
189 | col = LINE_MAX; | ||
190 | |||
191 | HNode *ptr = dlist; | ||
192 | HNode *prev = NULL; | ||
154 | while (ptr) { | 193 | while (ptr) { |
155 | HList *next = ptr->dnext; | 194 | HNode *next = ptr->dnext; |
156 | if (ptr->bytes) { | 195 | if (--ptr->ttl > 0) { |
157 | char ip_src[30]; | 196 | char bytes[11]; |
158 | sprintf(ip_src, "%d.%d.%d.%d:%u", PRINT_IP(ptr->ip_src), ptr->port_src); | 197 | if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 1024 * 2)) // > 2 MB/second |
159 | char ip_dst[30]; | 198 | sprintf(bytes, "%u MB/s", |
160 | sprintf(ip_dst, "%d.%d.%d.%d", PRINT_IP(ptr->ip_dst)); | 199 | (unsigned) (ptr->bytes / (DISPLAY_INTERVAL * 1024* 1024))); |
161 | printf("%-22s => %-15s %s:", | 200 | else if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 2)) // > 2 KB/second |
162 | ip_src, | 201 | sprintf(bytes, "%u KB/s", |
163 | ip_dst, | 202 | (unsigned) (ptr->bytes / (DISPLAY_INTERVAL * 1024))); |
164 | (ptr->protocol == 6)? "TCP": "UDP"); | ||
165 | |||
166 | if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 2)) // > 2 KB/second | ||
167 | printf(" %lu KB/sec\n", | ||
168 | ptr->bytes / (DISPLAY_INTERVAL * 1024)); | ||
169 | else | 203 | else |
170 | printf(" %lu B/sec\n", | 204 | sprintf(bytes, "%u B/s", (unsigned) (ptr->bytes / DISPLAY_INTERVAL)); |
171 | ptr->bytes / DISPLAY_INTERVAL); | 205 | |
206 | char *hostname = ptr->hostname; | ||
207 | if (!hostname) | ||
208 | hostname = radix_find_last(ptr->ip_src); | ||
209 | |||
210 | if (!hostname) | ||
211 | hostname = retrieve_hostname(ptr->ip_src); | ||
212 | |||
213 | if (!hostname) | ||
214 | hostname = " "; | ||
215 | else { | ||
216 | ptr->hostname = strdup(hostname); | ||
217 | if (!ptr->hostname) | ||
218 | errExit("strdup"); | ||
219 | } | ||
220 | |||
221 | unsigned bwunit = bwmax / DISPLAY_BW_UNITS; | ||
222 | unsigned units = ptr->bytes / bwunit; | ||
223 | char *bwline = print_bw(units); | ||
224 | |||
225 | sprintf(line, "%10s %s %d.%d.%d.%d:%u %s\n", bytes, bwline, PRINT_IP(ptr->ip_src), ptr->port_src, hostname); | ||
226 | int len = strlen(line); | ||
227 | if (col > 4 && len > col) { | ||
228 | line[col] = '\0'; | ||
229 | line[col - 1] = '\n'; | ||
230 | } | ||
231 | printf("%s", line); | ||
232 | |||
233 | if (ptr->bytes) | ||
234 | ptr->ttl = DISPLAY_TTL; | ||
172 | ptr->bytes = 0; | 235 | ptr->bytes = 0; |
173 | prev = ptr; | 236 | prev = ptr; |
174 | } | 237 | } |
@@ -178,7 +241,7 @@ static void hlist_print(void) { | |||
178 | dlist = next; | 241 | dlist = next; |
179 | else | 242 | else |
180 | prev->dnext = next; | 243 | prev->dnext = next; |
181 | hlist_free(ptr); | 244 | hnode_free(ptr); |
182 | } | 245 | } |
183 | 246 | ||
184 | ptr = next; | 247 | ptr = next; |
@@ -199,14 +262,18 @@ static void run_trace(void) { | |||
199 | unsigned last_print_traces = 0; | 262 | unsigned last_print_traces = 0; |
200 | unsigned last_print_remaining = 0; | 263 | unsigned last_print_remaining = 0; |
201 | unsigned char buf[MAX_BUF_SIZE]; | 264 | unsigned char buf[MAX_BUF_SIZE]; |
265 | unsigned bwcurrent = 0; | ||
202 | while (1) { | 266 | while (1) { |
203 | unsigned end = time(NULL); | 267 | unsigned end = time(NULL); |
204 | if (arg_netfilter && end - start >= NETLOCK_INTERVAL) | 268 | if (arg_netfilter && end - start >= NETLOCK_INTERVAL) |
205 | break; | 269 | break; |
206 | if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second | 270 | if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second |
271 | if (bwcurrent > bwmax) | ||
272 | bwmax = bwcurrent; | ||
207 | if (!arg_netfilter) | 273 | if (!arg_netfilter) |
208 | hlist_print(); | 274 | hnode_print(); |
209 | last_print_traces = end; | 275 | last_print_traces = end; |
276 | bwcurrent = 0; | ||
210 | } | 277 | } |
211 | if (arg_netfilter && last_print_remaining != end) { | 278 | if (arg_netfilter && last_print_remaining != end) { |
212 | logprintf("."); | 279 | logprintf("."); |
@@ -233,6 +300,7 @@ static void run_trace(void) { | |||
233 | 300 | ||
234 | unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL); | 301 | unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL); |
235 | if (bytes >= 20) { // size of IP header | 302 | if (bytes >= 20) { // size of IP header |
303 | bwcurrent += bytes + 14; // assume a 14 byte Ethernet layer | ||
236 | // filter out loopback traffic | 304 | // filter out loopback traffic |
237 | if (buf[12] != 127) { | 305 | if (buf[12] != 127) { |
238 | uint32_t ip_src; | 306 | uint32_t ip_src; |
@@ -248,7 +316,7 @@ static void run_trace(void) { | |||
248 | memcpy(&port_src, buf + hlen, 2); | 316 | memcpy(&port_src, buf + hlen, 2); |
249 | port_src = ntohs(port_src); | 317 | port_src = ntohs(port_src); |
250 | 318 | ||
251 | hlist_add(ip_src, ip_dst, buf[9], port_src, (uint64_t) bytes); | 319 | hnode_add(ip_src, ip_dst, buf[9], port_src, bytes + 14); |
252 | } | 320 | } |
253 | } | 321 | } |
254 | } | 322 | } |
@@ -274,7 +342,7 @@ static int print_filter(FILE *fp) { | |||
274 | 342 | ||
275 | int i; | 343 | int i; |
276 | for (i = 0; i < HMAX; i++) { | 344 | for (i = 0; i < HMAX; i++) { |
277 | HList *ptr = htable[i]; | 345 | HNode *ptr = htable[i]; |
278 | while (ptr) { | 346 | while (ptr) { |
279 | // filter rules are targeting ip address, the port number is disregarded, | 347 | // filter rules are targeting ip address, the port number is disregarded, |
280 | // so we look only at the first instance of an address | 348 | // so we look only at the first instance of an address |
@@ -416,6 +484,7 @@ void logprintf(char* fmt, ...) { | |||
416 | static void usage(void) { | 484 | static void usage(void) { |
417 | printf("Usage: fnetlock [OPTIONS]\n"); | 485 | printf("Usage: fnetlock [OPTIONS]\n"); |
418 | printf("Options:\n"); | 486 | printf("Options:\n"); |
487 | printf(" --build=filename - compact list of addresses\n"); | ||
419 | printf(" --help, -? - this help screen\n"); | 488 | printf(" --help, -? - this help screen\n"); |
420 | printf(" --netfilter - build the firewall rules and commit them.\n"); | 489 | printf(" --netfilter - build the firewall rules and commit them.\n"); |
421 | printf(" --log=filename - logfile\n"); | 490 | printf(" --log=filename - logfile\n"); |
@@ -424,7 +493,26 @@ static void usage(void) { | |||
424 | 493 | ||
425 | int main(int argc, char **argv) { | 494 | int main(int argc, char **argv) { |
426 | int i; | 495 | int i; |
427 | printf("\n\n"); | 496 | |
497 | #ifdef DEBUG | ||
498 | // radix test | ||
499 | radix_add(0x09000000, 0xff000000, "IBM"); | ||
500 | radix_add(0x09090909, 0xffffffff, "Quad9 DNS"); | ||
501 | radix_add(0x09000000, 0xff000000, "IBM"); | ||
502 | radix_print(); | ||
503 | printf("This test should print \"IBM, Quad9 DNS, IBM\"\n"); | ||
504 | char *name = radix_find_first(0x09090909); | ||
505 | printf("%s, ", name); | ||
506 | name = radix_find_last(0x09090909); | ||
507 | printf("%s, ", name); | ||
508 | name = radix_find_last(0x09322209); | ||
509 | printf("%s\n", name); | ||
510 | #endif | ||
511 | |||
512 | if (argc == 2 && strncmp(argv[1], "--build=", 8) == 0) { | ||
513 | build_list(argv[1] + 8); | ||
514 | return 0; | ||
515 | } | ||
428 | 516 | ||
429 | if (getuid() != 0) { | 517 | if (getuid() != 0) { |
430 | fprintf(stderr, "Error: you need to be root to run this program\n"); | 518 | fprintf(stderr, "Error: you need to be root to run this program\n"); |
@@ -449,6 +537,13 @@ int main(int argc, char **argv) { | |||
449 | ansi_clrscr(); | 537 | ansi_clrscr(); |
450 | if (arg_netfilter) | 538 | if (arg_netfilter) |
451 | logprintf("starting network lockdown\n"); | 539 | logprintf("starting network lockdown\n"); |
540 | else { | ||
541 | char *fname; | ||
542 | if (asprintf(&fname, "%s/hostnames", SYSCONFDIR) == -1) | ||
543 | errExit("asprintf"); | ||
544 | load_hostnames(fname); | ||
545 | free(fname); | ||
546 | } | ||
452 | 547 | ||
453 | run_trace(); | 548 | run_trace(); |
454 | if (arg_netfilter) { | 549 | if (arg_netfilter) { |