aboutsummaryrefslogtreecommitdiffstats
path: root/src/fnettrace/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fnettrace/main.c')
-rw-r--r--src/fnettrace/main.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c
new file mode 100644
index 000000000..f036d0c9e
--- /dev/null
+++ b/src/fnettrace/main.c
@@ -0,0 +1,433 @@
1#include "fnettrace.h"
2#define MAX_BUF_SIZE (64 * 1024)
3
4static int arg_netfilter = 0;
5static char *arg_log = NULL;
6
7typedef struct hlist_t {
8 struct hlist_t *next;
9 uint32_t ip_src;
10 uint32_t ip_dst;
11 uint16_t port_src;
12 uint64_t bytes;
13 int instance;
14#define MAX_TTL 20 // 20 * DISPLAY_INTERVAL = 1 minute
15 short ttl;
16 uint8_t protocol;
17} HList;
18
19#define HMAX 256
20HList *htable[HMAX] = {NULL};
21static int htable_empty = 1;
22
23static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16_t port_src, uint64_t bytes) {
24 uint8_t h = hash(ip_src);
25 htable_empty = 0;
26
27 // find
28 int instance = 0;
29 HList *ptr = htable[h];
30 while (ptr) {
31 if (ptr->ip_src == ip_src) {
32 instance++;
33 if (ptr->ip_dst == ip_dst && ptr->port_src == port_src && ptr->protocol == protocol) {
34 ptr->bytes += bytes;
35 ptr->ttl = MAX_TTL;
36 return;
37 }
38 }
39 ptr = ptr->next;
40 }
41
42 HList *hnew = malloc(sizeof(HList));
43 hnew->ip_src = ip_src;
44 hnew->ip_dst = ip_dst;
45 hnew->port_src = port_src;
46 hnew->protocol = protocol;
47 hnew->next = NULL;
48 hnew->bytes = bytes;
49 hnew->ttl = MAX_TTL;
50 hnew->instance = instance + 1;
51 if (htable[h] == NULL)
52 htable[h] = hnew;
53 else {
54 hnew->next = htable[h];
55 htable[h] = hnew;
56 }
57
58 ansi_clrline(1);
59 logprintf(" %u.%u.%u.%u\n", PRINT_IP(hnew->ip_src));
60}
61
62// remove entries with a ttl <= 0
63static void hlist_clean_ttl() {
64 if (htable_empty)
65 return;
66
67 int i;
68 for (i = 0; i < HMAX; i++) {
69 HList *ptr = htable[i];
70 HList *parent = NULL;
71 while (ptr) {
72 if (--ptr->ttl <= 0) {
73 HList *tmp = ptr;
74 ptr = ptr->next;
75 if (parent)
76 parent->next = ptr;
77 else
78 htable[i] = ptr;
79 free(tmp);
80 }
81 else {
82 parent = ptr;
83 ptr = ptr->next;
84 }
85 }
86 }
87}
88
89static void hlist_print() {
90 ansi_clrscr(0);
91 if (htable_empty)
92 return;
93 if (arg_netfilter)
94 printf("\n\n");
95 static int clear_cnt = 0;
96
97 int i;
98 int cnt = 0;
99 int cnt_printed = 0;
100 for (i = 0; i < HMAX; i++) {
101 HList *ptr = htable[i];
102 while (ptr) {
103 if (ptr->bytes) {
104 cnt_printed++;
105 char ip_src[30];
106 sprintf(ip_src, "%u.%u.%u.%u:%u", PRINT_IP(ptr->ip_src), ptr->port_src);
107 char ip_dst[30];
108 sprintf(ip_dst, "%u.%u.%u.%u", PRINT_IP(ptr->ip_dst));
109 printf("%-25s => %-25s\t%s:",
110 ip_src,
111 ip_dst,
112 (ptr->protocol == 6)? "TCP": "UDP");
113
114 if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 2)) // > 2 KB/second
115 printf(" %lu KB/sec\n",
116 ptr->bytes / (DISPLAY_INTERVAL * 1024));
117 else
118 printf(" %lu B/sec\n",
119 ptr->bytes / DISPLAY_INTERVAL);
120 ptr->bytes = 0;
121 }
122
123 ptr = ptr->next;
124 cnt++;
125 }
126 }
127
128 if (cnt_printed < 7) {
129 for (i = 0; i < 7 - cnt_printed; i++)
130 printf("\n");
131 }
132
133 if (!arg_netfilter) {
134 printf("(%d %s in the last one minute)\n", cnt, (cnt == 1)? "stream": "streams");
135 hlist_clean_ttl();
136 }
137}
138
139static void run_trace(void) {
140 logprintf("accumulating traffic for %d seconds...\n", NETLOCK_INTERVAL);
141
142 // trace only rx ipv4 tcp and upd
143 int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
144 int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
145 if (s1 < 0 || s2 < 0)
146 errExit("socket");
147
148 unsigned start = time(NULL);
149 unsigned last_print_traces = 0;
150 unsigned last_print_remaining = 0;
151 unsigned char buf[MAX_BUF_SIZE];
152 int progress_cnt = 0;
153 while (1) {
154 unsigned end = time(NULL);
155 if (arg_netfilter && end - start >= NETLOCK_INTERVAL) {
156 ansi_clrline(1);
157 break;
158 }
159 if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second
160 hlist_print();
161 last_print_traces = end;
162 }
163 if (arg_netfilter && last_print_remaining != end) {
164 ansi_clrline(1);
165 int secs = NETLOCK_INTERVAL - (end - start);
166 logprintf("%d %s remaining ", secs, (secs == 1)? "second": "seconds");
167 last_print_remaining = end;
168 }
169
170 fd_set rfds;
171 FD_ZERO(&rfds);
172 FD_SET(s1, &rfds);
173 FD_SET(s2, &rfds);
174 int maxfd = (s1 > s2) ? s1 : s2;
175 maxfd++;
176 struct timeval tv;
177 tv.tv_sec = 1;
178 tv.tv_usec = 0;
179 int rv = select(maxfd, &rfds, NULL, NULL, &tv);
180 if (rv < 0)
181 errExit("select");
182 else if (rv == 0)
183 continue;
184
185
186
187 int sock = (FD_ISSET(s1, &rfds)) ? s1 : s2;
188
189 unsigned char buf[MAX_BUF_SIZE];
190 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
191 if (bytes >= 20) { // size of IP header
192 // filter out loopback traffic
193 if (buf[12] != 127) {
194 uint32_t ip_src;
195 memcpy(&ip_src, buf + 12, 4);
196 ip_src = ntohl(ip_src);
197
198 uint32_t ip_dst;
199 memcpy(&ip_dst, buf + 16, 4);
200 ip_dst = ntohl(ip_dst);
201
202 uint8_t hlen = (buf[0] & 0x0f) * 4;
203 uint16_t port_src;
204 memcpy(&port_src, buf + hlen, 2);
205 port_src = ntohs(port_src);
206
207 hlist_add(ip_src, ip_dst, buf[9], port_src, (uint64_t) bytes);
208 }
209 }
210 }
211
212 close(s1);
213 close(s2);
214}
215
216static char *filter_start =
217"*filter\n"
218":INPUT DROP [0:0]\n"
219":FORWARD DROP [0:0]\n"
220":OUTPUT DROP [0:0]\n";
221
222// return 1 if error
223static int print_filter(FILE *fp) {
224 if (htable_empty)
225 return 1;
226 fprintf(fp, "%s\n", filter_start);
227 fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n");
228 fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n");
229 fprintf(fp, "\n");
230
231 int i;
232 for (i = 0; i < HMAX; i++) {
233 HList *ptr = htable[i];
234 while (ptr) {
235 if (ptr->instance == 1) {
236 char *protocol = (ptr->protocol == 6)? "tcp": "udp";
237 fprintf(fp, "-A INPUT -s %u.%u.%u.%u -sport %u -p %s -j ACCEPT\n",
238 PRINT_IP(ptr->ip_src),
239 ptr->port_src,
240 protocol);
241 fprintf(fp, "-A OUTPUT -d %u.%u.%u.%u -dport %u -p %s -j ACCEPT\n",
242 PRINT_IP(ptr->ip_src),
243 ptr->port_src,
244 protocol);
245 fprintf(fp, "\n");
246 }
247 ptr = ptr->next;
248 }
249 }
250 fprintf(fp, "COMMIT\n");
251
252 return 0;
253}
254
255static char *flush_rules[] = {
256 "-P INPUT ACCEPT",
257 "-P FORWARD ACCEPT",
258 "-P OUTPUT ACCEPT",
259 "-F",
260 "-X",
261 "-t nat -F",
262 "-t nat -X",
263 "-t mangle -F",
264 "-t mangle -X",
265 "iptables -t raw -F",
266 "-t raw -X",
267 NULL
268};
269
270static void flush_netfilter(void) {
271 // find iptables command
272 struct stat s;
273 char *iptables = NULL;
274 if (stat("/sbin/iptables", &s) == 0)
275 iptables = "/sbin/iptables";
276 else if (stat("/usr/sbin/iptables", &s) == 0)
277 iptables = "/usr/sbin/iptables";
278 if (iptables == NULL) {
279 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
280 exit(1);
281 }
282
283 int i = 0;
284 while (flush_rules[i]) {
285 char *cmd;
286 if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1)
287 errExit("asprintf");
288 int rv = system(cmd);
289 (void) rv;
290 free(cmd);
291 i++;
292 }
293}
294
295static void deploy_netfilter(void) {
296 int rv;
297 char *cmd;
298
299 // create temporary file
300 char fname[] = "/tmp/firejail-XXXXXX";
301 int fd = mkstemp(fname);
302 if (fd == -1) {
303 fprintf(stderr, "Error: cannot create temporary configuration file\n");
304 exit(1);
305 }
306
307 FILE* fp = fdopen(fd, "w");
308 if (!fp) {
309 rv = unlink(fname);
310 (void) rv;
311 fprintf(stderr, "Error: cannot create temporary configuration file\n");
312 exit(1);
313 }
314 print_filter(fp);
315 fclose(fp);
316
317 if (arg_log) {
318 logprintf("\n");
319 logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
320 if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1)
321 errExit("asprintf");
322 rv = system(cmd);
323 (void) rv;
324 free(cmd);
325 logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
326 }
327
328 // find iptables command
329 struct stat s;
330 char *iptables = NULL;
331 char *iptables_restore = NULL;
332 if (stat("/sbin/iptables", &s) == 0) {
333 iptables = "/sbin/iptables";
334 iptables_restore = "/sbin/iptables-restore";
335 }
336 else if (stat("/usr/sbin/iptables", &s) == 0) {
337 iptables = "/usr/sbin/iptables";
338 iptables_restore = "/usr/sbin/iptables-restore";
339 }
340 if (iptables == NULL || iptables_restore == NULL) {
341 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
342 rv = unlink(fname);
343 (void) rv;
344 exit(1);
345 }
346
347 // configuring
348 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
349 errExit("asprintf");
350 rv = system(cmd);
351 if (rv)
352 fprintf(stdout, "Warning: possible netfilter problem!");
353 free(cmd);
354
355 sleep(1);
356 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
357 errExit("asprintf");
358 rv = system(cmd);
359 free(cmd);
360
361 printf("Current firewall configuration:\n\n");
362 if (asprintf(&cmd, "%s -vL -n", iptables) == -1)
363 errExit("asprintf");
364 rv = system(cmd);
365
366 rv = unlink(fname);
367 (void) rv;
368 logprintf("\nfirewall deployed\n");
369}
370
371void logprintf(char* fmt, ...) {
372 if (!arg_log)
373 return;
374
375 FILE *fp = fopen(arg_log, "a");
376 if (fp) { // disregard if error
377 va_list args;
378 va_start(args,fmt);
379 vfprintf(fp, fmt, args);
380 va_end(args);
381 fclose(fp);
382 }
383}
384
385static void usage(void) {
386 printf("Usage: fnetlock [OPTIONS]\n");
387 printf("Options:\n");
388 printf(" --help, -? - this help screen\n");
389 printf(" --netfilter - build the firewall rules and commit them.\n");
390 printf(" --log=filename - logfile\n");
391 printf("\n");
392}
393
394int main(int argc, char **argv) {
395 int i;
396 printf("\n\n");
397
398 if (getuid() != 0) {
399 fprintf(stderr, "Error: you need to be root to run this program\n");
400 return 1;
401 }
402
403 for (i = 1; i < argc; i++) {
404 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
405 usage();
406 return 0;
407 }
408 else if (strcmp(argv[i], "--netfilter") == 0)
409 arg_netfilter = 1;
410 else if (strncmp(argv[i], "--log=", 6) == 0)
411 arg_log = argv[i] + 6;
412 else {
413 fprintf(stderr, "Error: invalid argument\n");
414 return 1;
415 }
416 }
417
418 if (arg_netfilter) {
419 logprintf("starting network lockdown\n");
420 flush_netfilter();
421 }
422
423 ansi_clrscr(0);
424 run_trace();
425 if (arg_netfilter) {
426 deploy_netfilter();
427 sleep(3);
428 if (arg_log)
429 unlink(arg_log);
430 }
431
432 return 0;
433}