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
;
352 unsigned int overhead
;
361 qosDefaultClassId
= (nvram_get_int("qos_default") + 1) * 10;
362 incomingBandwidthInKilobitsPerSecond
= strtoul(nvram_safe_get("qos_ibw"), NULL
, 10);
365 x
= nvram_get_int("ne_vegas");
368 char alpha
[10], beta
[10], gamma
[10];
369 sprintf(alpha
, "alpha=%d", nvram_get_int("ne_valpha"));
370 sprintf(beta
, "beta=%d", nvram_get_int("ne_vbeta"));
371 sprintf(gamma
, "gamma=%d", nvram_get_int("ne_vgamma"));
372 modprobe("tcp_vegas", alpha
, beta
, gamma
);
373 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "vegas", 0, 0);
376 modprobe_r("tcp_vegas");
377 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "", FW_NEWLINE
, 0);
380 f_write_string("/proc/sys/net/ipv4/tcp_vegas_cong_avoid", x
? "1" : "0", 0, 0);
382 f_write_string("/proc/sys/net/ipv4/tcp_vegas_alpha", nvram_safe_get("ne_valpha"), 0, 0);
383 f_write_string("/proc/sys/net/ipv4/tcp_vegas_beta", nvram_safe_get("ne_vbeta"), 0, 0);
384 f_write_string("/proc/sys/net/ipv4/tcp_vegas_gamma", nvram_safe_get("ne_vgamma"), 0, 0);
388 if (!nvram_get_int("qos_enable")) return;
390 if ((f
= fopen(qosfn
, "w")) == NULL
) return;
392 i
= nvram_get_int("qos_burst0");
393 if (i
> 0) sprintf(burst_root
, "burst %dk", i
);
394 else burst_root
[0] = 0;
395 i
= nvram_get_int("qos_burst1");
396 if (i
> 0) sprintf(burst_leaf
, "burst %dk", i
);
397 else burst_leaf
[0] = 0;
399 mtu
= strtoul(nvram_safe_get("wan_mtu"), NULL
, 10);
400 bw
= strtoul(nvram_safe_get("qos_obw"), NULL
, 10);
401 overhead
= strtoul(nvram_safe_get("atm_overhead"), NULL
, 10);
404 if ((bw
* 1000) / (8 * r2q
) < mtu
) {
405 r2q
= (bw
* 1000) / (8 * mtu
);
406 if (r2q
< 1) r2q
= 1;
407 } else if ((bw
* 1000) / (8 * r2q
) > 60000) {
408 r2q
= (bw
* 1000) / (8 * 60000) + 1;
416 "TQA=\"tc qdisc add dev $WAN_DEV\"\n"
417 "TCA=\"tc class add dev $WAN_DEV\"\n"
418 "TFA=\"tc filter add dev $WAN_DEV\"\n"
419 "TQA_IMQ=\"tc qdisc add dev $IMQ_DEV\"\n"
420 "TCA_IMQ=\"tc class add dev $IMQ_DEV\"\n"
421 "TFA_IMQ=\"tc filter add dev $IMQ_DEV\"\n"
426 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
427 "\t$TQA root handle 1: htb default %u r2q %u\n"
428 "\t$TCA parent 1: classid 1:1 htb rate %ukbit ceil %ukbit %s\n",
431 nvram_get_int("qos_pfifo") ? "pfifo limit 256" : "sfq perturb 10",
432 qosDefaultClassId
, r2q
,
439 "TQA=\"tc qdisc add dev $WAN_DEV\"\n"
440 "TCA=\"tc class add dev $WAN_DEV\"\n"
441 "TFA=\"tc filter add dev $WAN_DEV\"\n"
442 "TQA_IMQ=\"tc qdisc add dev $IMQ_DEV\"\n"
443 "TCA_IMQ=\"tc class add dev $IMQ_DEV\"\n"
444 "TFA_IMQ=\"tc filter add dev $IMQ_DEV\"\n"
449 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
450 "\t$TQA root handle 1: htb default %u r2q %u\n"
451 "\t$TCA parent 1: classid 1:1 htb rate %ukbit ceil %ukbit %s overhead %u atm\n",
454 nvram_get_int("qos_pfifo") ? "pfifo limit 256" : "sfq perturb 10",
455 qosDefaultClassId
, r2q
,
456 bw
, bw
, burst_root
, overhead
);
459 inuse
= nvram_get_int("qos_inuse");
461 g
= buf
= strdup(nvram_safe_get("qos_orates"));
462 for (i
= 0; i
< 10; ++i
) {
463 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
)) break;
465 if ((inuse
& (1 << i
)) == 0) continue;
467 // check if we've got a percentage definition in the form of "rate-ceiling"
468 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) != 2) || (rate
< 1)) continue; // 0=off
470 if (ceil
> 0) sprintf(s
, "ceil %ukbit ", calc(bw
, ceil
));
476 "# egress %d: %u-%u%%\n"
477 "\t$TCA parent 1:1 classid 1:%d htb rate %ukbit %s %s prio %d quantum %u\n"
478 "\t$TQA parent 1:%d handle %d: $Q\n"
479 "\t$TFA parent 1: prio %d protocol ip handle %d fw flowid 1:%d\n",
481 x
, calc(bw
, rate
), s
, burst_leaf
, i
+1, mtu
,
486 "# egress %d: %u-%u%%\n"
487 "\t$TCA parent 1:1 classid 1:%d htb rate %ukbit %s %s prio %d quantum %u overhead %u atm\n"
488 "\t$TQA parent 1:%d handle %d: $Q\n"
489 "\t$TFA parent 1: prio %d protocol ip handle %d fw flowid 1:%d\n",
491 x
, calc(bw
, rate
), s
, burst_leaf
, i
+1, mtu
, overhead
,
498 // "\t$TFA parent 1: prio 10 protocol ip u32 match ip tos 0x10 0xff flowid :10\n" // TOS EF -> Highest
502 if (nvram_match("qos_ack", "1")) {
505 "\t$TFA parent 1: prio 15 protocol ip u32 "
506 "match ip protocol 6 0xff " // TCP
507 "match u8 0x05 0x0f at 0 " // IP header length
508 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
509 "match u8 0x10 0xff at 33 " // ACK only
512 if (nvram_match("qos_icmp", "1")) {
513 fputs("\n\t$TFA parent 1: prio 14 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
518 if (nvram_get_int("qos_ack")) {
521 "\t$TFA parent 1: prio 14 protocol ip u32 "
522 "match ip protocol 6 0xff " // TCP
523 "match u8 0x05 0x0f at 0 " // IP header length
524 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
525 "match u8 0x10 0xff at 33 " // ACK only
529 if (nvram_get_int("qos_syn")) {
535 "\t$TFA parent 1: prio 15 protocol ip u32 "
536 "match ip protocol 6 0xff " // TCP
537 "match u8 0x05 0x0f at 0 " // IP header length
538 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
539 "match u8 0x02 0xff at 33 " // SYN only
542 "\t$TFA parent 1: prio 16 protocol ip u32 "
543 "match ip protocol 6 0xff " // TCP
544 "match u8 0x05 0x0f at 0 " // IP header length
545 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
546 "match u8 0x12 0xff at 33 " // SYN,ACK
550 if (nvram_get_int("qos_fin")) {
556 "\t$TFA parent 1: prio 17 protocol ip u32 "
557 "match ip protocol 6 0xff " // TCP
558 "match u8 0x05 0x0f at 0 " // IP header length
559 "match u8 0x11 0xff at 33 " // ACK,FIN
562 "\t$TFA parent 1: prio 18 protocol ip u32 "
563 "match ip protocol 6 0xff " // TCP
564 "match u8 0x05 0x0f at 0 " // IP header length
565 "match u8 0x01 0xff at 33 " // FIN
569 if (nvram_get_int("qos_rst")) {
574 "\t$TFA parent 1: prio 19 protocol ip u32 "
575 "match ip protocol 6 0xff " // TCP
576 "match u8 0x05 0x0f at 0 " // IP header length
577 "match u8 0x14 0xff at 33 " // ACK,RST
580 "\t$TFA parent 1: prio 20 protocol ip u32 "
581 "match ip protocol 6 0xff " // TCP
582 "match u8 0x05 0x0f at 0 " // IP header length
583 "match u8 0x04 0xff at 33 " // RST
588 if (nvram_get_int("qos_icmp")) {
589 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
600 if (nvram_get_int("qos_ack")) {
603 "\t$TFA parent 1: prio 14 protocol ip u32 "
604 "match ip protocol 6 0xff " // TCP
605 "match u8 0x05 0x0f at 0 " // IP header length
606 // "match u16 0x0000 0xff80 at 2 " // total length (0-127)
607 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
608 "match u8 0x10 0xff at 33 " // ACK only
612 if (nvram_get_int("qos_syn")) {
615 "\t$TFA parent 1: prio 15 protocol ip u32 "
616 "match ip protocol 6 0xff " // TCP
617 "match u8 0x05 0x0f at 0 " // IP header length
618 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
619 "match u8 0x02 0x02 at 33 " // SYN,*
623 if (nvram_get_int("qos_fin")) {
626 "\t$TFA parent 1: prio 17 protocol ip u32 "
627 "match ip protocol 6 0xff " // TCP
628 "match u8 0x05 0x0f at 0 " // IP header length
629 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
630 "match u8 0x01 0x01 at 33 " // FIN,*
634 if (nvram_get_int("qos_rst")) {
637 "\t$TFA parent 1: prio 19 protocol ip u32 "
638 "match ip protocol 6 0xff " // TCP
639 "match u8 0x05 0x0f at 0 " // IP header length
640 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
641 "match u8 0x04 0x04 at 33 " // RST,*
645 if (nvram_get_int("qos_icmp")) {
646 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f
);
651 //// INCOMING TRAFFIC SHAPING
655 overhead
= strtoul(nvram_safe_get("atm_overhead"), NULL
, 10);
657 g
= buf
= strdup(nvram_safe_get("qos_irates"));
659 for (i
= 0; i
< 10; ++i
)
661 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
))
666 if ((inuse
& (1 << i
)) == 0)
671 // check if we've got a percentage definition in the form of "rate-ceiling"
672 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) != 2) || (rate
< 1))
678 unsigned int classid
= ((unsigned int)i
+ 1) * 10;
681 unsigned int priority
= (unsigned int)i
+ 1; //prios 1-10 - Toastman
684 unsigned int rateInKilobitsPerSecond
=
685 calc(incomingBandwidthInKilobitsPerSecond
, rate
);
688 unsigned int ceilingInKilobitsPerSecond
=
689 calc(incomingBandwidthInKilobitsPerSecond
, ceil
);
691 // burst rate (2% of the classes' rate) - don't know if we should use this
692 //Commented out KDB 20130531 - produces compiler warning about being unused!
693 // unsigned int burstRateInBitsPerSecond =
694 // (rateInKilobitsPerSecond * 1000) / 50;
697 if ((incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * r2q
) < mtu
)
699 r2q
= (incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * mtu
);
700 if (r2q
< 1) r2q
= 1;
702 else if ((incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * r2q
) > 60000)
704 r2q
= (incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * 60000) + 1;
713 "\tip link set $IMQ_DEV up\n"
714 "\ttc qdisc del dev $IMQ_DEV 2>/dev/null\n"
715 "\t$TQA_IMQ handle 1: root htb default %u r2q %u\n"
716 "\t$TCA_IMQ parent 1: classid 1:1 htb rate %ukbit ceil %ukbit\n",
717 qosDefaultClassId
, r2q
,
718 incomingBandwidthInKilobitsPerSecond
,
719 incomingBandwidthInKilobitsPerSecond
);
723 "\tip link set $IMQ_DEV up\n"
724 "\ttc qdisc del dev $IMQ_DEV 2>/dev/null\n"
725 "\t$TQA_IMQ handle 1: root htb default %u r2q %u\n"
726 "\t$TCA_IMQ parent 1: classid 1:1 htb rate %ukbit ceil %ukbit overhead %u atm\n",
727 qosDefaultClassId
, r2q
,
728 incomingBandwidthInKilobitsPerSecond
,
729 incomingBandwidthInKilobitsPerSecond
, overhead
);
736 "\t# class id %u: rate %ukbit ceil %ukbit\n",
737 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
);
742 "\t$TCA_IMQ parent 1:1 classid 1:%u htb rate %ukbit ceil %ukbit prio %u quantum %u\n",
743 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
, priority
, mtu
);
747 "\t$TCA_IMQ parent 1:1 classid 1:%u htb rate %ukbit ceil %ukbit prio %u quantum %u overhead %u atm\n",
748 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
, priority
, mtu
, overhead
);
753 "\t$TQA_IMQ parent 1:%u handle %u: $Q\n",
758 "\t$TFA_IMQ parent 1: prio %u protocol ip handle %u fw flowid 1:%u \n",
759 classid
, priority
, classid
);
764 //// write commands which adds rule to forward traffic to IMQ device
767 "\t# set up the IMQ device (otherwise this won't work) to limit the incoming data\n"
768 "\tip link set $IMQ_DEV up\n",
774 "\tip link set $IMQ_DEV down\n"
775 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
776 "\ttc qdisc del dev $IMQ_DEV root 2>/dev/null\n"
780 "\techo \"... OUTGOING QDISCS AND CLASSES FOR $WAN_DEV\"\n"
782 "\ttc -s -d qdisc ls dev $WAN_DEV\n"
784 "\ttc -s -d class ls dev $WAN_DEV\n"
787 "\techo \"... INCOMING QDISCS AND CLASSES FOR $WAN_DEV (routed through $IMQ_DEV)\"\n"
789 "\ttc -s -d qdisc ls dev $IMQ_DEV\n"
791 "\ttc -s -d class ls dev $IMQ_DEV\n"
798 eval((char *)qosfn
, "start");
803 eval((char *)qosfn
, "stop");
805 if (!nvram_match("debug_keepfiles", "1")) {
813 PREROUTING (mn) ----> x ----> FORWARD (f) ----> + ----> POSTROUTING (n)
817 INPUT (f) OUTPUT (mnf)