Tomato 1.28
[tomato.git] / release / src / router / rc / qos.c
blob8b40ff284d83ca674e42295abb89437dfbcd21e6
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 int class_num;
26 int proto_num;
27 int i;
28 char sport[192];
29 char saddr[192];
30 char end[256];
31 char s[32];
32 char app[128];
33 int inuse;
34 const char *chain;
35 int used_qosox;
36 unsigned long min;
37 int used_bcount;
38 int gum;
40 if (!nvram_get_int("qos_enable")) return;
42 used_qosox = 0;
43 used_bcount = 0;
44 inuse = 0;
45 gum = 0x100;
47 ipt_write(
48 ":QOSO - [0:0]\n"
49 "-A QOSO -j CONNMARK --restore-mark --mask 0xff\n"
50 "-A QOSO -m connmark ! --mark 0/0xff00 -j RETURN\n");
52 g = buf = strdup(nvram_safe_get("qos_orules"));
53 while (g) {
57 addr_type<addr<proto<port_type<port<ipp2p<L7<bcount<desc
59 addr_type:
60 0 = any
61 1 = dest ip
62 2 = src ip
63 3 = src mac
64 addr:
65 ip/mac if addr_type == 1-3
66 proto:
67 0-65535 = protocol
68 -1 = tcp or udp
69 -2 = any protocol
70 port_type:
71 if proto == -1,tcp,udp:
72 d = dest
73 s = src
74 x = both
75 a = any
76 port:
77 port # if proto == -1,tcp,udp
78 bcount:
79 min:max
80 blank = none
81 class_prio:
82 0-8
83 -1 = disabled
87 if ((p = strsep(&g, ">")) == NULL) break;
88 i = vstrsep(p, "<", &addr_type, &addr, &proto, &port_type, &port, &ipp2p, &layer7, &bcount, &class_prio, &p);
89 if (i == 9) {
90 // fixup < v0.08 // !!! temp
91 class_prio = bcount;
92 bcount = "";
94 else if (i != 10) continue;
96 class_num = atoi(class_prio);
97 if ((class_num < 0) || (class_num > 9)) continue;
99 i = 1 << class_num;
100 ++class_num;
102 if ((inuse & i) == 0) {
103 inuse |= i;
106 // mac or ip address
107 if ((*addr_type == '1') || (*addr_type == '2')) { // match ip
108 if (strchr(addr, '-') != NULL) {
109 sprintf(saddr, "-m iprange --%s-range %s", (*addr_type == '1') ? "dst" : "src", addr);
111 else {
112 sprintf(saddr, "-%c %s", (*addr_type == '1') ? 'd' : 's', addr);
115 else if (*addr_type == '3') { // match mac
116 sprintf(saddr, "-m mac --mac-source %s", addr); // (-m mac modified, returns !match in OUTPUT)
118 else {
119 saddr[0] = 0;
123 if (!ipt_ipp2p(ipp2p, app)) ipt_layer7(layer7, app);
124 if (app[0]) {
125 class_num |= 0x100;
126 gum = 0;
128 strcpy(end, app);
130 // -m bcount --range x-y
131 if (*bcount) {
132 min = strtoul(bcount, &p, 10);
133 if (*p != 0) {
134 strcat(end, " -m bcount --range ");
135 ++p;
136 if (*p == 0) {
137 sprintf(end + strlen(end), "0x%lx", min * 1024);
139 else {
140 sprintf(end + strlen(end), "0x%lx-0x%lx", min * 1024, (strtoul(p, NULL, 10) * 1024) - 1);
141 class_num &= 0x2FF;
144 used_bcount++;
145 gum = 0;
147 else {
148 bcount = "";
152 chain = "QOSO";
153 class_num |= gum;
154 sprintf(end + strlen(end), " -j CONNMARK --set-return 0x%x/0xFF\n", class_num);
156 // protocol & ports
157 proto_num = atoi(proto);
158 if (proto_num > -2) {
159 if ((proto_num == 6) || (proto_num == 17) || (proto_num == -1)) {
160 if (*port_type != 'a') {
161 if ((*port_type == 'x') || (strchr(port, ','))) {
162 // dst-or-src port matches, and anything with multiple lists "," use mport
163 sprintf(sport, "-m mport --%sports %s", (*port_type == 's') ? "s" : ((*port_type == 'd') ? "d" : ""), port);
165 else {
166 // single or simple x:y range, use built-in tcp/udp match
167 sprintf(sport, "--%sport %s", (*port_type == 's') ? "s" : ((*port_type == 'd') ? "d" : ""), port);
170 else {
171 sport[0] = 0;
173 if (proto_num != 6) ipt_write("-A %s -p %s %s %s %s", chain, "udp", sport, saddr, end);
174 if (proto_num != 17) ipt_write("-A %s -p %s %s %s %s", chain, "tcp", sport, saddr, end);
176 else {
177 ipt_write("-A %s -p %d %s %s", chain, proto_num, saddr, end);
180 else { // any protocol
181 ipt_write("-A %s %s %s", chain, saddr, end);
186 free(buf);
188 if (used_bcount) {
189 ipt_write("-I QOSO -j BCOUNT\n");
192 i = nvram_get_int("qos_default");
193 if ((i < 0) || (i > 9)) i = 3; // "low"
194 class_num = i + 1;
195 ipt_write(
196 "-A QOSO -j CONNMARK --set-return 0x%x\n"
197 "-A FORWARD -o %s -j QOSO\n"
198 "-A OUTPUT -o %s -j QOSO\n",
199 class_num, wanface, wanface);
201 inuse |= (1 << i) | 1; // default and highest are always built
202 sprintf(s, "%d", inuse);
203 nvram_set("qos_inuse", s);
206 g = buf = strdup(nvram_safe_get("qos_irates"));
207 for (i = 0; i < 10; ++i) {
208 if ((!g) || ((p = strsep(&g, ",")) == NULL)) continue;
209 if ((inuse & (1 << i)) == 0) continue;
210 if (atoi(p) > 0) {
211 ipt_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", wanface);
212 break;
215 free(buf);
220 static const char *qosfn = "/etc/qos";
222 static unsigned calc(unsigned bw, unsigned pct)
224 unsigned n = ((unsigned long)bw * pct) / 100;
225 return (n < 2) ? 2 : n;
228 void start_qos(void)
230 int i;
231 char *buf, *g, *p;
232 unsigned int rate;
233 unsigned int ceil;
234 unsigned int bw;
235 unsigned int mtu;
236 FILE *f;
237 int x;
238 int inuse;
239 char s[256];
240 int first;
241 char burst_root[32];
242 char burst_leaf[32];
245 // move me?
246 x = nvram_get_int("ne_vegas");
247 f_write_string("/proc/sys/net/ipv4/tcp_vegas_cong_avoid", x ? "1" : "0", 0, 0);
248 if (x) {
249 f_write_string("/proc/sys/net/ipv4/tcp_vegas_alpha", nvram_safe_get("ne_valpha"), 0, 0);
250 f_write_string("/proc/sys/net/ipv4/tcp_vegas_beta", nvram_safe_get("ne_vbeta"), 0, 0);
251 f_write_string("/proc/sys/net/ipv4/tcp_vegas_gamma", nvram_safe_get("ne_vgamma"), 0, 0);
255 if (!nvram_get_int("qos_enable")) return;
257 if ((f = fopen(qosfn, "w")) == NULL) return;
259 i = nvram_get_int("qos_burst0");
260 if (i > 0) sprintf(burst_root, "burst %dk", i);
261 else burst_root[0] = 0;
262 i = nvram_get_int("qos_burst1");
263 if (i > 0) sprintf(burst_leaf, "burst %dk", i);
264 else burst_leaf[0] = 0;
266 mtu = strtoul(nvram_safe_get("wan_mtu"), NULL, 10);
267 bw = strtoul(nvram_safe_get("qos_obw"), NULL, 10);
269 fprintf(f,
270 "#!/bin/sh\n"
271 "I=%s\n"
272 "TQA=\"tc qdisc add dev $I\"\n"
273 "TCA=\"tc class add dev $I\"\n"
274 "TFA=\"tc filter add dev $I\"\n"
275 "Q=\"%s\"\n"
276 "\n"
277 "case \"$1\" in\n"
278 "start)\n"
279 "\ttc qdisc del dev $I root 2>/dev/null\n"
280 "\t$TQA root handle 1: htb default %u\n"
281 "\t$TCA parent 1: classid 1:1 htb rate %ukbit ceil %ukbit %s\n",
282 nvram_safe_get("wan_iface"),
283 nvram_get_int("qos_pfifo") ? "pfifo limit 256" : "sfq perturb 10",
284 (nvram_get_int("qos_default") + 1) * 10,
285 bw, bw, burst_root);
287 inuse = nvram_get_int("qos_inuse");
289 g = buf = strdup(nvram_safe_get("qos_orates"));
290 for (i = 0; i < 10; ++i) {
291 if ((!g) || ((p = strsep(&g, ",")) == NULL)) break;
293 if ((inuse & (1 << i)) == 0) continue;
295 if ((sscanf(p, "%u-%u", &rate, &ceil) != 2) || (rate < 1)) continue; // 0=off
297 if (ceil > 0) sprintf(s, "ceil %ukbit ", calc(bw, ceil));
298 else s[0] = 0;
299 x = (i + 1) * 10;
300 fprintf(f,
301 "# egress %d: %u-%u%%\n"
302 "\t$TCA parent 1:1 classid 1:%d htb rate %ukbit %s %s prio %d quantum %u\n"
303 "\t$TQA parent 1:%d handle %d: $Q\n"
304 "\t$TFA parent 1: prio %d protocol ip handle %d fw flowid 1:%d\n",
305 i, rate, ceil,
306 x, calc(bw, rate), s, burst_leaf, (i >= 6) ? 7 : (i + 1), mtu,
307 x, x,
308 x, i + 1, x);
310 free(buf);
312 // "\t$TFA parent 1: prio 10 protocol ip u32 match ip tos 0x10 0xff flowid :10\n" // TOS EF -> Highest
316 if (nvram_match("qos_ack", "1")) {
317 fprintf(f,
318 "\n"
319 "\t$TFA parent 1: prio 15 protocol ip u32 "
320 "match ip protocol 6 0xff " // TCP
321 "match u8 0x05 0x0f at 0 " // IP header length
322 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
323 "match u8 0x10 0xff at 33 " // ACK only
324 "flowid 1:10\n");
326 if (nvram_match("qos_icmp", "1")) {
327 fputs("\n\t$TFA parent 1: prio 14 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
332 if (nvram_get_int("qos_ack")) {
333 fprintf(f,
334 "\n"
335 "\t$TFA parent 1: prio 14 protocol ip u32 "
336 "match ip protocol 6 0xff " // TCP
337 "match u8 0x05 0x0f at 0 " // IP header length
338 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
339 "match u8 0x10 0xff at 33 " // ACK only
340 "flowid 1:10\n");
343 if (nvram_get_int("qos_syn")) {
344 // 10000 = ACK
345 // 00010 = SYN
347 fprintf(f,
348 "\n"
349 "\t$TFA parent 1: prio 15 protocol ip u32 "
350 "match ip protocol 6 0xff " // TCP
351 "match u8 0x05 0x0f at 0 " // IP header length
352 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
353 "match u8 0x02 0xff at 33 " // SYN only
354 "flowid 1:10\n"
355 "\n"
356 "\t$TFA parent 1: prio 16 protocol ip u32 "
357 "match ip protocol 6 0xff " // TCP
358 "match u8 0x05 0x0f at 0 " // IP header length
359 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
360 "match u8 0x12 0xff at 33 " // SYN,ACK
361 "flowid 1:10\n");
364 if (nvram_get_int("qos_fin")) {
365 // 10000 = ACK
366 // 00001 = FIN
368 fprintf(f,
369 "\n"
370 "\t$TFA parent 1: prio 17 protocol ip u32 "
371 "match ip protocol 6 0xff " // TCP
372 "match u8 0x05 0x0f at 0 " // IP header length
373 "match u8 0x11 0xff at 33 " // ACK,FIN
374 "flowid 1:10\n"
375 "\n"
376 "\t$TFA parent 1: prio 18 protocol ip u32 "
377 "match ip protocol 6 0xff " // TCP
378 "match u8 0x05 0x0f at 0 " // IP header length
379 "match u8 0x01 0xff at 33 " // FIN
380 "flowid 1:10\n");
383 if (nvram_get_int("qos_rst")) {
384 // 10000 = ACK
385 // 00100 = RST
386 fprintf(f,
387 "\n"
388 "\t$TFA parent 1: prio 19 protocol ip u32 "
389 "match ip protocol 6 0xff " // TCP
390 "match u8 0x05 0x0f at 0 " // IP header length
391 "match u8 0x14 0xff at 33 " // ACK,RST
392 "flowid 1:10\n"
393 "\n"
394 "\t$TFA parent 1: prio 20 protocol ip u32 "
395 "match ip protocol 6 0xff " // TCP
396 "match u8 0x05 0x0f at 0 " // IP header length
397 "match u8 0x04 0xff at 33 " // RST
398 "flowid 1:10\n");
402 if (nvram_get_int("qos_icmp")) {
403 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
408 10000 = ACK
409 00100 = RST
410 00010 = SYN
411 00001 = FIN
414 if (nvram_get_int("qos_ack")) {
415 fprintf(f,
416 "\n"
417 "\t$TFA parent 1: prio 14 protocol ip u32 "
418 "match ip protocol 6 0xff " // TCP
419 "match u8 0x05 0x0f at 0 " // IP header length
420 // "match u16 0x0000 0xff80 at 2 " // total length (0-127)
421 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
422 "match u8 0x10 0xff at 33 " // ACK only
423 "flowid 1:10\n");
426 if (nvram_get_int("qos_syn")) {
427 fprintf(f,
428 "\n"
429 "\t$TFA parent 1: prio 15 protocol ip u32 "
430 "match ip protocol 6 0xff " // TCP
431 "match u8 0x05 0x0f at 0 " // IP header length
432 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
433 "match u8 0x02 0x02 at 33 " // SYN,*
434 "flowid 1:10\n");
437 if (nvram_get_int("qos_fin")) {
438 fprintf(f,
439 "\n"
440 "\t$TFA parent 1: prio 17 protocol ip u32 "
441 "match ip protocol 6 0xff " // TCP
442 "match u8 0x05 0x0f at 0 " // IP header length
443 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
444 "match u8 0x01 0x01 at 33 " // FIN,*
445 "flowid 1:10\n");
448 if (nvram_get_int("qos_rst")) {
449 fprintf(f,
450 "\n"
451 "\t$TFA parent 1: prio 19 protocol ip u32 "
452 "match ip protocol 6 0xff " // TCP
453 "match u8 0x05 0x0f at 0 " // IP header length
454 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
455 "match u8 0x04 0x04 at 33 " // RST,*
456 "flowid 1:10\n");
460 if (nvram_get_int("qos_icmp")) {
461 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
464 // ingress
466 first = 1;
467 bw = strtoul(nvram_safe_get("qos_ibw"), NULL, 10);
468 g = buf = strdup(nvram_safe_get("qos_irates"));
469 for (i = 0; i < 10; ++i) {
470 if ((!g) || ((p = strsep(&g, ",")) == NULL)) break;
472 if ((inuse & (1 << i)) == 0) continue;
474 if ((rate = atoi(p)) < 1) continue; // 0 = off
476 if (first) {
477 first = 0;
478 fprintf(f,
479 "\n"
480 "\ttc qdisc del dev $I ingress 2>/dev/null\n"
481 "\t$TQA handle ffff: ingress\n");
484 // rate in kb/s
485 unsigned int u = calc(bw, rate);
487 // burst rate
488 unsigned int v = u / 25;
489 if (v < 50) v = 50;
490 // const unsigned int v = 200;
492 x = i + 1;
493 fprintf(f,
494 "# ingress %d: %u%%\n"
495 "\t$TFA parent ffff: prio %d protocol ip handle %d fw police rate %ukbit burst %ukbit drop flowid ffff:%d\n",
496 i, rate,
497 x, x, u, v, x);
499 free(buf);
501 fputs(
502 "\t;;\n"
503 "stop)\n"
504 "\ttc qdisc del dev $I root 2>/dev/null\n"
505 "\ttc qdisc del dev $I ingress 2>/dev/null\n"
506 "\t;;\n"
507 "*)\n"
508 "\ttc -s -d qdisc ls dev $I\n"
509 "\techo\n"
510 "\ttc -s -d class ls dev $I\n"
511 "esac\n",
514 fclose(f);
515 chmod(qosfn, 0700);
516 eval((char *)qosfn, "start");
519 void stop_qos(void)
521 eval((char *)qosfn, "stop");
523 if (!nvram_match("debug_keepfiles", "1")) {
524 unlink(qosfn);
531 PREROUTING (mn) ----> x ----> FORWARD (f) ----> + ----> POSTROUTING (n)
532 QD | ^
535 INPUT (f) OUTPUT (mnf)