diff options
Diffstat (limited to 'src/fnettrace/main.c')
-rw-r--r-- | src/fnettrace/main.c | 486 |
1 files changed, 224 insertions, 262 deletions
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c index 932afff61..3bafd9090 100644 --- a/src/fnettrace/main.c +++ b/src/fnettrace/main.c | |||
@@ -25,15 +25,63 @@ | |||
25 | #include <signal.h> | 25 | #include <signal.h> |
26 | #define MAX_BUF_SIZE (64 * 1024) | 26 | #define MAX_BUF_SIZE (64 * 1024) |
27 | 27 | ||
28 | static int arg_netfilter = 0; | ||
29 | static int arg_tail = 0; | ||
30 | static char *arg_log = NULL; | 28 | static char *arg_log = NULL; |
31 | 29 | ||
30 | //***************************************************************** | ||
31 | // packet stats | ||
32 | //***************************************************************** | ||
32 | uint32_t stats_pkts = 0; | 33 | uint32_t stats_pkts = 0; |
33 | uint32_t stats_icmp = 0; | 34 | uint32_t stats_icmp_echo = 0; |
34 | uint32_t stats_dns = 0; | 35 | uint32_t stats_dns = 0; |
36 | uint32_t stats_dns_dot = 0; | ||
37 | uint32_t stats_dns_doh = 0; | ||
38 | uint32_t stats_dns_doq = 0; | ||
39 | uint32_t stats_tls = 0; | ||
40 | uint32_t stats_quic = 0; | ||
41 | uint32_t stats_tor = 0; | ||
42 | uint32_t stats_http = 0; | ||
43 | uint32_t stats_ssh = 0; | ||
44 | |||
45 | //***************************************************************** | ||
46 | // sni/dns log storage | ||
47 | //***************************************************************** | ||
48 | typedef struct lognode_t { | ||
49 | #define LOG_RECORD_LEN 255 | ||
50 | char record[LOG_RECORD_LEN + 1]; | ||
51 | } LogNode; | ||
52 | // circular list of SNI log records | ||
53 | #define SNIMAX 64 | ||
54 | LogNode sni_table[SNIMAX] = {0}; | ||
55 | int sni_index = 0; | ||
56 | |||
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 | } | ||
35 | 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 | } | ||
36 | 81 | ||
82 | //***************************************************************** | ||
83 | // traffic trace storage - hash table for fast access + linked list for display purposes | ||
84 | //***************************************************************** | ||
37 | typedef struct hnode_t { | 85 | typedef struct hnode_t { |
38 | struct hnode_t *hnext; // used for hash table and unused linked list | 86 | struct hnode_t *hnext; // used for hash table and unused linked list |
39 | struct hnode_t *dnext; // used to display streams on the screen | 87 | struct hnode_t *dnext; // used to display streams on the screen |
@@ -42,6 +90,7 @@ typedef struct hnode_t { | |||
42 | 90 | ||
43 | // stats | 91 | // stats |
44 | uint32_t bytes; // number of bytes received in the last display interval | 92 | 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 | ||
45 | uint16_t port_src; | 94 | uint16_t port_src; |
46 | uint8_t protocol; | 95 | uint8_t protocol; |
47 | 96 | ||
@@ -97,6 +146,7 @@ static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint | |||
97 | ip_instance++; | 146 | ip_instance++; |
98 | if (ptr->port_src == port_src && ptr->protocol == protocol) { | 147 | if (ptr->port_src == port_src && ptr->protocol == protocol) { |
99 | ptr->bytes += bytes; | 148 | ptr->bytes += bytes; |
149 | ptr->pkts++; | ||
100 | assert(ptr->rnode); | 150 | assert(ptr->rnode); |
101 | ptr->rnode->pkts++; | 151 | ptr->rnode->pkts++; |
102 | return; | 152 | return; |
@@ -115,6 +165,7 @@ static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint | |||
115 | hnew->protocol = protocol; | 165 | hnew->protocol = protocol; |
116 | hnew->hnext = NULL; | 166 | hnew->hnext = NULL; |
117 | hnew->bytes = bytes; | 167 | hnew->bytes = bytes; |
168 | hnew->pkts = 1; | ||
118 | hnew->ip_instance = ip_instance + 1; | 169 | hnew->ip_instance = ip_instance + 1; |
119 | hnew->ttl = DISPLAY_TTL; | 170 | hnew->ttl = DISPLAY_TTL; |
120 | if (htable[h] == NULL) | 171 | if (htable[h] == NULL) |
@@ -139,9 +190,6 @@ static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint | |||
139 | if (!hnew->rnode) | 190 | if (!hnew->rnode) |
140 | hnew->rnode = radix_add(hnew->ip_src, 0xffffffff, NULL); | 191 | hnew->rnode = radix_add(hnew->ip_src, 0xffffffff, NULL); |
141 | hnew->rnode->pkts++; | 192 | hnew->rnode->pkts++; |
142 | |||
143 | if (arg_netfilter) | ||
144 | logprintf(" %d.%d.%d.%d ", PRINT_IP(hnew->ip_src)); | ||
145 | } | 193 | } |
146 | 194 | ||
147 | static void hnode_free(HNode *elem) { | 195 | static void hnode_free(HNode *elem) { |
@@ -242,23 +290,23 @@ typedef struct port_type_t { | |||
242 | char *service; | 290 | char *service; |
243 | } PortType; | 291 | } PortType; |
244 | static PortType ports[] = { | 292 | static PortType ports[] = { |
245 | {20, "(FTP)"}, | 293 | {20, "FTP"}, |
246 | {21, "(FTP)"}, | 294 | {21, "FTP"}, |
247 | {22, "(SSH)"}, | 295 | {22, "SSH"}, |
248 | {23, "(telnet)"}, | 296 | {23, "telnet"}, |
249 | {25, "(SMTP)"}, | 297 | {25, "SMTP"}, |
250 | {43, "(WHOIS)"}, | 298 | {43, "WHOIS"}, |
251 | {67, "(DHCP)"}, | 299 | {67, "DHCP"}, |
252 | {68, "(DHCP)"}, | 300 | {68, "DHCP"}, |
253 | {69, "(TFTP)"}, | 301 | {69, "TFTP"}, |
254 | {80, "(HTTP)"}, | 302 | {80, "HTTP"}, |
255 | {109, "(POP2)"}, | 303 | {109, "POP2"}, |
256 | {110, "(POP3)"}, | 304 | {110, "POP3"}, |
257 | {113, "(IRC)"}, | 305 | {113, "IRC"}, |
258 | {123, "(NTP)"}, | 306 | {123, "NTP"}, |
259 | {161, "(SNMP)"}, | 307 | {161, "SNMP"}, |
260 | {162, "(SNMP)"}, | 308 | {162, "SNMP"}, |
261 | {194, "(IRC)"}, | 309 | {194, "IRC"}, |
262 | {0, NULL}, | 310 | {0, NULL}, |
263 | }; | 311 | }; |
264 | 312 | ||
@@ -266,32 +314,32 @@ static PortType ports[] = { | |||
266 | static inline const char *common_port(uint16_t port) { | 314 | static inline const char *common_port(uint16_t port) { |
267 | if (port >= 6660 && port <= 10162) { | 315 | if (port >= 6660 && port <= 10162) { |
268 | if (port >= 6660 && port <= 6669) | 316 | if (port >= 6660 && port <= 6669) |
269 | return "(IRC)"; | 317 | return "IRC"; |
270 | else if (port == 6679) | 318 | else if (port == 6679) |
271 | return "(IRC)"; | 319 | return "IRC"; |
272 | else if (port == 6771) | 320 | else if (port == 6771) |
273 | return "(BitTorrent)"; | 321 | return "BitTorrent"; |
274 | else if (port >= 6881 && port <= 6999) | 322 | else if (port >= 6881 && port <= 6999) |
275 | return "(BitTorrent)"; | 323 | return "BitTorrent"; |
276 | else if (port == 9001) | 324 | else if (port == 9001) |
277 | return "(Tor)"; | 325 | return "Tor"; |
278 | else if (port == 9030) | 326 | else if (port == 9030) |
279 | return "(Tor)"; | 327 | return "Tor"; |
280 | else if (port == 9050) | 328 | else if (port == 9050) |
281 | return "(Tor)"; | 329 | return "Tor"; |
282 | else if (port == 9051) | 330 | else if (port == 9051) |
283 | return "(Tor)"; | 331 | return "Tor"; |
284 | else if (port == 9150) | 332 | else if (port == 9150) |
285 | return "(Tor)"; | 333 | return "Tor"; |
286 | else if (port == 10161) | 334 | else if (port == 10161) |
287 | return "(secure SNMP)"; | 335 | return "secure SNMP"; |
288 | else if (port == 10162) | 336 | else if (port == 10162) |
289 | return "(secure SNMP)"; | 337 | return "secure SNMP"; |
290 | return NULL; | 338 | return NULL; |
291 | } | 339 | } |
292 | 340 | ||
293 | if (port <= 194) { | 341 | if (port <= 194) { |
294 | PortType *ptr =&ports[0]; | 342 | PortType *ptr = &ports[0]; |
295 | while(ptr->service != NULL) { | 343 | while(ptr->service != NULL) { |
296 | if (ptr->port == port) | 344 | if (ptr->port == port) |
297 | return ptr->service; | 345 | return ptr->service; |
@@ -305,7 +353,6 @@ static inline const char *common_port(uint16_t port) { | |||
305 | 353 | ||
306 | 354 | ||
307 | static void hnode_print(unsigned bw) { | 355 | static void hnode_print(unsigned bw) { |
308 | assert(!arg_netfilter); | ||
309 | bw = (bw < 1024 * DISPLAY_INTERVAL) ? 1024 * DISPLAY_INTERVAL : bw; | 356 | bw = (bw < 1024 * DISPLAY_INTERVAL) ? 1024 * DISPLAY_INTERVAL : bw; |
310 | #ifdef DEBUG | 357 | #ifdef DEBUG |
311 | printf("*********************\n"); | 358 | printf("*********************\n"); |
@@ -336,7 +383,7 @@ static void hnode_print(unsigned bw) { | |||
336 | else | 383 | else |
337 | sprintf(stats, "%u KB/s ", bw / (1024 * DISPLAY_INTERVAL)); | 384 | sprintf(stats, "%u KB/s ", bw / (1024 * DISPLAY_INTERVAL)); |
338 | // int len = snprintf(line, LINE_MAX, "%32s geoip %d, IP database %d\n", stats, geoip_calls, radix_nodes); | 385 | // int len = snprintf(line, LINE_MAX, "%32s geoip %d, IP database %d\n", stats, geoip_calls, radix_nodes); |
339 | int len = snprintf(line, LINE_MAX, "%32s address:port (protocol) network (packets)\n", stats); | 386 | int len = snprintf(line, LINE_MAX, "%32s address:port (protocol) network\n", stats); |
340 | adjust_line(line, len, cols); | 387 | adjust_line(line, len, cols); |
341 | printf("%s", line); | 388 | printf("%s", line); |
342 | 389 | ||
@@ -369,47 +416,67 @@ static void hnode_print(unsigned bw) { | |||
369 | bwline = print_bw(ptr->bytes / bwunit); | 416 | bwline = print_bw(ptr->bytes / bwunit); |
370 | 417 | ||
371 | const char *protocol = NULL; | 418 | const char *protocol = NULL; |
372 | if (ptr->port_src == 443 && ptr->protocol == 0x06) // TCP | 419 | if (ptr->port_src == 443 && ptr->protocol == 0x06) { // TCP |
373 | protocol = "(TLS)"; | 420 | protocol = "TLS"; |
374 | else if (ptr->port_src == 443 && ptr->protocol == 0x11) // UDP | 421 | stats_tls += ptr->pkts; |
375 | protocol = "(QUIC)"; | 422 | if (strstr(ptr->rnode->name, "DNS")) { |
376 | else if (ptr->port_src == 53) | 423 | protocol = "DoH"; |
377 | protocol = "(DNS)"; | 424 | stats_dns_doh += ptr->pkts; |
425 | } | ||
426 | |||
427 | } | ||
428 | else if (ptr->port_src == 443 && ptr->protocol == 0x11) { // UDP | ||
429 | protocol = "QUIC"; | ||
430 | stats_quic += ptr->pkts; | ||
431 | if (strstr(ptr->rnode->name, "DNS")) { | ||
432 | protocol = "DoQ"; | ||
433 | stats_dns_doq += ptr->pkts; | ||
434 | } | ||
435 | } | ||
436 | else if (ptr->port_src == 53) { | ||
437 | protocol = "DNS"; | ||
438 | stats_dns += ptr->pkts; | ||
439 | } | ||
378 | else if (ptr->port_src == 853) { | 440 | else if (ptr->port_src == 853) { |
379 | if (ptr->protocol == 0x06) | 441 | if (ptr->protocol == 0x06) { |
380 | protocol = "(DoT)"; | 442 | protocol = "DoT"; |
381 | else if (ptr->protocol == 0x11) | 443 | stats_dns_dot += ptr->pkts; |
382 | protocol = "(DoQ)"; | 444 | } |
445 | else if (ptr->protocol == 0x11) { | ||
446 | protocol = "DoQ"; | ||
447 | stats_dns_doq += ptr->pkts; | ||
448 | } | ||
383 | else | 449 | else |
384 | protocol = NULL; | 450 | protocol = NULL; |
385 | } | 451 | } |
386 | else if ((protocol = common_port(ptr->port_src)) != NULL) | 452 | else if ((protocol = common_port(ptr->port_src)) != NULL) { |
387 | ; | 453 | if (strcmp(protocol, "HTTP") == 0) |
454 | stats_http += ptr->pkts; | ||
455 | else if (strcmp(protocol, "Tor") == 0) | ||
456 | stats_tor += ptr->pkts; | ||
457 | else if (strcmp(protocol, "SSH") == 0) | ||
458 | stats_ssh += ptr->pkts; | ||
459 | } | ||
388 | else if (ptr->protocol == 0x11) | 460 | else if (ptr->protocol == 0x11) |
389 | protocol = "(UDP)"; | 461 | protocol = "UDP"; |
390 | else if (ptr->protocol == 0x06) | 462 | else if (ptr->protocol == 0x06) |
391 | protocol = "(TCP)"; | 463 | protocol = "TCP"; |
392 | 464 | ||
393 | if (protocol == NULL) | 465 | if (protocol == NULL) |
394 | protocol = ""; | 466 | protocol = ""; |
395 | if (ptr->port_src == 0) | 467 | if (ptr->port_src == 0) |
396 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d (ICMP) %s\n", | 468 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d (ICMP) %s\n", |
397 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->rnode->name); | 469 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->rnode->name); |
398 | else if (ptr->rnode->pkts > 1000000) | ||
399 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d:%u%s %s (%.01fM)\n", | ||
400 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->port_src, protocol, ptr->rnode->name, ((double) ptr->rnode->pkts) / 1000000); | ||
401 | else if (ptr->rnode->pkts > 1000) | ||
402 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d:%u%s %s (%.01fK)\n", | ||
403 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->port_src, protocol, ptr->rnode->name, ((double) ptr->rnode->pkts) / 1000); | ||
404 | else | 470 | else |
405 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d:%u%s %s (%u)\n", | 471 | len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d:%u (%s) %s\n", |
406 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->port_src, protocol, ptr->rnode->name, ptr->rnode->pkts); | 472 | bytes, bwline, PRINT_IP(ptr->ip_src), ptr->port_src, protocol, ptr->rnode->name); |
407 | adjust_line(line, len, cols); | 473 | adjust_line(line, len, cols); |
408 | printf("%s", line); | 474 | printf("%s", line); |
409 | 475 | ||
410 | if (ptr->bytes) | 476 | if (ptr->bytes) |
411 | ptr->ttl = DISPLAY_TTL; | 477 | ptr->ttl = DISPLAY_TTL; |
412 | ptr->bytes = 0; | 478 | ptr->bytes = 0; |
479 | ptr->pkts = 0; | ||
413 | prev = ptr; | 480 | prev = ptr; |
414 | } | 481 | } |
415 | else { | 482 | else { |
@@ -440,17 +507,10 @@ static void hnode_print(unsigned bw) { | |||
440 | 507 | ||
441 | 508 | ||
442 | void print_stats(void) { | 509 | void print_stats(void) { |
443 | printf("\nIP table: %d entries, %d unknown\n", radix_nodes, geoip_calls); | ||
444 | printf(" address network (packets)\n"); | ||
445 | radix_print(1); | ||
446 | printf("Packets: %u total, ICMP %u, DNS %u\n", stats_pkts, stats_icmp, stats_dns); | ||
447 | } | 510 | } |
448 | 511 | ||
449 | // trace rx traffic coming in | 512 | // trace rx traffic coming in |
450 | static void run_trace(void) { | 513 | static void run_trace(void) { |
451 | if (arg_netfilter) | ||
452 | logprintf("accumulating traffic for %d seconds\n", NETLOCK_INTERVAL); | ||
453 | |||
454 | // trace only rx ipv4 tcp and upd | 514 | // trace only rx ipv4 tcp and upd |
455 | int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); | 515 | int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); |
456 | int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); | 516 | int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); |
@@ -458,37 +518,45 @@ static void run_trace(void) { | |||
458 | if (s1 < 0 || s2 < 0 || s3 < 0) | 518 | if (s1 < 0 || s2 < 0 || s3 < 0) |
459 | errExit("socket"); | 519 | errExit("socket"); |
460 | 520 | ||
461 | unsigned start = time(NULL); | 521 | |
522 | int p1 = runprog(LIBDIR "/firejail/fnettrace-sni"); | ||
523 | if (p1 != -1) | ||
524 | printf("loading snitrace..."); | ||
525 | |||
526 | int p2 = runprog(LIBDIR "/firejail/fnettrace-dns --nolocal"); | ||
527 | if (p2 != -1) | ||
528 | printf("loading dnstrace..."); | ||
462 | unsigned last_print_traces = 0; | 529 | unsigned last_print_traces = 0; |
463 | unsigned last_print_remaining = 0; | ||
464 | unsigned char buf[MAX_BUF_SIZE]; | 530 | unsigned char buf[MAX_BUF_SIZE]; |
465 | unsigned bw = 0; // bandwidth calculations | 531 | unsigned bw = 0; // bandwidth calculations |
466 | 532 | ||
467 | while (1) { | 533 | while (1) { |
468 | unsigned end = time(NULL); | 534 | unsigned end = time(NULL); |
469 | if (arg_netfilter && end - start >= NETLOCK_INTERVAL) | ||
470 | break; | ||
471 | if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second | 535 | if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second |
472 | if (!arg_netfilter) | 536 | hnode_print(bw); |
473 | hnode_print(bw); | ||
474 | last_print_traces = end; | 537 | last_print_traces = end; |
475 | bw = 0; | 538 | bw = 0; |
476 | } | 539 | } |
477 | if (arg_netfilter && last_print_remaining != end) { | ||
478 | logprintf("."); | ||
479 | fflush(0); | ||
480 | last_print_remaining = end; | ||
481 | } | ||
482 | 540 | ||
483 | fd_set rfds; | 541 | fd_set rfds; |
484 | FD_ZERO(&rfds); | 542 | FD_ZERO(&rfds); |
543 | FD_SET(0, &rfds); | ||
544 | |||
485 | FD_SET(s1, &rfds); | 545 | FD_SET(s1, &rfds); |
486 | FD_SET(s2, &rfds); | 546 | FD_SET(s2, &rfds); |
487 | FD_SET(s3, &rfds); | 547 | FD_SET(s3, &rfds); |
488 | if (!arg_netfilter) | ||
489 | FD_SET(0, &rfds); | ||
490 | int maxfd = (s1 > s2) ? s1 : s2; | 548 | int maxfd = (s1 > s2) ? s1 : s2; |
491 | maxfd = (s3 > maxfd) ? s3 : maxfd; | 549 | maxfd = (s3 > maxfd) ? s3 : maxfd; |
550 | |||
551 | if (p1 != -1) { | ||
552 | FD_SET(p1, &rfds); | ||
553 | maxfd = (p1 > maxfd) ? p1 : maxfd; | ||
554 | } | ||
555 | |||
556 | if (p2 != -1) { | ||
557 | FD_SET(p2, &rfds); | ||
558 | maxfd = (p2 > maxfd) ? p2 : maxfd; | ||
559 | } | ||
492 | maxfd++; | 560 | maxfd++; |
493 | 561 | ||
494 | struct timeval tv; | 562 | struct timeval tv; |
@@ -508,10 +576,78 @@ static void run_trace(void) { | |||
508 | 576 | ||
509 | if (FD_ISSET(0, &rfds)) { | 577 | if (FD_ISSET(0, &rfds)) { |
510 | getchar(); | 578 | getchar(); |
511 | print_stats(); | 579 | printf("\n\nStats: %u packets\n", stats_pkts); |
580 | printf(" encrypted: TLS %u, QUIC %u, SSH %u, Tor %u\n", | ||
581 | stats_tls, stats_quic, stats_ssh, stats_tor); | ||
582 | printf(" unencrypted: HTTP %u\n", stats_http); | ||
583 | printf(" C&C backchannel: PING %u, DNS %u, DoH %u, DoT %u, DoQ %u\n", | ||
584 | stats_icmp_echo, stats_dns, stats_dns_doh, stats_dns_dot, stats_dns_doq); | ||
585 | printf("press any key to continue..."); | ||
586 | fflush(0); | ||
587 | |||
588 | getchar(); | ||
589 | printf("\n\nSNI log - time server-address SNI\n"); | ||
590 | print_sni(); | ||
512 | printf("press any key to continue..."); | 591 | printf("press any key to continue..."); |
513 | fflush(0); | 592 | fflush(0); |
593 | |||
594 | getchar(); | ||
595 | printf("\n\nDNS log - time server-address domain\n"); | ||
596 | print_dns(); | ||
597 | printf("press any key to continue..."); | ||
598 | fflush(0); | ||
599 | |||
514 | getchar(); | 600 | getchar(); |
601 | printf("\n\nIP table: %d addresses - server-address network (packets)\n", radix_nodes); | ||
602 | radix_print(1); | ||
603 | printf("press any key to continue..."); | ||
604 | fflush(0); | ||
605 | |||
606 | getchar(); | ||
607 | continue; | ||
608 | } | ||
609 | else if (FD_ISSET(p1, &rfds)) { | ||
610 | char buf[1024]; | ||
611 | ssize_t sz = read(p1, buf, 1024 - 1); | ||
612 | if (sz == -1) | ||
613 | errExit("error reading snitrace"); | ||
614 | if (sz == 0) { | ||
615 | fprintf(stderr, "Error: snitrace EOF!!!\n"); | ||
616 | p1 = -1; | ||
617 | } | ||
618 | if (strncmp(buf, "SNI trace", 9) == 0) | ||
619 | continue; | ||
620 | |||
621 | if (sz > LOG_RECORD_LEN) | ||
622 | sz = LOG_RECORD_LEN; | ||
623 | buf[sz] = '\0'; | ||
624 | strcpy(sni_table[sni_index].record, buf); | ||
625 | if (++sni_index >= SNIMAX) { | ||
626 | sni_index = 0; | ||
627 | *sni_table[sni_index].record = '\0'; | ||
628 | } | ||
629 | continue; | ||
630 | } | ||
631 | else if (FD_ISSET(p2, &rfds)) { | ||
632 | char buf[1024]; | ||
633 | ssize_t sz = read(p2, buf, 1024 - 1); | ||
634 | if (sz == -1) | ||
635 | errExit("error reading dnstrace"); | ||
636 | if (sz == 0) { | ||
637 | fprintf(stderr, "Error: dnstrace EOF!!!\n"); | ||
638 | p2 = -1; | ||
639 | } | ||
640 | if (strncmp(buf, "DNS trace", 9) == 0) | ||
641 | continue; | ||
642 | |||
643 | if (sz > LOG_RECORD_LEN) | ||
644 | sz = LOG_RECORD_LEN; | ||
645 | buf[sz] = '\0'; | ||
646 | strcpy(dns_table[dns_index].record, buf); | ||
647 | if (++dns_index >= DNSMAX) { | ||
648 | dns_index = 0; | ||
649 | *dns_table[dns_index].record = '\0'; | ||
650 | } | ||
515 | continue; | 651 | continue; |
516 | } | 652 | } |
517 | else if (FD_ISSET(s2, &rfds)) | 653 | else if (FD_ISSET(s2, &rfds)) |
@@ -557,10 +693,10 @@ static void run_trace(void) { | |||
557 | 693 | ||
558 | // stats | 694 | // stats |
559 | stats_pkts++; | 695 | stats_pkts++; |
560 | if (icmp) | 696 | if (icmp) { |
561 | stats_icmp++; | 697 | if (*(buf + hlen) == 0 || *(buf + hlen) == 8) |
562 | if (port_src == 53) | 698 | stats_icmp_echo++; |
563 | stats_dns++; | 699 | } |
564 | 700 | ||
565 | } | 701 | } |
566 | } | 702 | } |
@@ -572,142 +708,6 @@ static void run_trace(void) { | |||
572 | print_stats(); | 708 | print_stats(); |
573 | } | 709 | } |
574 | 710 | ||
575 | static char *filter_start = | ||
576 | "*filter\n" | ||
577 | ":INPUT DROP [0:0]\n" | ||
578 | ":FORWARD DROP [0:0]\n" | ||
579 | ":OUTPUT DROP [0:0]\n"; | ||
580 | |||
581 | // return 1 if error | ||
582 | static int print_filter(FILE *fp) { | ||
583 | if (dlist == NULL) | ||
584 | return 1; | ||
585 | fprintf(fp, "%s\n", filter_start); | ||
586 | fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n"); | ||
587 | fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n"); | ||
588 | fprintf(fp, "\n"); | ||
589 | |||
590 | int i; | ||
591 | for (i = 0; i < HMAX; i++) { | ||
592 | HNode *ptr = htable[i]; | ||
593 | while (ptr) { | ||
594 | // filter rules are targeting ip address, the port number is disregarded, | ||
595 | // so we look only at the first instance of an address | ||
596 | if (ptr->ip_instance == 1) { | ||
597 | char *protocol = (ptr->protocol == 6) ? "tcp" : "udp"; | ||
598 | fprintf(fp, "-A INPUT -s %d.%d.%d.%d -p %s -j ACCEPT\n", | ||
599 | PRINT_IP(ptr->ip_src), | ||
600 | protocol); | ||
601 | fprintf(fp, "-A OUTPUT -d %d.%d.%d.%d -p %s -j ACCEPT\n", | ||
602 | PRINT_IP(ptr->ip_src), | ||
603 | protocol); | ||
604 | fprintf(fp, "\n"); | ||
605 | } | ||
606 | ptr = ptr->hnext; | ||
607 | } | ||
608 | } | ||
609 | fprintf(fp, "COMMIT\n"); | ||
610 | |||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static char *flush_rules[] = { | ||
615 | "-P INPUT ACCEPT", | ||
616 | // "-P FORWARD DENY", | ||
617 | "-P OUTPUT ACCEPT", | ||
618 | "-F", | ||
619 | "-X", | ||
620 | // "-t nat -F", | ||
621 | // "-t nat -X", | ||
622 | // "-t mangle -F", | ||
623 | // "-t mangle -X", | ||
624 | // "iptables -t raw -F", | ||
625 | // "-t raw -X", | ||
626 | NULL | ||
627 | }; | ||
628 | |||
629 | static void deploy_netfilter(void) { | ||
630 | int rv; | ||
631 | char *cmd; | ||
632 | int i; | ||
633 | |||
634 | if (dlist == NULL) { | ||
635 | logprintf("Sorry, no network traffic was detected. The firewall was not configured.\n"); | ||
636 | return; | ||
637 | } | ||
638 | // find iptables command | ||
639 | char *iptables = NULL; | ||
640 | char *iptables_restore = NULL; | ||
641 | if (access("/sbin/iptables", X_OK) == 0) { | ||
642 | iptables = "/sbin/iptables"; | ||
643 | iptables_restore = "/sbin/iptables-restore"; | ||
644 | } | ||
645 | else if (access("/usr/sbin/iptables", X_OK) == 0) { | ||
646 | iptables = "/usr/sbin/iptables"; | ||
647 | iptables_restore = "/usr/sbin/iptables-restore"; | ||
648 | } | ||
649 | if (iptables == NULL || iptables_restore == NULL) { | ||
650 | fprintf(stderr, "Error: iptables command not found, netfilter not configured\n"); | ||
651 | exit(1); | ||
652 | } | ||
653 | |||
654 | // flush all netfilter rules | ||
655 | i = 0; | ||
656 | while (flush_rules[i]) { | ||
657 | char *cmd; | ||
658 | if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1) | ||
659 | errExit("asprintf"); | ||
660 | int rv = system(cmd); | ||
661 | (void) rv; | ||
662 | free(cmd); | ||
663 | i++; | ||
664 | } | ||
665 | |||
666 | // create temporary file | ||
667 | char fname[] = "/tmp/firejail-XXXXXX"; | ||
668 | int fd = mkstemp(fname); | ||
669 | if (fd == -1) { | ||
670 | fprintf(stderr, "Error: cannot create temporary configuration file\n"); | ||
671 | exit(1); | ||
672 | } | ||
673 | |||
674 | FILE *fp = fdopen(fd, "w"); | ||
675 | if (!fp) { | ||
676 | rv = unlink(fname); | ||
677 | (void) rv; | ||
678 | fprintf(stderr, "Error: cannot create temporary configuration file\n"); | ||
679 | exit(1); | ||
680 | } | ||
681 | print_filter(fp); | ||
682 | fclose(fp); | ||
683 | |||
684 | logprintf("\n\n"); | ||
685 | logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); | ||
686 | if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1) | ||
687 | errExit("asprintf"); | ||
688 | rv = system(cmd); | ||
689 | (void) rv; | ||
690 | free(cmd); | ||
691 | |||
692 | if (asprintf(&cmd, "cat %s", fname) == -1) | ||
693 | errExit("asprintf"); | ||
694 | rv = system(cmd); | ||
695 | (void) rv; | ||
696 | free(cmd); | ||
697 | logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); | ||
698 | |||
699 | // configuring | ||
700 | if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1) | ||
701 | errExit("asprintf"); | ||
702 | rv = system(cmd); | ||
703 | if (rv) | ||
704 | fprintf(stdout, "Warning: possible netfilter problem!"); | ||
705 | free(cmd); | ||
706 | |||
707 | rv = unlink(fname); | ||
708 | (void) rv; | ||
709 | logprintf("\nfirewall deployed\n"); | ||
710 | } | ||
711 | 711 | ||
712 | void logprintf(char *fmt, ...) { | 712 | void logprintf(char *fmt, ...) { |
713 | if (!arg_log) | 713 | if (!arg_log) |
@@ -733,14 +733,8 @@ static const char *const usage_str = | |||
733 | "Options:\n" | 733 | "Options:\n" |
734 | " --help, -? - this help screen\n" | 734 | " --help, -? - this help screen\n" |
735 | " --log=filename - netlocker logfile\n" | 735 | " --log=filename - netlocker logfile\n" |
736 | " --netfilter - build the firewall rules and commit them\n" | ||
737 | " --print-map - print IP map\n" | 736 | " --print-map - print IP map\n" |
738 | " --squash-map - compress IP map\n" | 737 | " --squash-map - compress IP map\n"; |
739 | " --tail - \"tail -f\" functionality\n" | ||
740 | "Examples:\n" | ||
741 | " # fnettrace - traffic trace\n" | ||
742 | " # fnettrace --netfilter --log=logfile - netlocker, dump output in logfile\n" | ||
743 | " # fnettrace --tail --log=logifile - similar to \"tail -f logfile\"\n"; | ||
744 | 738 | ||
745 | static void usage(void) { | 739 | static void usage(void) { |
746 | puts(usage_str); | 740 | puts(usage_str); |
@@ -775,7 +769,7 @@ int main(int argc, char **argv) { | |||
775 | return 0; | 769 | return 0; |
776 | } | 770 | } |
777 | else if (strncmp(argv[i], "--squash-map=", 13) == 0) { | 771 | else if (strncmp(argv[i], "--squash-map=", 13) == 0) { |
778 | if (i !=(argc - 1)) { | 772 | if (i != (argc - 1)) { |
779 | fprintf(stderr, "Error: please provide a map file\n"); | 773 | fprintf(stderr, "Error: please provide a map file\n"); |
780 | return 1; | 774 | return 1; |
781 | } | 775 | } |
@@ -798,10 +792,6 @@ int main(int argc, char **argv) { | |||
798 | fprintf(stderr, "static ip map: input %d, output %d\n", in, radix_nodes); | 792 | fprintf(stderr, "static ip map: input %d, output %d\n", in, radix_nodes); |
799 | return 0; | 793 | return 0; |
800 | } | 794 | } |
801 | else if (strcmp(argv[i], "--netfilter") == 0) | ||
802 | arg_netfilter = 1; | ||
803 | else if (strcmp(argv[i], "--tail") == 0) | ||
804 | arg_tail = 1; | ||
805 | else if (strncmp(argv[i], "--log=", 6) == 0) | 795 | else if (strncmp(argv[i], "--log=", 6) == 0) |
806 | arg_log = argv[i] + 6; | 796 | arg_log = argv[i] + 6; |
807 | else { | 797 | else { |
@@ -810,19 +800,6 @@ int main(int argc, char **argv) { | |||
810 | } | 800 | } |
811 | } | 801 | } |
812 | 802 | ||
813 | // tail | ||
814 | if (arg_tail) { | ||
815 | if (!arg_log) { | ||
816 | fprintf(stderr, "Error: no log file\n"); | ||
817 | usage(); | ||
818 | exit(1); | ||
819 | } | ||
820 | |||
821 | tail(arg_log); | ||
822 | sleep(5); | ||
823 | exit(0); | ||
824 | } | ||
825 | |||
826 | if (getuid() != 0) { | 803 | if (getuid() != 0) { |
827 | fprintf(stderr, "Error: you need to be root to run this program\n"); | 804 | fprintf(stderr, "Error: you need to be root to run this program\n"); |
828 | return 1; | 805 | return 1; |
@@ -838,25 +815,10 @@ int main(int argc, char **argv) { | |||
838 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); | 815 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); |
839 | 816 | ||
840 | ansi_clrscr(); | 817 | ansi_clrscr(); |
841 | if (arg_netfilter) | 818 | char *fname = LIBDIR "/firejail/static-ip-map"; |
842 | logprintf("starting network lockdown\n"); | 819 | load_hostnames(fname); |
843 | else { | ||
844 | char *fname = LIBDIR "/firejail/static-ip-map"; | ||
845 | load_hostnames(fname); | ||
846 | } | ||
847 | 820 | ||
848 | run_trace(); | 821 | run_trace(); |
849 | if (arg_netfilter) { | ||
850 | // TCP path MTU discovery will not work properly since the firewall drops all ICMP packets | ||
851 | // Instead, we use iPacketization Layer PMTUD (RFC 4821) support in Linux kernel | ||
852 | int rv = system("echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing"); | ||
853 | (void) rv; | ||
854 | |||
855 | deploy_netfilter(); | ||
856 | sleep(3); | ||
857 | if (arg_log) | ||
858 | unlink(arg_log); | ||
859 | } | ||
860 | 822 | ||
861 | return 0; | 823 | return 0; |
862 | } | 824 | } |