cosmetics
[tomato.git] / release / src / router / httpd / ctnf.c
blob80d13bafc369968a81cb7b35adf72742b543d7e6
1 /*
3 Tomato Firmware
4 Copyright (C) 2006-2009 Jonathan Zarate
6 */
8 #include "tomato.h"
10 #include <ctype.h>
11 #include <sys/sysinfo.h>
12 #include <sys/stat.h>
13 #include <sys/ioctl.h>
14 #include <net/if.h>
15 #include <fcntl.h>
16 #include <netinet/in.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <arpa/inet.h>
20 #include <time.h>
21 #include <dirent.h>
23 #define IP6_PREFIX_NOT_MATCH( a, b, bits ) (memcmp(&a.s6_addr[0], &b.s6_addr[0], bits/8) != 0 || ( bits % 8 && (a.s6_addr[bits/8] ^ b.s6_addr[bits/8]) >> (8-(bits % 8)) ))
26 static void ctvbuf(FILE *f)
28 int n;
29 struct sysinfo si;
30 // meminfo_t mem;
32 #if 1
33 const char *p;
35 if ((p = nvram_get("ct_max")) != NULL) {
36 n = atoi(p);
37 if (n == 0) n = 2048;
38 else if (n < 1024) n = 1024;
39 else if (n > 10240) n = 10240;
41 else {
42 n = 2048;
44 #else
45 char s[64];
47 if (f_read_string("/proc/sys/net/ipv4/ip_conntrack_max", s, sizeof(s)) > 0) n = atoi(s);
48 else n = 1024;
49 if (n < 1024) n = 1024;
50 else if (n > 10240) n = 10240;
51 #endif
53 n *= 170; // avg tested
55 // get_memory(&mem);
56 // if (mem.maxfreeram < (n + (64 * 1024))) n = mem.maxfreeram - (64 * 1024);
58 sysinfo(&si);
59 if (si.freeram < (n + (64 * 1024))) n = si.freeram - (64 * 1024);
61 // cprintf("free: %dK, buffer: %dK\n", si.freeram / 1024, n / 1024);
63 if (n > 4096) {
64 // n =
65 setvbuf(f, NULL, _IOFBF, n);
66 // cprintf("setvbuf = %d\n", n);
70 void asp_ctcount(int argc, char **argv)
72 static const char *states[10] = {
73 "NONE", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT",
74 "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN" };
75 int count[13]; // tcp(10) + udp(2) + total(1) = 13 / max classes = 10
76 FILE *f;
77 char s[512];
78 char *p;
79 char *t;
80 int i;
81 int n;
82 int mode;
83 unsigned long rip;
84 unsigned long lan;
85 unsigned long mask;
87 if (argc != 1) return;
89 #if defined(TCONFIG_IPV6) && defined(LINUX26)
90 char src[INET6_ADDRSTRLEN];
91 char dst[INET6_ADDRSTRLEN];
92 struct in6_addr rip6;
93 struct in6_addr lan6;
94 struct in6_addr in6;
95 int lan6_prefix_len;
97 lan6_prefix_len = nvram_get_int("ipv6_prefix_length");
98 if (ipv6_enabled()) {
99 inet_pton(AF_INET6, nvram_safe_get("ipv6_prefix"), &lan6);
100 ipv6_router_address(&rip6);
102 #endif
104 mode = atoi(argv[0]);
106 memset(count, 0, sizeof(count));
108 #if defined(TCONFIG_IPV6) && defined(LINUX26)
109 if ((f = fopen("/proc/net/nf_conntrack", "r")) != NULL) {
110 #else
111 if ((f = fopen("/proc/net/ip_conntrack", "r")) != NULL) {
112 #endif
113 ctvbuf(f); // if possible, read in one go
115 if (nvram_match("t_hidelr", "1")) {
116 mask = inet_addr(nvram_safe_get("lan_netmask"));
117 rip = inet_addr(nvram_safe_get("lan_ipaddr"));
118 lan = rip & mask;
120 else {
121 rip = lan = mask = 0;
124 while (fgets(s, sizeof(s), f)) {
125 #if defined(TCONFIG_IPV6) && defined(LINUX26)
126 if (strncmp(s, "ipv4", 4) == 0) {
127 t = s + 11;
128 #else
129 t = s;
130 #endif
131 if (rip != 0) {
133 // src=x.x.x.x dst=x.x.x.x // DIR_ORIGINAL
134 if ((p = strstr(t + 14, "src=")) == NULL) continue;
135 if ((inet_addr(p + 4) & mask) == lan) {
136 if ((p = strstr(p + 13, "dst=")) == NULL) continue;
137 if (inet_addr(p + 4) == rip) continue;
140 #if defined(TCONFIG_IPV6) && defined(LINUX26)
142 else if (strncmp(s, "ipv6", 4) == 0) {
143 t = s + 12;
145 if (rip != 0) {
146 if ((p = strstr(t + 14, "src=")) == NULL) continue;
147 if (sscanf(p, "src=%s dst=%s", src, dst) != 2) continue;
149 if (inet_pton(AF_INET6, src, &in6) <= 0) continue;
150 inet_ntop(AF_INET6, &in6, src, sizeof(src));
152 if (!IP6_PREFIX_NOT_MATCH(lan6, in6, lan6_prefix_len)) continue;
155 else {
156 continue; // another proto family?!
158 #endif
160 if (mode == 0) {
161 // count connections per state
162 if (strncmp(t, "tcp", 3) == 0) {
163 for (i = 9; i >= 0; --i) {
164 if (strstr(s, states[i]) != NULL) {
165 count[i]++;
166 break;
170 else if (strncmp(t, "udp", 3) == 0) {
171 if (strstr(s, "[ASSURED]") != NULL) {
172 count[11]++;
174 else {
175 count[10]++;
178 count[12]++;
180 else {
181 // count connections per mark
182 if ((p = strstr(s, " mark=")) != NULL) {
183 n = atoi(p + 6) & 0xFF;
184 if (n <= 10) count[n]++;
189 fclose(f);
192 if (mode == 0) {
193 p = s;
194 for (i = 0; i < 12; ++i) {
195 p += sprintf(p, ",%d", count[i]);
197 web_printf("\nconntrack = [%d%s];\n", count[12], s);
199 else {
200 p = s;
201 for (i = 1; i < 11; ++i) {
202 p += sprintf(p, ",%d", count[i]);
204 web_printf("\nnfmarks = [%d%s];\n", count[0], s);
208 void asp_ctdump(int argc, char **argv)
210 FILE *f;
211 char s[512];
212 char *p, *q;
213 int x;
214 int mark;
215 int rule;
216 int findmark;
217 unsigned int proto;
218 unsigned int time;
219 #if defined(TCONFIG_IPV6) && defined(LINUX26)
220 unsigned int family;
221 char src[INET6_ADDRSTRLEN];
222 char dst[INET6_ADDRSTRLEN];
223 #else
224 const unsigned int family = 2;
225 char src[INET_ADDRSTRLEN];
226 char dst[INET_ADDRSTRLEN];
227 #endif
228 char sport[16];
229 char dport[16];
230 char byteso[16];
231 char bytesi[16];
232 int dir_reply;
234 unsigned long rip;
235 unsigned long lan;
236 unsigned long mask;
237 char comma;
239 if (argc != 1) return;
241 findmark = atoi(argv[0]);
243 mask = inet_addr(nvram_safe_get("lan_netmask"));
244 rip = inet_addr(nvram_safe_get("lan_ipaddr"));
245 lan = rip & mask;
247 #if defined(TCONFIG_IPV6) && defined(LINUX26)
248 struct in6_addr rip6;
249 struct in6_addr lan6;
250 struct in6_addr in6;
251 int lan6_prefix_len;
253 lan6_prefix_len = nvram_get_int("ipv6_prefix_length");
254 if (ipv6_enabled()) {
255 inet_pton(AF_INET6, nvram_safe_get("ipv6_prefix"), &lan6);
256 ipv6_router_address(&rip6);
258 #endif
260 if (nvram_match("t_hidelr", "0")) rip = 0; // hide lan -> router?
264 /proc/net/nf_conntrack prefix (compared to ip_conntrack):
265 "ipvx" + 5 spaces + "2" or "10" + 1 space
267 add bytes out/in to table
271 web_puts("\nctdump = [");
272 comma = ' ';
273 #if defined(TCONFIG_IPV6) && defined(LINUX26)
274 if ((f = fopen("/proc/net/nf_conntrack", "r")) != NULL) {
275 #else
276 if ((f = fopen("/proc/net/ip_conntrack", "r")) != NULL) {
277 #endif
278 ctvbuf(f);
279 while (fgets(s, sizeof(s), f)) {
280 dir_reply = 0;
281 if ((p = strstr(s, " mark=")) == NULL) continue;
282 mark = atoi(p + 6);
283 rule = (mark >> 20) & 0xFF;
284 if ((mark &= 0xFF) > 10) mark = 0;
285 if ((findmark != -1) && (mark != findmark)) continue;
286 #if defined(TCONFIG_IPV6) && defined(LINUX26)
287 if (sscanf(s, "%*s %u %*s %u %u", &family, &proto, &time) != 3) continue;
288 if ((p = strstr(s + 25, "src=")) == NULL) continue;
289 #else
290 if (sscanf(s, "%*s %u %u", &proto, &time) != 2) continue;
291 if ((p = strstr(s + 14, "src=")) == NULL) continue;
292 #endif
293 if (sscanf(p, "src=%s dst=%s %n", src, dst, &x) != 2) continue;
294 p += x;
296 if ((proto == 6) || (proto == 17)) {
297 if (sscanf(p, "sport=%s dport=%s %*s bytes=%s %n", sport, dport, byteso, &x) != 3) continue;
298 p += x;
299 if ((q = strstr(p, "bytes=")) == NULL) continue;
300 if (sscanf(q, "bytes=%s", bytesi) != 1) continue;
302 else {
303 sport[0] = 0;
304 dport[0] = 0;
305 byteso[0] = 0;
306 bytesi[0] = 0;
309 switch (family) {
310 case 2:
311 if ((inet_addr(src) & mask) != lan) {
312 dir_reply = 1;
313 // de-nat
314 if ((p = strstr(p, "src=")) == NULL) continue;
315 if ((proto == 6) || (proto == 17)) {
316 if (sscanf(p, "src=%s dst=%s sport=%s dport=%s", dst, src, dport, sport) != 4) continue; // intentionally backwards
318 else {
319 if (sscanf(p, "src=%s dst=%s", dst, src) != 2) continue;
322 else if (rip != 0 && inet_addr(dst) == rip) continue;
323 break;
324 #if defined(TCONFIG_IPV6) && defined(LINUX26)
325 case 10:
326 if (inet_pton(AF_INET6, src, &in6) <= 0) continue;
327 inet_ntop(AF_INET6, &in6, src, sizeof(src));
329 if (IP6_PREFIX_NOT_MATCH(lan6, in6, lan6_prefix_len))
330 dir_reply = 1;
332 if (inet_pton(AF_INET6, dst, &in6) <= 0) continue;
333 inet_ntop(AF_INET6, &in6, dst, sizeof(dst));
335 if (dir_reply == 0 && rip != 0 && (IN6_ARE_ADDR_EQUAL(&rip6, &in6)))
336 continue;
337 break;
338 #endif
341 if (dir_reply == 1) {
342 web_printf("%c[%u,%u,'%s','%s','%s','%s','%s','%s',%d,%d]", comma, proto, time, dst, src, dport, sport, bytesi, byteso, mark, rule );
344 else {
345 web_printf("%c[%u,%u,'%s','%s','%s','%s','%s','%s',%d,%d]", comma, proto, time, src, dst, sport, dport, byteso, bytesi, mark, rule );
347 comma = ',';
350 web_puts("];\n");
353 void asp_ctrate(int argc, char **argv)
355 unsigned int a_time, a_proto;
356 unsigned int a_bytes_o, a_bytes_i;
357 char a_sport[16];
358 char a_dport[16];
360 char *p, *q;
361 int x;
362 int len;
364 #if defined(TCONFIG_IPV6) && defined(LINUX26)
365 unsigned int a_fam, b_fam;
366 char a_src[INET6_ADDRSTRLEN];
367 char a_dst[INET6_ADDRSTRLEN];
368 #else
369 const unsigned int a_fam = 2;
370 char a_src[INET_ADDRSTRLEN];
371 char a_dst[INET_ADDRSTRLEN];
372 #endif
374 unsigned int b_time, b_proto;
375 unsigned int b_bytes_o, b_bytes_i;
377 char sa[512];
378 char sb[512];
380 FILE *a;
381 FILE *b;
383 long b_pos;
385 int delay;
386 int thres;
388 long outbytes;
389 long inbytes;
390 int n;
391 char comma;
393 int dir_reply;
395 unsigned long rip;
396 unsigned long lan;
397 unsigned long mask;
399 mask = inet_addr(nvram_safe_get("lan_netmask"));
400 rip = inet_addr(nvram_safe_get("lan_ipaddr"));
401 lan = rip & mask;
403 #if defined(TCONFIG_IPV6) && defined(LINUX26)
404 struct in6_addr rip6;
405 struct in6_addr lan6;
406 struct in6_addr in6;
407 int lan6_prefix_len;
409 lan6_prefix_len = nvram_get_int("ipv6_prefix_length");
410 if (ipv6_enabled()) {
411 inet_pton(AF_INET6, nvram_safe_get("ipv6_prefix"), &lan6);
412 ipv6_router_address(&rip6);
414 #endif
416 if (nvram_match("t_hidelr", "0")) rip = 0; // hide lan -> router?
418 web_puts("\nctrate = [");
419 comma = ' ';
421 #if defined(TCONFIG_IPV6) && defined(LINUX26)
422 const char name[] = "/proc/net/nf_conntrack";
423 #else
424 const char name[] = "/proc/net/ip_conntrack";
425 #endif
427 if (argc != 2) return;
429 delay = atoi(argv[0]);
430 thres = atoi(argv[1]) * delay;
432 if ((a = fopen(name, "r")) == NULL) return;
433 if ((b = tmpfile()) == NULL) return;
435 #ifdef LINUX26
436 size_t count;
437 char *buffer;
439 if ((buffer=(char *)malloc(1024)) == NULL) {
440 // can't allocate memory? clean-up and exit
441 web_puts("];\n");
442 fclose(a);
443 fclose(b);
444 return;
447 while (!feof(a)) {
448 count = fread(buffer, 1, 1024, a);
449 fwrite(buffer, 1, count, b);
451 // release/deallocate buffer
452 free(buffer);
453 #else
454 while (fgets(sa, sizeof(sa), a)) {
455 fputs(sa,b);
457 #endif
459 rewind(b);
460 rewind(a);
462 usleep(1000000 * (int)delay);
464 #define MAX_SEARCH 10
466 // a = current, b = previous
467 while (fgets(sa, sizeof(sa), a)) {
468 #if defined(TCONFIG_IPV6) && defined(LINUX26)
469 if (sscanf(sa, "%*s %u %*s %u %u", &a_fam, &a_proto, &a_time) != 3) continue;
470 #else
471 if (sscanf(sa, "%*s %u %u", &a_proto, &a_time) != 2) continue;
472 #endif
473 if ((a_proto != 6) && (a_proto != 17)) continue;
474 if ((p = strstr(sa, "src=")) == NULL) continue;
476 if (sscanf(p, "src=%s dst=%s sport=%s dport=%s%n %*s bytes=%u %n", a_src, a_dst, a_sport, a_dport, &len, &a_bytes_o, &x) != 5) continue;
478 if ((q = strstr(p+x, "bytes=")) == NULL) continue;
479 if (sscanf(q, "bytes=%u", &a_bytes_i) != 1) continue;
481 dir_reply = 0;
483 switch(a_fam) {
484 case 2:
485 if ((inet_addr(a_src) & mask) != lan) dir_reply = 1;
486 else if (rip != 0 && inet_addr(a_dst) == rip) continue;
487 break;
488 #if defined(TCONFIG_IPV6) && defined(LINUX26)
489 case 10:
490 if (inet_pton(AF_INET6, a_src, &in6) <= 0) continue;
491 inet_ntop(AF_INET6, &in6, a_src, sizeof(a_src));
493 if (IP6_PREFIX_NOT_MATCH(lan6, in6, lan6_prefix_len))
494 dir_reply = 1;
496 if (inet_pton(AF_INET6, a_dst, &in6) <= 0) continue;
497 inet_ntop(AF_INET6, &in6, a_dst, sizeof(a_dst));
499 if (dir_reply == 0 && rip != 0 && (IN6_ARE_ADDR_EQUAL(&rip6, &in6)))
500 continue;
501 break;
502 default:
503 continue;
504 #endif
507 b_pos = ftell(b);
508 n = 0;
509 while (fgets(sb, sizeof(sb), b) && ++n < MAX_SEARCH) {
510 #if defined(TCONFIG_IPV6) && defined(LINUX26)
511 if (sscanf(sb, "%*s %u %*s %u %u", &b_fam, &b_proto, &b_time) != 3) continue;
512 if ((b_fam != a_fam)) continue;
513 #else
514 if (sscanf(sb, "%*s %u %u", &b_proto, &b_time) != 2) continue;
515 #endif
516 if ((b_proto != a_proto)) continue;
517 if ((q = strstr(sb, "src=")) == NULL) continue;
519 if (strncmp(p, q, (size_t)len)) continue;
521 // Ok, they should be the same now. Grab the byte counts.
522 if ((q = strstr(q+len, "bytes=")) == NULL) continue;
523 if (sscanf(q, "bytes=%u", &b_bytes_o) != 1) continue;
525 if ((q = strstr(q+len, "bytes=")) == NULL) continue;
526 if (sscanf(q, "bytes=%u", &b_bytes_i) != 1) continue;
528 break;
531 if (feof(b) || n >= MAX_SEARCH) {
532 // Assume this is a new connection
533 b_bytes_o = 0;
534 b_bytes_i = 0;
535 b_time = 0;
537 // Reset the search so we don't miss anything
538 fseek(b, b_pos, SEEK_SET);
539 n = -n;
542 outbytes = ((long)a_bytes_o - (long)b_bytes_o);
543 inbytes = ((long)a_bytes_i - (long)b_bytes_i);
545 if ((outbytes < thres) && (inbytes < thres)) continue;
547 if (dir_reply == 1 && a_fam == 2) {
548 // de-nat
549 if ((q = strstr(p+x, "src=")) == NULL) continue;
550 if (sscanf(q, "src=%s dst=%s sport=%s dport=%s", a_dst, a_src, a_dport, a_sport) != 4) continue;
553 if (dir_reply == 1) {
554 web_printf("%c[%u,'%s','%s','%s','%s',%li,%li]",
555 comma, a_proto, a_dst, a_src, a_dport, a_sport, inbytes, outbytes );
557 else {
558 web_printf("%c[%u,'%s','%s','%s','%s',%li,%li]",
559 comma, a_proto, a_src, a_dst, a_sport, a_dport, outbytes, inbytes );
561 comma = ',';
563 web_puts("];\n");
565 fclose(a);
566 fclose(b);
570 void asp_qrate(int argc, char **argv)
572 FILE *f;
573 char s[256];
574 unsigned long rates[10];
575 unsigned long u;
576 char *e;
577 int n;
578 char comma;
579 char *a[1];
581 a[0] = "1";
582 asp_ctcount(1, a);
584 memset(rates, 0, sizeof(rates));
585 sprintf(s, "tc -s class ls dev %s", get_wanface());
586 if ((f = popen(s, "r")) != NULL) {
587 n = 1;
588 while (fgets(s, sizeof(s), f)) {
589 if (strncmp(s, "class htb 1:", 12) == 0) {
590 n = atoi(s + 12);
592 else if (strncmp(s, " rate ", 6) == 0) {
593 if ((n % 10) == 0) {
594 n /= 10;
595 if ((n >= 1) && (n <= 10)) {
596 u = strtoul(s + 6, &e, 10);
597 if (*e == 'K') u *= 1000;
598 else if (*e == 'M') u *= 1000 * 1000;
599 rates[n - 1] = u;
600 n = 1;
605 pclose(f);
608 comma = ' ';
609 web_puts("\nqrates = [0,");
610 for (n = 0; n < 10; ++n) {
611 web_printf("%c%lu", comma, rates[n]);
612 comma = ',';
614 web_puts("];");
617 static void layer7_list(const char *path, int *first)
619 DIR *dir;
620 struct dirent *de;
621 char *p;
622 char name[NAME_MAX];
624 if ((dir = opendir(path)) != NULL) {
625 while ((de = readdir(dir)) != NULL) {
626 strlcpy(name, de->d_name, sizeof(name));
627 if ((p = strstr(name, ".pat")) == NULL) continue;
628 *p = 0;
629 web_printf("%s'%s'", *first ? "" : ",", name);
630 *first = 0;
632 closedir(dir);
636 void asp_layer7(int argc, char **argv)
638 int first = 1;
639 web_puts("\nlayer7 = [");
640 layer7_list("/etc/l7-extra", &first);
641 layer7_list("/etc/l7-protocols", &first);
642 web_puts("];\n");
645 void wo_expct(char *url)
647 f_write_string("/proc/net/expire_early", "15", 0, 0);