4 Copyright (C) 2006-2009 Jonathan Zarate
12 static const char *qosfn
= "/etc/qos";
13 static const char *qosImqDeviceNumberString
= "0";
14 static const char *qosImqDeviceString
= "imq0";
22 char *addr_type
, *addr
;
24 char *port_type
, *port
;
43 unsigned long prev_max
;
50 if (!nvram_get_int("qos_enable")) return;
60 "-A QOSO -j CONNMARK --restore-mark --mask 0xff\n"
61 "-A QOSO -m connmark ! --mark 0/0x0f00 -j RETURN\n");
63 g
= buf
= strdup(nvram_safe_get("qos_orules"));
68 addr_type<addr<proto<port_type<port<ipp2p<L7<bcount<dscp<class_prio<desc
76 ip/mac if addr_type == 1-3
82 if proto == -1,tcp,udp:
88 port # if proto == -1,tcp,udp
94 numeric (0:63) - dscp value
95 afXX, csX, be, ef - dscp class
97 0-10 // Changed from 8 in pkt_sched.h - Toastman
102 if ((p
= strsep(&g
, ">")) == NULL
) break;
103 i
= vstrsep(p
, "<", &addr_type
, &addr
, &proto
, &port_type
, &port
, &ipp2p
, &layer7
, &bcount
, &dscp
, &class_prio
, &desc
);
106 // fixup < v1.28.XX55
112 // fixup < v0.08 // !!! temp
118 else if (i
!= 11) continue;
120 class_num
= atoi(class_prio
);
121 if ((class_num
< 0) || (class_num
> 9)) continue;
126 if ((inuse
& i
) == 0) {
138 if (ipt_ipp2p(ipp2p
, app
)) v4v6_ok
&= ~IPT_V6
;
139 else ipt_layer7(layer7
, app
);
141 v4v6_ok
&= ~IPT_V6
; // temp: l7 not working either!
143 // IPP2P and L7 rules may need more than one packet before matching
144 // so port-based rules that come after them in the list can't be sticky
145 // or else these rules might never match.
151 if (ipt_dscp(dscp
, s
)) {
153 v4v6_ok
&= ~IPT_V6
; // dscp ipv6 match is not present in K2.4
159 if ((*addr_type
== '1') || (*addr_type
== '2')) { // match ip
160 v4v6_ok
&= ipt_addr(saddr
, sizeof(saddr
), addr
, (*addr_type
== '1') ? "dst" : "src",
161 v4v6_ok
, (v4v6_ok
==IPT_V4
), "QoS", desc
);
162 if (!v4v6_ok
) continue;
164 else if (*addr_type
== '3') { // match mac
165 sprintf(saddr
, "-m mac --mac-source %s", addr
); // (-m mac modified, returns !match in OUTPUT)
172 // -m connbytes --connbytes x:y --connbytes-dir both --connbytes-mode bytes
174 min
= strtoul(bcount
, &p
, 10);
176 strcat(end
, " -m connbytes --connbytes-mode bytes --connbytes-dir both --connbytes ");
179 sprintf(end
+ strlen(end
), "%lu:", min
* 1024);
182 max
= strtoul(p
, NULL
, 10);
183 sprintf(end
+ strlen(end
), "%lu:%lu", min
* 1024, (max
* 1024) - 1);
186 // Create table of connbytes sizes, pass appropriate connections there
187 // and only continue processing them if mark was wiped
190 "-I QOSO 3 -m connmark ! --mark 0/0xff000 -j QOSSIZE\n"
191 "-I QOSO 4 -m connmark ! --mark 0/0xff000 -j RETURN\n");
193 if (max
!= prev_max
&& sizegroup
<255) {
194 class_flag
= ++sizegroup
<< 12;
196 ip46t_flagged_write(v4v6_ok
,
197 "-A QOSSIZE -m connmark --mark 0x%x/0xff000"
198 " -m connbytes --connbytes-mode bytes --connbytes-dir both --connbytes %lu: -j CONNMARK --set-return 0x00000/0xFF\n",
199 (sizegroup
<< 12), (max
* 1024));
202 class_flag
= sizegroup
<< 12;
214 class_num
|= class_flag
;
215 class_num
|= rule_num
<< 20;
216 sprintf(end
+ strlen(end
), " -j CONNMARK --set-return 0x%x/0xFF\n", class_num
);
219 proto_num
= atoi(proto
);
220 if (proto_num
> -2) {
221 if ((proto_num
== 6) || (proto_num
== 17) || (proto_num
== -1)) {
222 if (*port_type
!= 'a') {
223 if ((*port_type
== 'x') || (strchr(port
, ','))) {
224 // dst-or-src port matches, and anything with multiple lists "," use multiport
225 sprintf(sport
, "-m multiport --%sports %s", (*port_type
== 's') ? "s" : ((*port_type
== 'd') ? "d" : ""), port
);
228 // single or simple x:y range, use built-in tcp/udp match
229 sprintf(sport
, "--%sport %s", (*port_type
== 's') ? "s" : ((*port_type
== 'd') ? "d" : ""), port
);
235 if (proto_num
!= 6) ip46t_flagged_write(v4v6_ok
, "-A %s -p %s %s %s %s", chain
, "udp", sport
, saddr
, end
);
236 if (proto_num
!= 17) ip46t_flagged_write(v4v6_ok
, "-A %s -p %s %s %s %s", chain
, "tcp", sport
, saddr
, end
);
239 ip46t_flagged_write(v4v6_ok
, "-A %s -p %d %s %s", chain
, proto_num
, saddr
, end
);
242 else { // any protocol
243 ip46t_flagged_write(v4v6_ok
, "-A %s %s %s", chain
, saddr
, end
);
249 qface
= wanfaces
.iface
[0].name
;
251 i
= nvram_get_int("qos_default");
252 if ((i
< 0) || (i
> 9)) i
= 3; // "low"
254 class_num
|= 0xFF00000; // use rule_num=255 for default
255 ip46t_write("-A QOSO -j CONNMARK --set-return 0x%x\n", class_num
);
258 "-A FORWARD -o %s -j QOSO\n"
259 "-A OUTPUT -o %s -j QOSO\n",
265 "-A FORWARD -o %s -j QOSO\n"
266 "-A OUTPUT -o %s -j QOSO\n",
271 inuse
|= (1 << i
) | 1; // default and highest are always built
272 sprintf(s
, "%d", inuse
);
273 nvram_set("qos_inuse", s
);
276 g
= buf
= strdup(nvram_safe_get("qos_irates"));
278 for (i
= 0; i
< 10; ++i
)
280 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
)) continue;
281 if ((inuse
& (1 << i
)) == 0) continue;
286 // check if we've got a percentage definition in the form of "rate-ceiling"
288 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) == 2) && (rate
>= 1))
290 ipt_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", qface
);
293 if (nvram_get_int("qos_udp")) {
294 ipt_write("-A PREROUTING -i %s -p tcp -j IMQ --todev %s\n", qface
, qosImqDeviceNumberString
); // pass only tcp
297 ipt_write("-A PREROUTING -i %s -j IMQ --todev %s\n", qface
, qosImqDeviceNumberString
); // pass everything thru ingress
303 ip6t_write("-A PREROUTING -i %s -j CONNMARK --restore-mark --mask 0xff\n", wan6face
);
305 if (nvram_get_int("qos_udp")) {
306 ip6t_write("-A PREROUTING -i %s -p tcp -j IMQ --todev %s\n", wan6face
, qosImqDeviceNumberString
); // pass only tcp
309 ip6t_write("-A PREROUTING -i %s -j IMQ --todev %s\n", wan6face
, qosImqDeviceNumberString
); // pass everything thru ingress
319 static unsigned calc(unsigned bw
, unsigned pct
)
321 unsigned n
= ((unsigned long)bw
* pct
) / 100;
322 return (n
< 2) ? 2 : n
;
332 unsigned int incomingBandwidthInKilobitsPerSecond
;
335 unsigned int qosDefaultClassId
;
344 qosDefaultClassId
= (nvram_get_int("qos_default") + 1) * 10;
345 incomingBandwidthInKilobitsPerSecond
= strtoul(nvram_safe_get("qos_ibw"), NULL
, 10);
348 x
= nvram_get_int("ne_vegas");
351 char alpha
[10], beta
[10], gamma
[10];
352 sprintf(alpha
, "alpha=%d", nvram_get_int("ne_valpha"));
353 sprintf(beta
, "beta=%d", nvram_get_int("ne_vbeta"));
354 sprintf(gamma
, "gamma=%d", nvram_get_int("ne_vgamma"));
355 modprobe("tcp_vegas", alpha
, beta
, gamma
);
356 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "vegas", 0, 0);
359 modprobe_r("tcp_vegas");
360 f_write_string("/proc/sys/net/ipv4/tcp_congestion_control", "", FW_NEWLINE
, 0);
363 f_write_string("/proc/sys/net/ipv4/tcp_vegas_cong_avoid", x
? "1" : "0", 0, 0);
365 f_write_string("/proc/sys/net/ipv4/tcp_vegas_alpha", nvram_safe_get("ne_valpha"), 0, 0);
366 f_write_string("/proc/sys/net/ipv4/tcp_vegas_beta", nvram_safe_get("ne_vbeta"), 0, 0);
367 f_write_string("/proc/sys/net/ipv4/tcp_vegas_gamma", nvram_safe_get("ne_vgamma"), 0, 0);
371 if (!nvram_get_int("qos_enable")) return;
373 if ((f
= fopen(qosfn
, "w")) == NULL
) return;
375 i
= nvram_get_int("qos_burst0");
376 if (i
> 0) sprintf(burst_root
, "burst %dk", i
);
377 else burst_root
[0] = 0;
378 i
= nvram_get_int("qos_burst1");
379 if (i
> 0) sprintf(burst_leaf
, "burst %dk", i
);
380 else burst_leaf
[0] = 0;
382 mtu
= strtoul(nvram_safe_get("wan_mtu"), NULL
, 10);
383 bw
= strtoul(nvram_safe_get("qos_obw"), NULL
, 10);
386 if ((bw
* 1000) / (8 * r2q
) < mtu
) {
387 r2q
= (bw
* 1000) / (8 * mtu
);
388 if (r2q
< 1) r2q
= 1;
389 } else if ((bw
* 1000) / (8 * r2q
) > 60000) {
390 r2q
= (bw
* 1000) / (8 * 60000) + 1;
397 "TQA=\"tc qdisc add dev $WAN_DEV\"\n"
398 "TCA=\"tc class add dev $WAN_DEV\"\n"
399 "TFA=\"tc filter add dev $WAN_DEV\"\n"
400 "TQA_IMQ=\"tc qdisc add dev $IMQ_DEV\"\n"
401 "TCA_IMQ=\"tc class add dev $IMQ_DEV\"\n"
402 "TFA_IMQ=\"tc filter add dev $IMQ_DEV\"\n"
407 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
408 "\t$TQA root handle 1: htb default %u r2q %u\n"
409 "\t$TCA parent 1: classid 1:1 htb rate %ukbit ceil %ukbit %s\n",
412 nvram_get_int("qos_pfifo") ? "pfifo limit 256" : "sfq perturb 10",
413 qosDefaultClassId
, r2q
,
416 inuse
= nvram_get_int("qos_inuse");
418 g
= buf
= strdup(nvram_safe_get("qos_orates"));
419 for (i
= 0; i
< 10; ++i
) {
420 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
)) break;
422 if ((inuse
& (1 << i
)) == 0) continue;
424 // check if we've got a percentage definition in the form of "rate-ceiling"
425 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) != 2) || (rate
< 1)) continue; // 0=off
427 if (ceil
> 0) sprintf(s
, "ceil %ukbit ", calc(bw
, ceil
));
431 "# egress %d: %u-%u%%\n"
432 "\t$TCA parent 1:1 classid 1:%d htb rate %ukbit %s %s prio %d quantum %u\n"
433 "\t$TQA parent 1:%d handle %d: $Q\n"
434 "\t$TFA parent 1: prio %d protocol ip handle %d fw flowid 1:%d\n",
436 // x, calc(bw, rate), s, burst_leaf, (i >= 6) ? 7 : (i + 1), mtu,
437 x
, calc(bw
, rate
), s
, burst_leaf
, i
, mtu
, //Toastman
443 // "\t$TFA parent 1: prio 10 protocol ip u32 match ip tos 0x10 0xff flowid :10\n" // TOS EF -> Highest
447 if (nvram_match("qos_ack", "1")) {
450 "\t$TFA parent 1: prio 15 protocol ip u32 "
451 "match ip protocol 6 0xff " // TCP
452 "match u8 0x05 0x0f at 0 " // IP header length
453 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
454 "match u8 0x10 0xff at 33 " // ACK only
457 if (nvram_match("qos_icmp", "1")) {
458 fputs("\n\t$TFA parent 1: prio 14 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
463 if (nvram_get_int("qos_ack")) {
466 "\t$TFA parent 1: prio 14 protocol ip u32 "
467 "match ip protocol 6 0xff " // TCP
468 "match u8 0x05 0x0f at 0 " // IP header length
469 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
470 "match u8 0x10 0xff at 33 " // ACK only
474 if (nvram_get_int("qos_syn")) {
480 "\t$TFA parent 1: prio 15 protocol ip u32 "
481 "match ip protocol 6 0xff " // TCP
482 "match u8 0x05 0x0f at 0 " // IP header length
483 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
484 "match u8 0x02 0xff at 33 " // SYN only
487 "\t$TFA parent 1: prio 16 protocol ip u32 "
488 "match ip protocol 6 0xff " // TCP
489 "match u8 0x05 0x0f at 0 " // IP header length
490 "match u16 0x0000 0xff80 at 2 " // total length (0-127)
491 "match u8 0x12 0xff at 33 " // SYN,ACK
495 if (nvram_get_int("qos_fin")) {
501 "\t$TFA parent 1: prio 17 protocol ip u32 "
502 "match ip protocol 6 0xff " // TCP
503 "match u8 0x05 0x0f at 0 " // IP header length
504 "match u8 0x11 0xff at 33 " // ACK,FIN
507 "\t$TFA parent 1: prio 18 protocol ip u32 "
508 "match ip protocol 6 0xff " // TCP
509 "match u8 0x05 0x0f at 0 " // IP header length
510 "match u8 0x01 0xff at 33 " // FIN
514 if (nvram_get_int("qos_rst")) {
519 "\t$TFA parent 1: prio 19 protocol ip u32 "
520 "match ip protocol 6 0xff " // TCP
521 "match u8 0x05 0x0f at 0 " // IP header length
522 "match u8 0x14 0xff at 33 " // ACK,RST
525 "\t$TFA parent 1: prio 20 protocol ip u32 "
526 "match ip protocol 6 0xff " // TCP
527 "match u8 0x05 0x0f at 0 " // IP header length
528 "match u8 0x04 0xff at 33 " // RST
533 if (nvram_get_int("qos_icmp")) {
534 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f);
545 if (nvram_get_int("qos_ack")) {
548 "\t$TFA parent 1: prio 14 protocol ip u32 "
549 "match ip protocol 6 0xff " // TCP
550 "match u8 0x05 0x0f at 0 " // IP header length
551 // "match u16 0x0000 0xff80 at 2 " // total length (0-127)
552 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
553 "match u8 0x10 0xff at 33 " // ACK only
557 if (nvram_get_int("qos_syn")) {
560 "\t$TFA parent 1: prio 15 protocol ip u32 "
561 "match ip protocol 6 0xff " // TCP
562 "match u8 0x05 0x0f at 0 " // IP header length
563 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
564 "match u8 0x02 0x02 at 33 " // SYN,*
568 if (nvram_get_int("qos_fin")) {
571 "\t$TFA parent 1: prio 17 protocol ip u32 "
572 "match ip protocol 6 0xff " // TCP
573 "match u8 0x05 0x0f at 0 " // IP header length
574 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
575 "match u8 0x01 0x01 at 33 " // FIN,*
579 if (nvram_get_int("qos_rst")) {
582 "\t$TFA parent 1: prio 19 protocol ip u32 "
583 "match ip protocol 6 0xff " // TCP
584 "match u8 0x05 0x0f at 0 " // IP header length
585 "match u16 0x0000 0xffc0 at 2 " // total length (0-63)
586 "match u8 0x04 0x04 at 33 " // RST,*
590 if (nvram_get_int("qos_icmp")) {
591 fputs("\n\t$TFA parent 1: prio 13 protocol ip u32 match ip protocol 1 0xff flowid 1:10\n", f
);
596 //// INCOMING TRAFFIC SHAPING
601 g
= buf
= strdup(nvram_safe_get("qos_irates"));
603 for (i
= 0; i
< 10; ++i
)
605 if ((!g
) || ((p
= strsep(&g
, ",")) == NULL
))
610 if ((inuse
& (1 << i
)) == 0)
615 // check if we've got a percentage definition in the form of "rate-ceiling"
616 if ((sscanf(p
, "%u-%u", &rate
, &ceil
) != 2) || (rate
< 1))
622 unsigned int classid
= ((unsigned int)i
+ 1) * 10;
625 unsigned int priority
= (unsigned int)i
+ 1;
628 unsigned int rateInKilobitsPerSecond
=
629 calc(incomingBandwidthInKilobitsPerSecond
, rate
);
632 unsigned int ceilingInKilobitsPerSecond
=
633 calc(incomingBandwidthInKilobitsPerSecond
, ceil
);
635 // burst rate (2% of the classes' rate) - don't know if we should use this
636 unsigned int burstRateInBitsPerSecond
=
637 (rateInKilobitsPerSecond
* 1000) / 50;
640 if ((incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * r2q
) < mtu
)
642 r2q
= (incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * mtu
);
643 if (r2q
< 1) r2q
= 1;
645 else if ((incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * r2q
) > 60000)
647 r2q
= (incomingBandwidthInKilobitsPerSecond
* 1000) / (8 * 60000) + 1;
655 "\tip link set $IMQ_DEV up\n"
656 "\ttc qdisc del dev $IMQ_DEV 2>/dev/null\n"
657 "\t$TQA_IMQ handle 1: root htb default %u r2q %u\n"
658 "\t$TCA_IMQ parent 1: classid 1:1 htb rate %ukbit ceil %ukbit\n",
659 qosDefaultClassId
, r2q
,
660 incomingBandwidthInKilobitsPerSecond
,
661 incomingBandwidthInKilobitsPerSecond
);
667 "\t# class id %u: rate %ukbit ceil %ukbit\n",
668 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
);
672 "\t$TCA_IMQ parent 1:1 classid 1:%u htb rate %ukbit ceil %ukbit prio %u quantum %u\n",
673 classid
, rateInKilobitsPerSecond
, ceilingInKilobitsPerSecond
, priority
, mtu
);
677 "\t$TQA_IMQ parent 1:%u handle %u: $Q\n",
682 "\t$TFA_IMQ parent 1: prio %u protocol ip handle %u fw flowid 1:%u \n",
683 classid
, priority
, classid
);
688 //// write commands which adds rule to forward traffic to IMQ device
691 "\t# set up the IMQ device (otherwise this won't work) to limit the incoming data\n"
692 "\tip link set $IMQ_DEV up\n",
698 "\tip link set $IMQ_DEV down\n"
699 "\ttc qdisc del dev $WAN_DEV root 2>/dev/null\n"
700 "\ttc qdisc del dev $IMQ_DEV root 2>/dev/null\n"
704 "\techo \"... OUTGOING QDISCS AND CLASSES FOR $WAN_DEV\"\n"
706 "\ttc -s -d qdisc ls dev $WAN_DEV\n"
708 "\ttc -s -d class ls dev $WAN_DEV\n"
711 "\techo \"... INCOMING QDISCS AND CLASSES FOR $WAN_DEV (routed through $IMQ_DEV)\"\n"
713 "\ttc -s -d qdisc ls dev $IMQ_DEV\n"
715 "\ttc -s -d class ls dev $IMQ_DEV\n"
722 eval((char *)qosfn
, "start");
727 eval((char *)qosfn
, "stop");
729 if (!nvram_match("debug_keepfiles", "1")) {
737 PREROUTING (mn) ----> x ----> FORWARD (f) ----> + ----> POSTROUTING (n)
741 INPUT (f) OUTPUT (mnf)