fixes, fully translated tomato, with english dictionary and Polish translation
[tomato.git] / release / src / router / rc / qos.c
blob23a611402ca4ceefa2c327ccf5b1e76b2b43ffe0
1 /*
3 Tomato Firmware
4 Copyright (C) 2006-2009 Jonathan Zarate
6 */
8 #include "rc.h"
10 #include <sys/stat.h>
13 // in mangle table
14 void ipt_qos(void)
16 char *buf;
17 char *g;
18 char *p;
19 char *addr_type, *addr;
20 char *proto;
21 char *port_type, *port;
22 char *class_prio;
23 char *ipp2p, *layer7;
24 char *bcount;
25 char *dscp;
26 char *desc;
27 int class_num;
28 int proto_num;
29 int v4v6_ok;
30 int i;
31 char sport[192];
32 char saddr[192];
33 char end[256];
34 char s[32];
35 char app[128];
36 int inuse;
37 const char *chain;
38 unsigned long min;
39 unsigned long max;
40 unsigned long prev_max;
41 int gum;
42 const char *qface;
43 int sizegroup;
44 int class_flag;
45 int rule_num;
47 if (!nvram_get_int("qos_enable")) return;
49 inuse = 0;
50 gum = 0x100;
51 sizegroup = 0;
52 prev_max = 0;
53 rule_num = 0;
55 ip46t_write(
56 ":QOSO - [0:0]\n"
57 "-A QOSO -j CONNMARK --restore-mark --mask 0xff\n"
58 "-A QOSO -m connmark ! --mark 0/0x0f00 -j RETURN\n");
60 g = buf = strdup(nvram_safe_get("qos_orules"));
61 while (g) {
65 addr_type<addr<proto<port_type<port<ipp2p<L7<bcount<dscp<class_prio<desc
67 addr_type:
68 0 = any
69 1 = dest ip
70 2 = src ip
71 3 = src mac
72 addr:
73 ip/mac if addr_type == 1-3
74 proto:
75 0-65535 = protocol
76 -1 = tcp or udp
77 -2 = any protocol
78 port_type:
79 if proto == -1,tcp,udp:
80 d = dest
81 s = src
82 x = both
83 a = any
84 port:
85 port # if proto == -1,tcp,udp
86 bcount:
87 min:max
88 blank = none
89 dscp:
90 empty - any
91 numeric (0:63) - dscp value
92 afXX, csX, be, ef - dscp class
93 class_prio:
94 0-8
95 -1 = disabled
99 if ((p = strsep(&g, ">")) == NULL) break;
100 i = vstrsep(p, "<", &addr_type, &addr, &proto, &port_type, &port, &ipp2p, &layer7, &bcount, &dscp, &class_prio, &desc);
101 rule_num++;
102 if (i == 10) {
103 // fixup < v1.28.XX55
104 desc = class_prio;
105 class_prio = dscp;
106 dscp = "";
108 else if (i == 9) {
109 // fixup < v0.08 // !!! temp
110 desc = class_prio;
111 class_prio = bcount;
112 bcount = "";
113 dscp = "";
115 else if (i != 11) continue;
117 class_num = atoi(class_prio);
118 if ((class_num < 0) || (class_num > 9)) continue;
120 i = 1 << class_num;
121 ++class_num;
123 if ((inuse & i) == 0) {
124 inuse |= i;
127 v4v6_ok = IPT_V4;
128 #ifdef TCONFIG_IPV6
129 if (ipv6_enabled())
130 v4v6_ok |= IPT_V6;
131 #endif
132 class_flag = gum;
135 if (ipt_ipp2p(ipp2p, app)) v4v6_ok &= ~IPT_V6;
136 else ipt_layer7(layer7, app);
137 if (app[0]) {
138 v4v6_ok &= ~IPT_V6; // temp: l7 not working either!
139 class_flag = 0x100;
140 // IPP2P and L7 rules may need more than one packet before matching
141 // so port-based rules that come after them in the list can't be sticky
142 // or else these rules might never match.
143 gum = 0;
145 strcpy(end, app);
147 // dscp
148 if (ipt_dscp(dscp, s)) {
149 #ifndef LINUX26
150 v4v6_ok &= ~IPT_V6; // dscp ipv6 match is not present in K2.4
151 #endif
152 strcat(end, s);
155 // mac or ip address
156 if ((*addr_type == '1') || (*addr_type == '2')) { // match ip
157 v4v6_ok &= ipt_addr(saddr, sizeof(saddr), addr, (*addr_type == '1') ? "dst" : "src",
158 v4v6_ok, (v4v6_ok==IPT_V4), "QoS", desc);
159 if (!v4v6_ok) continue;
161 else if (*addr_type == '3') { // match mac
162 sprintf(saddr, "-m mac --mac-source %s", addr); // (-m mac modified, returns !match in OUTPUT)
164 else {
165 saddr[0] = 0;
169 // -m connbytes --connbytes x:y --connbytes-dir both --connbytes-mode bytes
170 if (*bcount) {
171 min = strtoul(bcount, &p, 10);
172 if (*p != 0) {
173 strcat(end, " -m connbytes --connbytes-mode bytes --connbytes-dir both --connbytes ");
174 ++p;
175 if (*p == 0) {
176 sprintf(end + strlen(end), "%lu:", min * 1024);
178 else {
179 max = strtoul(p, NULL, 10);
180 sprintf(end + strlen(end), "%lu:%lu", min * 1024, (max * 1024) - 1);
181 if (gum) {
182 if (!sizegroup) {
183 // Create table of connbytes sizes, pass appropriate connections there
184 // and only continue processing them if mark was wiped
185 ip46t_write(
186 ":QOSSIZE - [0:0]\n"
187 "-I QOSO 3 -m connmark ! --mark 0/0xff000 -j QOSSIZE\n"
188 "-I QOSO 4 -m connmark ! --mark 0/0xff000 -j RETURN\n");
190 if (max != prev_max && sizegroup<255) {
191 class_flag = ++sizegroup << 12;
192 prev_max = max;
193 ip46t_flagged_write(v4v6_ok,
194 "-A QOSSIZE -m connmark --mark 0x%x/0xff000"
195 " -m connbytes --connbytes-mode bytes --connbytes-dir both --connbytes %lu: -j CONNMARK --set-return 0x00000/0xFF\n",
196 (sizegroup << 12), (max * 1024));
198 else {
199 class_flag = sizegroup << 12;
205 else {
206 bcount = "";
210 chain = "QOSO";
211 class_num |= class_flag;
212 class_num |= rule_num << 20;
213 sprintf(end + strlen(end), " -j CONNMARK --set-return 0x%x/0xFF\n", class_num);
215 // protocol & ports
216 proto_num = atoi(proto);
217 if (proto_num > -2) {
218 if ((proto_num == 6) || (proto_num == 17) || (proto_num == -1)) {
219 if (*port_type != 'a') {
220 if ((*port_type == 'x') || (strchr(port, ','))) {
221 // dst-or-src port matches, and anything with multiple lists "," use multiport
222 sprintf(sport, "-m multiport --%sports %s", (*port_type == 's') ? "s" : ((*port_type == 'd') ? "d" : ""), port);
224 else {
225 // single or simple x:y range, use built-in tcp/udp match
226 sprintf(sport, "--%sport %s", (*port_type == 's') ? "s" : ((*port_type == 'd') ? "d" : ""), port);
229 else {
230 sport[0] = 0;
232 if (proto_num != 6) ip46t_flagged_write(v4v6_ok, "-A %s -p %s %s %s %s", chain, "udp", sport, saddr, end);
233 if (proto_num != 17) ip46t_flagged_write(v4v6_ok, "-A %s -p %s %s %s %s", chain, "tcp", sport, saddr, end);
235 else {
236 ip46t_flagged_write(v4v6_ok, "-A %s -p %d %s %s", chain, proto_num, saddr, end);
239 else { // any protocol
240 ip46t_flagged_write(v4v6_ok, "-A %s %s %s", chain, saddr, end);
244 free(buf);
246 qface = wanfaces.iface[0].name;
248 i = nvram_get_int("qos_default");
249 if ((i < 0) || (i > 9)) i = 3; // "low"
250 class_num = i + 1;
251 class_num |= 0xFF00000; // use rule_num=255 for default
252 ip46t_write("-A QOSO -j CONNMARK --set-return 0x%x\n", class_num);
254 ipt_write(
255 "-A FORWARD -o %s -j QOSO\n"
256 "-A OUTPUT -o %s -j QOSO\n",
257 qface, qface);
259 #ifdef TCONFIG_IPV6
260 if (*wan6face) {
261 ip6t_write(
262 "-A FORWARD -o %s -j QOSO\n"
263 "-A OUTPUT -o %s -j QOSO\n",
264 wan6face, wan6face);
266 #endif
268 inuse |= (1 << i) | 1; // default and highest are always built
269 sprintf(s, "%d", inuse);
270 nvram_set("qos_inuse", s);
273 g = buf = strdup(nvram_safe_get("qos_irates"));
274 for (i = 0; i < 10; ++i) {
275 if ((!g) || ((p = strsep(&g, ",")) == NULL)) continue;
276 if ((inuse & (1 << i)) == 0) continue;
277 if (atoi(p) > 0) {
278 ipt_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", qface);
279 #ifdef TCONFIG_IPV6
280 if (*wan6face)
281 ip6t_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", wan6face);
282 #endif
283 break;
286 free(buf);
291 static const char *qosfn = "/etc/qos";
293 static unsigned calc(unsigned bw, unsigned pct)
295 unsigned n = ((unsigned long)bw * pct) / 100;
296 return (n < 2) ? 2 : n;
299 void start_qos(void)
301 int i;
302 char *buf, *g, *p;
303 unsigned int rate;
304 unsigned int ceil;
305 unsigned int bw;
306 unsigned int mtu;
307 unsigned int r2q;
308 FILE *f;
309 int x;
310 int inuse;
311 char s[256];
312 int first;
313 char burst_root[32];
314 char burst_leaf[32];
317 // move me?
318 x = nvram_get_int("ne_vegas");
319 #ifdef LINUX26
320 if (x) {
321 char alpha[10], beta[10], gamma[10];
322 sprintf(alpha, "alpha=%d", nvram_get_int("ne_valpha"));
323 sprintf(beta, "beta=%d", nvram_get_int("ne_vbeta"));
324 sprintf(gamma, "gamma=%d", nvram_get_int("ne_vgamma"));
325 modprobe("tcp_vegas", alpha, beta, gamma);
326 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "vegas", 0, 0);
328 else {
329 modprobe_r("tcp_vegas");
330 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "", FW_NEWLINE, 0);
332 #else
333 f_write_string("/proc/sys/net/ipv4/tcp_vegas_cong_avoid", x ? "1" : "0", 0, 0);
334 if (x) {
335 f_write_string("/proc/sys/net/ipv4/tcp_vegas_alpha", nvram_safe_get("ne_valpha"), 0, 0);
336 f_write_string("/proc/sys/net/ipv4/tcp_vegas_beta", nvram_safe_get("ne_vbeta"), 0, 0);
337 f_write_string("/proc/sys/net/ipv4/tcp_vegas_gamma", nvram_safe_get("ne_vgamma"), 0, 0);
339 #endif
341 if (!nvram_get_int("qos_enable")) return;
343 if ((f = fopen(qosfn, "w")) == NULL) return;
345 i = nvram_get_int("qos_burst0");
346 if (i > 0) sprintf(burst_root, "burst %dk", i);
347 else burst_root[0] = 0;
348 i = nvram_get_int("qos_burst1");
349 if (i > 0) sprintf(burst_leaf, "burst %dk", i);
350 else burst_leaf[0] = 0;
352 mtu = strtoul(nvram_safe_get("wan_mtu"), NULL, 10);
353 bw = strtoul(nvram_safe_get("qos_obw"), NULL, 10);
355 r2q = 10;
356 if ((bw * 1000) / (8 * r2q) < mtu) {
357 r2q = (bw * 1000) / (8 * mtu);
358 if (r2q < 1) r2q = 1;
359 } else if ((bw * 1000) / (8 * r2q) > 60000) {
360 r2q = (bw * 1000) / (8 * 60000) + 1;
363 fprintf(f,
364 "#!/bin/sh\n"
365 "I=%s\n"
366 "TQA=\"tc qdisc add dev $I\"\n"
367 "TCA=\"tc class add dev $I\"\n"
368 "TFA=\"tc filter add dev $I\"\n"
369 "Q=\"%s\"\n"
370 "\n"
371 "case \"$1\" in\n"
372 "start)\n"
373 "\ttc qdisc del dev $I root 2>/dev/null\n"
374 "\t$TQA root handle 1: htb default %u r2q %u\n"
375 "\t$TCA parent 1: classid 1:1 htb rate %ukbit ceil %ukbit %s\n",
376 get_wanface(),
377 nvram_get_int("qos_pfifo") ? "pfifo limit 256" : "sfq perturb 10",
378 (nvram_get_int("qos_default") + 1) * 10, r2q,
379 bw, bw, burst_root);
381 inuse = nvram_get_int("qos_inuse");
383 g = buf = strdup(nvram_safe_get("qos_orates"));
384 for (i = 0; i < 10; ++i) {
385 if ((!g) || ((p = strsep(&g, ",")) == NULL)) break;
387 if ((inuse & (1 << i)) == 0) continue;
389 if ((sscanf(p, "%u-%u", &rate, &ceil) != 2) || (rate < 1)) continue; // 0=off
391 if (ceil > 0) sprintf(s, "ceil %ukbit ", calc(bw, ceil));
392 else s[0] = 0;
393 x = (i + 1) * 10;
394 fprintf(f,
395 "# egress %d: %u-%u%%\n"
396 "\t$TCA parent 1:1 classid 1:%d htb rate %ukbit %s %s prio %d quantum %u\n"
397 "\t$TQA parent 1:%d handle %d: $Q\n"
398 "\t$TFA parent 1: prio %d protocol ip handle %d fw flowid 1:%d\n",
399 i, rate, ceil,
400 x, calc(bw, rate), s, burst_leaf, (i >= 6) ? 7 : (i + 1), mtu,
401 x, x,
402 x, i + 1, x);
404 free(buf);
406 // "\t$TFA parent 1: prio 10 protocol ip u32 match ip tos 0x10 0xff flowid :10\n" // TOS EF -> Highest
410 if (nvram_match("qos_ack", "1")) {
411 fprintf(f,
412 "\n"
413 "\t$TFA parent 1: prio 15 protocol ip u32 "
414 "match ip protocol 6 0xff " // TCP
415 "match u8 0x05 0x0f at 0 " // IP header length
416 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
417 "match u8 0x10 0xff at 33 " // ACK only
418 "flowid 1:10\n");
420 if (nvram_match("qos_icmp", "1")) {
421 fputs("\n\t$TFA parent 1: prio 14 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
426 if (nvram_get_int("qos_ack")) {
427 fprintf(f,
428 "\n"
429 "\t$TFA parent 1: prio 14 protocol ip u32 "
430 "match ip protocol 6 0xff " // TCP
431 "match u8 0x05 0x0f at 0 " // IP header length
432 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
433 "match u8 0x10 0xff at 33 " // ACK only
434 "flowid 1:10\n");
437 if (nvram_get_int("qos_syn")) {
438 // 10000 = ACK
439 // 00010 = SYN
441 fprintf(f,
442 "\n"
443 "\t$TFA parent 1: prio 15 protocol ip u32 "
444 "match ip protocol 6 0xff " // TCP
445 "match u8 0x05 0x0f at 0 " // IP header length
446 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
447 "match u8 0x02 0xff at 33 " // SYN only
448 "flowid 1:10\n"
449 "\n"
450 "\t$TFA parent 1: prio 16 protocol ip u32 "
451 "match ip protocol 6 0xff " // TCP
452 "match u8 0x05 0x0f at 0 " // IP header length
453 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
454 "match u8 0x12 0xff at 33 " // SYN,ACK
455 "flowid 1:10\n");
458 if (nvram_get_int("qos_fin")) {
459 // 10000 = ACK
460 // 00001 = FIN
462 fprintf(f,
463 "\n"
464 "\t$TFA parent 1: prio 17 protocol ip u32 "
465 "match ip protocol 6 0xff " // TCP
466 "match u8 0x05 0x0f at 0 " // IP header length
467 "match u8 0x11 0xff at 33 " // ACK,FIN
468 "flowid 1:10\n"
469 "\n"
470 "\t$TFA parent 1: prio 18 protocol ip u32 "
471 "match ip protocol 6 0xff " // TCP
472 "match u8 0x05 0x0f at 0 " // IP header length
473 "match u8 0x01 0xff at 33 " // FIN
474 "flowid 1:10\n");
477 if (nvram_get_int("qos_rst")) {
478 // 10000 = ACK
479 // 00100 = RST
480 fprintf(f,
481 "\n"
482 "\t$TFA parent 1: prio 19 protocol ip u32 "
483 "match ip protocol 6 0xff " // TCP
484 "match u8 0x05 0x0f at 0 " // IP header length
485 "match u8 0x14 0xff at 33 " // ACK,RST
486 "flowid 1:10\n"
487 "\n"
488 "\t$TFA parent 1: prio 20 protocol ip u32 "
489 "match ip protocol 6 0xff " // TCP
490 "match u8 0x05 0x0f at 0 " // IP header length
491 "match u8 0x04 0xff at 33 " // RST
492 "flowid 1:10\n");
496 if (nvram_get_int("qos_icmp")) {
497 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
502 10000 = ACK
503 00100 = RST
504 00010 = SYN
505 00001 = FIN
508 if (nvram_get_int("qos_ack")) {
509 fprintf(f,
510 "\n"
511 "\t$TFA parent 1: prio 14 protocol ip u32 "
512 "match ip protocol 6 0xff " // TCP
513 "match u8 0x05 0x0f at 0 " // IP header length
514 // "match u16 0x0000 0xff80 at 2 " // total length (0-127)
515 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
516 "match u8 0x10 0xff at 33 " // ACK only
517 "flowid 1:10\n");
520 if (nvram_get_int("qos_syn")) {
521 fprintf(f,
522 "\n"
523 "\t$TFA parent 1: prio 15 protocol ip u32 "
524 "match ip protocol 6 0xff " // TCP
525 "match u8 0x05 0x0f at 0 " // IP header length
526 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
527 "match u8 0x02 0x02 at 33 " // SYN,*
528 "flowid 1:10\n");
531 if (nvram_get_int("qos_fin")) {
532 fprintf(f,
533 "\n"
534 "\t$TFA parent 1: prio 17 protocol ip u32 "
535 "match ip protocol 6 0xff " // TCP
536 "match u8 0x05 0x0f at 0 " // IP header length
537 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
538 "match u8 0x01 0x01 at 33 " // FIN,*
539 "flowid 1:10\n");
542 if (nvram_get_int("qos_rst")) {
543 fprintf(f,
544 "\n"
545 "\t$TFA parent 1: prio 19 protocol ip u32 "
546 "match ip protocol 6 0xff " // TCP
547 "match u8 0x05 0x0f at 0 " // IP header length
548 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
549 "match u8 0x04 0x04 at 33 " // RST,*
550 "flowid 1:10\n");
554 if (nvram_get_int("qos_icmp")) {
555 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
558 // ingress
560 first = 1;
561 bw = strtoul(nvram_safe_get("qos_ibw"), NULL, 10);
562 g = buf = strdup(nvram_safe_get("qos_irates"));
563 for (i = 0; i < 10; ++i) {
564 if ((!g) || ((p = strsep(&g, ",")) == NULL)) break;
566 if ((inuse & (1 << i)) == 0) continue;
568 if ((rate = atoi(p)) < 1) continue; // 0 = off
570 if (first) {
571 first = 0;
572 fprintf(f,
573 "\n"
574 "\ttc qdisc del dev $I ingress 2>/dev/null\n"
575 "\t$TQA handle ffff: ingress\n");
578 // rate in kb/s
579 unsigned int u = calc(bw, rate);
581 // burst rate
582 unsigned int v = u / 25;
583 if (v < 50) v = 50;
584 // const unsigned int v = 200;
586 x = i + 1;
587 fprintf(f,
588 "# ingress %d: %u%%\n"
589 "\t$TFA parent ffff: prio %d protocol ip handle %d fw police rate %ukbit burst %ukbit drop flowid ffff:%d\n",
590 i, rate,
591 x, x, u, v, x);
593 free(buf);
595 fputs(
596 "\t;;\n"
597 "stop)\n"
598 "\ttc qdisc del dev $I root 2>/dev/null\n"
599 "\ttc qdisc del dev $I ingress 2>/dev/null\n"
600 "\t;;\n"
601 "*)\n"
602 "\ttc -s -d qdisc ls dev $I\n"
603 "\techo\n"
604 "\ttc -s -d class ls dev $I\n"
605 "esac\n",
608 fclose(f);
609 chmod(qosfn, 0700);
610 eval((char *)qosfn, "start");
613 void stop_qos(void)
615 eval((char *)qosfn, "stop");
617 if (!nvram_match("debug_keepfiles", "1")) {
618 unlink(qosfn);
625 PREROUTING (mn) ----> x ----> FORWARD (f) ----> + ----> POSTROUTING (n)
626 QD | ^
629 INPUT (f) OUTPUT (mnf)