4 Copyright (C) 2006-2009 Jonathan Zarate
10 Originally Tomato had 10 classes, but only used 6 priority levels for some unknown reason.
11 Therefore, the last 4 classes all had the same prio of 7. The ingress system had no priority system
12 and allowed only class limits. It looked as if this was a legacy of an earlier prototype using CBQ.
14 On 4th February 2012 a new IMQ based ingress system was added to Toastman builds which
15 allows use of priorities on all incoming classes. QOS now uses 10 prios in both egress and ingress.
17 An incoming bandwidth pie chart was added at the same time, making it easier to see the result of
18 QOS rules on incoming data.
28 static const char *qosfn
= "/etc/qos";
29 static const char *qosImqDeviceNumberString
= "0";
30 static const char *qosImqDeviceString
= "imq0";
38 char *addr_type
, *addr
;
40 char *port_type
, *port
;
59 unsigned long prev_max
;
66 if (!nvram_get_int("qos_enable")) return;
76 "-A QOSO -j CONNMARK --restore-mark --mask 0xff\n"
77 "-A QOSO -m connmark ! --mark 0/0x0f00 -j RETURN\n");
79 g
= buf
= strdup(nvram_safe_get("qos_orules"));
84 addr_type<addr<proto<port_type<port<ipp2p<L7<bcount<dscp<class_prio<desc
92 ip/mac if addr_type == 1-3
98 if proto == -1,tcp,udp:
104 port # if proto == -1,tcp,udp
110 numeric (0:63) - dscp value
111 afXX, csX, be, ef - dscp class
113 0-10 // was 0-8 - Changed from 8 in pkt_sched.h - Toastman
118 if ((p
= strsep(&g
, ">")) == NULL
) break;
119 i
= vstrsep(p
, "<", &addr_type
, &addr
, &proto
, &port_type
, &port
, &ipp2p
, &layer7
, &bcount
, &dscp
, &class_prio
, &desc
);
122 // fixup < v1.28.XX55
128 // fixup < v0.08 // !!! temp
134 else if (i
!= 11) continue;
136 class_num
= atoi(class_prio
);
137 if ((class_num
< 0) || (class_num
> 9)) continue;
142 if ((inuse
& i
) == 0) {
154 if (ipt_ipp2p(ipp2p
, app
)) v4v6_ok
&= ~IPT_V6
;
155 else ipt_layer7(layer7
, app
);
157 v4v6_ok
&= ~IPT_V6
; // temp: l7 not working either!
159 // IPP2P and L7 rules may need more than one packet before matching
160 // so port-based rules that come after them in the list can't be sticky
161 // or else these rules might never match.
167 if (ipt_dscp(dscp
, s
)) {
169 v4v6_ok
&= ~IPT_V6
; // dscp ipv6 match is not present in K2.4
175 if ((*addr_type
== '1') || (*addr_type
== '2')) { // match ip
176 v4v6_ok
&= ipt_addr(saddr
, sizeof(saddr
), addr
, (*addr_type
== '1') ? "dst" : "src",
177 v4v6_ok
, (v4v6_ok
==IPT_V4
), "QoS", desc
);
178 if (!v4v6_ok
) continue;
180 else if (*addr_type
== '3') { // match mac
181 sprintf(saddr
, "-m mac --mac-source %s", addr
); // (-m mac modified, returns !match in OUTPUT)
188 // -m connbytes --connbytes x:y --connbytes-dir both --connbytes-mode bytes
190 min
= strtoul(bcount
, &p
, 10);
192 strcat(end
, " -m connbytes --connbytes-mode bytes --connbytes-dir both --connbytes ");
195 sprintf(end
+ strlen(end
), "%lu:", min
* 1024);
198 max
= strtoul(p
, NULL
, 10);
199 sprintf(end
+ strlen(end
), "%lu:%lu", min
* 1024, (max
* 1024) - 1);
202 // Create table of connbytes sizes, pass appropriate connections there
203 // and only continue processing them if mark was wiped
206 "-I QOSO 3 -m connmark ! --mark 0/0xff000 -j QOSSIZE\n"
207 "-I QOSO 4 -m connmark ! --mark 0/0xff000 -j RETURN\n");
209 if (max
!= prev_max
&& sizegroup
<255) {
210 class_flag
= ++sizegroup
<< 12;
212 ip46t_flagged_write(v4v6_ok
,
213 "-A QOSSIZE -m connmark --mark 0x%x/0xff000"
214 " -m connbytes --connbytes-mode bytes --connbytes-dir both --connbytes %lu: -j CONNMARK --set-return 0x00000/0xFF\n",
215 (sizegroup
<< 12), (max
* 1024));
218 class_flag
= sizegroup
<< 12;
230 class_num
|= class_flag
;
231 class_num
|= rule_num
<< 20;
232 sprintf(end
+ strlen(end
), " -j CONNMARK --set-return 0x%x/0xFF\n", class_num
);
235 proto_num
= atoi(proto
);
236 if (proto_num
> -2) {
237 if ((proto_num
== 6) || (proto_num
== 17) || (proto_num
== -1)) {
238 if (*port_type
!= 'a') {
239 if ((*port_type
== 'x') || (strchr(port
, ','))) {
240 // dst-or-src port matches, and anything with multiple lists "," use multiport
241 sprintf(sport
, "-m multiport --%sports %s", (*port_type
== 's') ? "s" : ((*port_type
== 'd') ? "d" : ""), port
);
244 // single or simple x:y range, use built-in tcp/udp match
245 sprintf(sport
, "--%sport %s", (*port_type
== 's') ? "s" : ((*port_type
== 'd') ? "d" : ""), port
);
251 if (proto_num
!= 6) ip46t_flagged_write(v4v6_ok
, "-A %s -p %s %s %s %s", chain
, "udp", sport
, saddr
, end
);
252 if (proto_num
!= 17) ip46t_flagged_write(v4v6_ok
, "-A %s -p %s %s %s %s", chain
, "tcp", sport
, saddr
, end
);
255 ip46t_flagged_write(v4v6_ok
, "-A %s -p %d %s %s", chain
, proto_num
, saddr
, end
);
258 else { // any protocol
259 ip46t_flagged_write(v4v6_ok
, "-A %s %s %s", chain
, saddr
, end
);
265 qface
= wanfaces
.iface
[0].name
;
267 i
= nvram_get_int("qos_default");
268 if ((i
< 0) || (i
> 9)) i
= 3; // "low"
270 class_num
|= 0xFF00000; // use rule_num=255 for default
271 ip46t_write("-A QOSO -j CONNMARK --set-return 0x%x\n", class_num
);
274 "-A FORWARD -o %s -j QOSO\n"
275 "-A OUTPUT -o %s -j QOSO\n",
281 "-A FORWARD -o %s -j QOSO\n"
282 "-A OUTPUT -o %s -j QOSO\n",
287 inuse
|= (1 << i
) | 1; // default and highest are always built
288 sprintf(s
, "%d", inuse
);
289 nvram_set("qos_inuse", s
);
292 g
= buf
= strdup(nvram_safe_get("qos_irates"));
294 for (i
= 0; i
< 10; ++i
)
296 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
)) continue;
297 if ((inuse
& (1 << i
)) == 0) continue;
302 // check if we've got a percentage definition in the form of "rate-ceiling"
304 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) == 2) && (rate
>= 1))
306 ipt_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", qface
);
309 if (nvram_get_int("qos_udp")) {
310 ipt_write("-A PREROUTING -i %s -p tcp -j IMQ --todev %s\n", qface
, qosImqDeviceNumberString
); // pass only tcp
313 ipt_write("-A PREROUTING -i %s -j IMQ --todev %s\n", qface
, qosImqDeviceNumberString
); // pass everything thru ingress
319 ip6t_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", wan6face
);
321 if (nvram_get_int("qos_udp")) {
322 ip6t_write("-A PREROUTING -i %s -p tcp -j IMQ --todev %s\n", wan6face
, qosImqDeviceNumberString
); // pass only tcp
325 ip6t_write("-A PREROUTING -i %s -j IMQ --todev %s\n", wan6face
, qosImqDeviceNumberString
); // pass everything thru ingress
335 static unsigned calc(unsigned bw
, unsigned pct
)
337 unsigned n
= ((unsigned long)bw
* pct
) / 100;
338 return (n
< 2) ? 2 : n
;
348 unsigned int incomingBandwidthInKilobitsPerSecond
;
351 unsigned int qosDefaultClassId
;
360 qosDefaultClassId
= (nvram_get_int("qos_default") + 1) * 10;
361 incomingBandwidthInKilobitsPerSecond
= strtoul(nvram_safe_get("qos_ibw"), NULL
, 10);
364 x
= nvram_get_int("ne_vegas");
367 char alpha
[10], beta
[10], gamma
[10];
368 sprintf(alpha
, "alpha=%d", nvram_get_int("ne_valpha"));
369 sprintf(beta
, "beta=%d", nvram_get_int("ne_vbeta"));
370 sprintf(gamma
, "gamma=%d", nvram_get_int("ne_vgamma"));
371 modprobe("tcp_vegas", alpha
, beta
, gamma
);
372 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "vegas", 0, 0);
375 modprobe_r("tcp_vegas");
376 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "", FW_NEWLINE
, 0);
379 f_write_string("/proc/sys/net/ipv4/tcp_vegas_cong_avoid", x
? "1" : "0", 0, 0);
381 f_write_string("/proc/sys/net/ipv4/tcp_vegas_alpha", nvram_safe_get("ne_valpha"), 0, 0);
382 f_write_string("/proc/sys/net/ipv4/tcp_vegas_beta", nvram_safe_get("ne_vbeta"), 0, 0);
383 f_write_string("/proc/sys/net/ipv4/tcp_vegas_gamma", nvram_safe_get("ne_vgamma"), 0, 0);
387 if (!nvram_get_int("qos_enable")) return;
389 if ((f
= fopen(qosfn
, "w")) == NULL
) return;
391 i
= nvram_get_int("qos_burst0");
392 if (i
> 0) sprintf(burst_root
, "burst %dk", i
);
393 else burst_root
[0] = 0;
394 i
= nvram_get_int("qos_burst1");
395 if (i
> 0) sprintf(burst_leaf
, "burst %dk", i
);
396 else burst_leaf
[0] = 0;
398 mtu
= strtoul(nvram_safe_get("wan_mtu"), NULL
, 10);
399 bw
= strtoul(nvram_safe_get("qos_obw"), NULL
, 10);
402 if ((bw
* 1000) / (8 * r2q
) < mtu
) {
403 r2q
= (bw
* 1000) / (8 * mtu
);
404 if (r2q
< 1) r2q
= 1;
405 } else if ((bw
* 1000) / (8 * r2q
) > 60000) {
406 r2q
= (bw
* 1000) / (8 * 60000) + 1;
413 "TQA=\"tc qdisc add dev $WAN_DEV\"\n"
414 "TCA=\"tc class add dev $WAN_DEV\"\n"
415 "TFA=\"tc filter add dev $WAN_DEV\"\n"
416 "TQA_IMQ=\"tc qdisc add dev $IMQ_DEV\"\n"
417 "TCA_IMQ=\"tc class add dev $IMQ_DEV\"\n"
418 "TFA_IMQ=\"tc filter add dev $IMQ_DEV\"\n"
423 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
424 "\t$TQA root handle 1: htb default %u r2q %u\n"
425 "\t$TCA parent 1: classid 1:1 htb rate %ukbit ceil %ukbit %s\n",
428 nvram_get_int("qos_pfifo") ? "pfifo limit 256" : "sfq perturb 10",
429 qosDefaultClassId
, r2q
,
432 inuse
= nvram_get_int("qos_inuse");
434 g
= buf
= strdup(nvram_safe_get("qos_orates"));
435 for (i
= 0; i
< 10; ++i
) {
436 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
)) break;
438 if ((inuse
& (1 << i
)) == 0) continue;
440 // check if we've got a percentage definition in the form of "rate-ceiling"
441 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) != 2) || (rate
< 1)) continue; // 0=off
443 if (ceil
> 0) sprintf(s
, "ceil %ukbit ", calc(bw
, ceil
));
447 "# egress %d: %u-%u%%\n"
448 "\t$TCA parent 1:1 classid 1:%d htb rate %ukbit %s %s prio %d quantum %u\n"
449 "\t$TQA parent 1:%d handle %d: $Q\n"
450 "\t$TFA parent 1: prio %d protocol ip handle %d fw flowid 1:%d\n",
452 x
, calc(bw
, rate
), s
, burst_leaf
, i
+1, mtu
, //prios 1-10 - Toastman
458 // "\t$TFA parent 1: prio 10 protocol ip u32 match ip tos 0x10 0xff flowid :10\n" // TOS EF -> Highest
462 if (nvram_match("qos_ack", "1")) {
465 "\t$TFA parent 1: prio 15 protocol ip u32 "
466 "match ip protocol 6 0xff " // TCP
467 "match u8 0x05 0x0f at 0 " // IP header length
468 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
469 "match u8 0x10 0xff at 33 " // ACK only
472 if (nvram_match("qos_icmp", "1")) {
473 fputs("\n\t$TFA parent 1: prio 14 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
478 if (nvram_get_int("qos_ack")) {
481 "\t$TFA parent 1: prio 14 protocol ip u32 "
482 "match ip protocol 6 0xff " // TCP
483 "match u8 0x05 0x0f at 0 " // IP header length
484 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
485 "match u8 0x10 0xff at 33 " // ACK only
489 if (nvram_get_int("qos_syn")) {
495 "\t$TFA parent 1: prio 15 protocol ip u32 "
496 "match ip protocol 6 0xff " // TCP
497 "match u8 0x05 0x0f at 0 " // IP header length
498 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
499 "match u8 0x02 0xff at 33 " // SYN only
502 "\t$TFA parent 1: prio 16 protocol ip u32 "
503 "match ip protocol 6 0xff " // TCP
504 "match u8 0x05 0x0f at 0 " // IP header length
505 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
506 "match u8 0x12 0xff at 33 " // SYN,ACK
510 if (nvram_get_int("qos_fin")) {
516 "\t$TFA parent 1: prio 17 protocol ip u32 "
517 "match ip protocol 6 0xff " // TCP
518 "match u8 0x05 0x0f at 0 " // IP header length
519 "match u8 0x11 0xff at 33 " // ACK,FIN
522 "\t$TFA parent 1: prio 18 protocol ip u32 "
523 "match ip protocol 6 0xff " // TCP
524 "match u8 0x05 0x0f at 0 " // IP header length
525 "match u8 0x01 0xff at 33 " // FIN
529 if (nvram_get_int("qos_rst")) {
534 "\t$TFA parent 1: prio 19 protocol ip u32 "
535 "match ip protocol 6 0xff " // TCP
536 "match u8 0x05 0x0f at 0 " // IP header length
537 "match u8 0x14 0xff at 33 " // ACK,RST
540 "\t$TFA parent 1: prio 20 protocol ip u32 "
541 "match ip protocol 6 0xff " // TCP
542 "match u8 0x05 0x0f at 0 " // IP header length
543 "match u8 0x04 0xff at 33 " // RST
548 if (nvram_get_int("qos_icmp")) {
549 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
560 if (nvram_get_int("qos_ack")) {
563 "\t$TFA parent 1: prio 14 protocol ip u32 "
564 "match ip protocol 6 0xff " // TCP
565 "match u8 0x05 0x0f at 0 " // IP header length
566 // "match u16 0x0000 0xff80 at 2 " // total length (0-127)
567 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
568 "match u8 0x10 0xff at 33 " // ACK only
572 if (nvram_get_int("qos_syn")) {
575 "\t$TFA parent 1: prio 15 protocol ip u32 "
576 "match ip protocol 6 0xff " // TCP
577 "match u8 0x05 0x0f at 0 " // IP header length
578 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
579 "match u8 0x02 0x02 at 33 " // SYN,*
583 if (nvram_get_int("qos_fin")) {
586 "\t$TFA parent 1: prio 17 protocol ip u32 "
587 "match ip protocol 6 0xff " // TCP
588 "match u8 0x05 0x0f at 0 " // IP header length
589 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
590 "match u8 0x01 0x01 at 33 " // FIN,*
594 if (nvram_get_int("qos_rst")) {
597 "\t$TFA parent 1: prio 19 protocol ip u32 "
598 "match ip protocol 6 0xff " // TCP
599 "match u8 0x05 0x0f at 0 " // IP header length
600 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
601 "match u8 0x04 0x04 at 33 " // RST,*
605 if (nvram_get_int("qos_icmp")) {
606 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f
);
611 //// INCOMING TRAFFIC SHAPING
616 g
= buf
= strdup(nvram_safe_get("qos_irates"));
618 for (i
= 0; i
< 10; ++i
)
620 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
))
625 if ((inuse
& (1 << i
)) == 0)
630 // check if we've got a percentage definition in the form of "rate-ceiling"
631 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) != 2) || (rate
< 1))
637 unsigned int classid
= ((unsigned int)i
+ 1) * 10;
640 unsigned int priority
= (unsigned int)i
+ 1; //prios 1-10 - Toastman
643 unsigned int rateInKilobitsPerSecond
=
644 calc(incomingBandwidthInKilobitsPerSecond
, rate
);
647 unsigned int ceilingInKilobitsPerSecond
=
648 calc(incomingBandwidthInKilobitsPerSecond
, ceil
);
650 // burst rate (2% of the classes' rate) - don't know if we should use this
651 unsigned int burstRateInBitsPerSecond
=
652 (rateInKilobitsPerSecond
* 1000) / 50;
655 if ((incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * r2q
) < mtu
)
657 r2q
= (incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * mtu
);
658 if (r2q
< 1) r2q
= 1;
660 else if ((incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * r2q
) > 60000)
662 r2q
= (incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * 60000) + 1;
670 "\tip link set $IMQ_DEV up\n"
671 "\ttc qdisc del dev $IMQ_DEV 2>/dev/null\n"
672 "\t$TQA_IMQ handle 1: root htb default %u r2q %u\n"
673 "\t$TCA_IMQ parent 1: classid 1:1 htb rate %ukbit ceil %ukbit\n",
674 qosDefaultClassId
, r2q
,
675 incomingBandwidthInKilobitsPerSecond
,
676 incomingBandwidthInKilobitsPerSecond
);
682 "\t# class id %u: rate %ukbit ceil %ukbit\n",
683 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
);
687 "\t$TCA_IMQ parent 1:1 classid 1:%u htb rate %ukbit ceil %ukbit prio %u quantum %u\n",
688 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
, priority
, mtu
);
692 "\t$TQA_IMQ parent 1:%u handle %u: $Q\n",
697 "\t$TFA_IMQ parent 1: prio %u protocol ip handle %u fw flowid 1:%u \n",
698 classid
, priority
, classid
);
703 //// write commands which adds rule to forward traffic to IMQ device
706 "\t# set up the IMQ device (otherwise this won't work) to limit the incoming data\n"
707 "\tip link set $IMQ_DEV up\n",
713 "\tip link set $IMQ_DEV down\n"
714 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
715 "\ttc qdisc del dev $IMQ_DEV root 2>/dev/null\n"
719 "\techo \"... OUTGOING QDISCS AND CLASSES FOR $WAN_DEV\"\n"
721 "\ttc -s -d qdisc ls dev $WAN_DEV\n"
723 "\ttc -s -d class ls dev $WAN_DEV\n"
726 "\techo \"... INCOMING QDISCS AND CLASSES FOR $WAN_DEV (routed through $IMQ_DEV)\"\n"
728 "\ttc -s -d qdisc ls dev $IMQ_DEV\n"
730 "\ttc -s -d class ls dev $IMQ_DEV\n"
737 eval((char *)qosfn
, "start");
742 eval((char *)qosfn
, "stop");
744 if (!nvram_match("debug_keepfiles", "1")) {
752 PREROUTING (mn) ----> x ----> FORWARD (f) ----> + ----> POSTROUTING (n)
756 INPUT (f) OUTPUT (mnf)