Development issues regarding the cerowrt test router project
 help / color / mirror / Atom feed
* [Cerowrt-devel] documentation review request and out of tree cake builds for openwrt/etc.
@ 2015-04-28 18:04 Dave Taht
  2015-04-28 20:52 ` [Cerowrt-devel] [Cake] " Jonathan Morton
  0 siblings, 1 reply; 8+ messages in thread
From: Dave Taht @ 2015-04-28 18:04 UTC (permalink / raw)
  To: cake, bloat, cerowrt-devel

1) I have burned much of the morning updating this:

http://www.bufferbloat.net/projects/codel/wiki/Cake

Reviewers wanted.

2) I am not sure of the right configuration to push stuff to the
iproute2-cake repo (removing sfq_codel, and there is an important
bugfix in the mainline iproute2 that needs to be pushed into it)

3) I have pushed support for building cake out of tree in openwrt to
the ceropackages repo (for now), but have not created a new
iproute2-cake for it due to item 2. My intent would be to get both
into openwrt chaos calmer for further testing... in some repo or
another that wasn't ceropackages. the routing repo?

4) http://www.dslreports.com/forum/news,133536~fmode=flat~days=2000
has some good convo going on also.

-- 
Dave Täht
Open Networking needs **Open Source Hardware**

https://plus.google.com/u/0/+EricRaymond/posts/JqxCe2pFr67

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-28 18:04 [Cerowrt-devel] documentation review request and out of tree cake builds for openwrt/etc Dave Taht
@ 2015-04-28 20:52 ` Jonathan Morton
  2015-04-30  0:10   ` Dave Taht
  0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Morton @ 2015-04-28 20:52 UTC (permalink / raw)
  To: Dave Taht; +Cc: cake, cerowrt-devel, bloat

[-- Attachment #1: Type: text/plain, Size: 469 bytes --]

> An integral shaper (that can be on or off or tuned dynamically)
> ---
> is much "tighter" than htb - works on current low end hardware without
running out of cpu. See attached graphs.

This seems to imply that "tighter" means "uses less CPU". In fact they are
two separate benefits; "tighter" means "bursts less".

Also, what graphs?

As for installing kernel headers, on Debian based distros the right package
should be linux-headers-`uname -r` .

- Jonathan Morton

[-- Attachment #2: Type: text/html, Size: 623 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-28 20:52 ` [Cerowrt-devel] [Cake] " Jonathan Morton
@ 2015-04-30  0:10   ` Dave Taht
  2015-04-30 14:38     ` Jonathan Morton
  2015-04-30 14:52     ` Jonathan Morton
  0 siblings, 2 replies; 8+ messages in thread
From: Dave Taht @ 2015-04-30  0:10 UTC (permalink / raw)
  To: Jonathan Morton; +Cc: cake, cerowrt-devel, bloat

On Tue, Apr 28, 2015 at 1:52 PM, Jonathan Morton <chromatix99@gmail.com> wrote:
>> An integral shaper (that can be on or off or tuned dynamically)
>> ---
>> is much "tighter" than htb - works on current low end hardware without
>> running out of cpu. See attached graphs.
>
> This seems to imply that "tighter" means "uses less CPU". In fact they are
> two separate benefits; "tighter" means "bursts less".

OK, will fix.

> Also, what graphs?

I did not put them up because I was fiddling with the dslreports test
at the same time as a rrul test and I got a result that I do not
understand.

http://snapon.lab.bufferbloat.net/~d/dsl/vsdslreports_at_the_sametime.png

(cake and pie results in the same dir, unplotted)

dslreports test result: http://www.dslreports.com/speedtest/384651

I have not fully internalized what a 10x1 ratio of down to up
bandwidth "looks like" - where a full download stream would basically
fill half the uplink with acks (every other ack) or the whole thing
(on tcp's acking on every packet), and I have internalized that aqms
have little effect on pure ack traffic... but....

What I had expected to see was about a 2ms (tops!) further increase in
measured inbound latency here. A single full mtu packet at 100Mbit is
130us, and I was under the impression the dslreports cable test was 16
inbound flows, so 130us * 16 = 2ms. The number of acks in flight
should have been constant, but got a great deal more service time, but
that is not evident from the uplink...

Where is the extra latency coming from at T+50? It is repeatable no
matter the inbound qdisc (tried pie, fq_codel, cake) This bump from 4
flows to 20 should ultimately have been handled, AND with FQ in place,
the measure at 100mbit should actually have stayed pretty flat, just
as it did for outbound. Instead, 10ms is added.

A) I have generally worried about the effects of pacing on packet
aggregation in cable modems, etc. HTB is doing a bit of pacing that
might be affecting media access opportunities here, and/or we are
running out of room to wedge acks into an aggregate.
B) Also DRR tends to release bunches of acks per TCP flow which in
turn releases a bunch in response...
C) NAPI?
D) Servicing tons of acks eats cpu
E) Something else?

Ah, well, I guess I gotta go try a test on ethernet at the same rates
and setup, and fiddle with owamp...

The topology here was:

OSX - wifi          - rangeley box - cablemodem - internet
Linux - ethernet - rangeley box

>
> As for installing kernel headers, on Debian based distros the right package
> should be linux-headers-`uname -r` .
>
> - Jonathan Morton



-- 
Dave Täht
Open Networking needs **Open Source Hardware**

https://plus.google.com/u/0/+EricRaymond/posts/JqxCe2pFr67

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-30  0:10   ` Dave Taht
@ 2015-04-30 14:38     ` Jonathan Morton
  2015-04-30 15:17       ` Dave Taht
  2015-04-30 14:52     ` Jonathan Morton
  1 sibling, 1 reply; 8+ messages in thread
From: Jonathan Morton @ 2015-04-30 14:38 UTC (permalink / raw)
  To: Dave Taht; +Cc: cake, cerowrt-devel, bloat

[-- Attachment #1: Type: text/plain, Size: 679 bytes --]

It took me a while to get around to thinking about this, partly because my
phone inexplicably refuses to believe snapon exists.

I have two possible explanations for these results. Maybe both apply to
some extent.

Dropping packets rather than marking them results in an increase in ack
density in the reverse direction, because delayed acks get temporarily
disabled. The strength of this effect depends on the BDP and the depth of
delayed acks.

Increasing the number of simultaneous flows might increase the CPU load of
connection tracking for NAT. Are you shaping and doing NAT on the same box?
I think this might be the basic reason for increased latency.

- Jonathan Morton

[-- Attachment #2: Type: text/html, Size: 766 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-30  0:10   ` Dave Taht
  2015-04-30 14:38     ` Jonathan Morton
@ 2015-04-30 14:52     ` Jonathan Morton
  1 sibling, 0 replies; 8+ messages in thread
From: Jonathan Morton @ 2015-04-30 14:52 UTC (permalink / raw)
  To: Dave Taht; +Cc: cake, cerowrt-devel, bloat

[-- Attachment #1: Type: text/plain, Size: 760 bytes --]

I'm not concerned about aggregation effects on cable, because it's not
station specific as it is on Wi-Fi. It might be a source of one extra
access grant delay at most; after that there'll be enough packets in the
modem's FIFO to justify a full sized grant. Here the modem's buffer really
does exist for a good reason, and we can rely on it to do the job.

I'm also not concerned about ack bunching, because realistically that isn't
really caused by FQ. Given a 10:1 bandwidth ratio, and a 3:1 delayed ack
factor, there'll be 3.33 acks for each data packet in the slow direction,
while at our default 300 quantum the DRR will cycle five times per data
packet. So acks for a given flow will only be delivered bunched if they
arrived bunched.

- Jonathan Morton

[-- Attachment #2: Type: text/html, Size: 851 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-30 14:38     ` Jonathan Morton
@ 2015-04-30 15:17       ` Dave Taht
  2015-05-01  9:50         ` Kevin Darbyshire-Bryant
  2015-05-01 11:06         ` Kevin Darbyshire-Bryant
  0 siblings, 2 replies; 8+ messages in thread
From: Dave Taht @ 2015-04-30 15:17 UTC (permalink / raw)
  To: Jonathan Morton; +Cc: cake, cerowrt-devel, bloat

On Thu, Apr 30, 2015 at 7:38 AM, Jonathan Morton <chromatix99@gmail.com> wrote:
> It took me a while to get around to thinking about this, partly because my
> phone inexplicably refuses to believe snapon exists.

It has an old dnssec signed dns tree, using the isc dlv, which turned
out to somewhat break older versions of dnsmasq, if that helps any.
Dire need for some sysadmin help on bufferbloat.net, the cruft has accumulated.

> I have two possible explanations for these results. Maybe both apply to some
> extent.
>
> Dropping packets rather than marking them results in an increase in ack
> density in the reverse direction, because delayed acks get temporarily
> disabled. The strength of this effect depends on the BDP and the depth of
> delayed acks.

Hmm.

>
> Increasing the number of simultaneous flows might increase the CPU load of
> connection tracking for NAT. Are you shaping and doing NAT on the same box?
> I think this might be the basic reason for increased latency.

That particular setup (yurtlab) offloads the NAT to the cable modem.
The 110_11Mbit shaping was of course, done on the rangeley. It is
possible given the older kernel that perhaps I could be hitting the
same power mgmt issue that someone else here ran into earlier, or
merely the kernel is weird, or something new like xmit_more is messing
up life.

I returned to another location (gf) and tested 50mbit down/5mbit up
(cerowrt 3.10.50-1 in front of a live cablemodem, same two boxes
driving the test) up for much of the past 2 days, and on the same test
(rrul starting, then doing the dslreports test) observe expected
results, with the download having the normal brief spike, the upload
impact being nearly immeasurable, and the observed latency increase in
either direction, negligible.

http://snapon.lab.bufferbloat.net/~d/dslreports2/dslreports_vs_rrul_50_5mbit.png

verses the puzzling one:

http://snapon.lab.bufferbloat.net/~d/dsl/vsdslreports_at_the_sametime.png

So the next step for me is to get cake working in openwrt on hardware
fast enough to run at 110Mbit and returning to the yurtlab to try
it... but that won't be til sunday at best. Tho I almost got it built,
at least, last night. Still sorting through patches....





>
> - Jonathan Morton



-- 
Dave Täht
Open Networking needs **Open Source Hardware**

https://plus.google.com/u/0/+EricRaymond/posts/JqxCe2pFr67

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-30 15:17       ` Dave Taht
@ 2015-05-01  9:50         ` Kevin Darbyshire-Bryant
  2015-05-01 11:06         ` Kevin Darbyshire-Bryant
  1 sibling, 0 replies; 8+ messages in thread
From: Kevin Darbyshire-Bryant @ 2015-05-01  9:50 UTC (permalink / raw)
  To: cerowrt-devel


[-- Attachment #1.1: Type: text/plain, Size: 77540 bytes --]

On 30/04/2015 16:17, Dave Taht wrote:
> On Thu, Apr 30, 2015 at 7:38 AM, Jonathan Morton <chromatix99@gmail.com> wrote:
>> It took me a while to get around to thinking about this, partly because my
>> phone inexplicably refuses to believe snapon exists.
> It has an old dnssec signed dns tree, using the isc dlv, which turned
> out to somewhat break older versions of dnsmasq, if that helps any.
OpenWrt CC trunk is now up to dnsmasq2.73rc7 - the latest fix was for a dnssec fallback to tcp
problem.  Highly recommended.
> So the next step for me is to get cake working in openwrt on hardware fast enough to run at 110Mbit and returning to the yurtlab to try it... but that won't be til sunday at best. Tho I almost got it built, at least, last night. Still sorting through patches....
I have no idea if this will help you guys.  I was fiddling with getting cake into CC a little earlier
based on the web page instructions.  My totally unsubtle approach was to basically copy over
relevant cerowrt-3.10 packages into the CC build tree:

ceropackages-3.10/net/sqm-scripts openwrt/package/feeds/packages/sqm-scripts
ceropackages-3.10/net/kmod-sched-cake openwrt/feeds/packages/net/kmod-sched-cake *

ceropackages-3.10/luci/luci-app-sqm openwrt/package/feeds/packages/luci-app-sqm

iproute2 140&141 patches to openwrt/package/network/utils/iproute2/patches

You'll need to put a symlink from openwrt/package/feeds/packages/kmod-sched-cake to
openwrt/feeds/packages/net/kmod-sched-cake

* note different directory!

Also to get iproute2 tc to understand cake I basically did a git diff on toke's iproute-cake
vs iproute 4.0.0 which is where CC currently is:  Result 2 patches, 140 & 141, 1st is to get
cake in, the 2nd is to do the required tc makefile tweak.  CC builds, runs on Archer C7,
nothing obviously exploded or broken...yet... your mileage may vary!  VERY VERY
UNTESTED, DRAGONS LURK, I'M AN IDIOT AND DON'T REALLY KNOW WHAT I'M DOING.
Is that clear enough? :-)

Maybe it helps.  What I will say is that a dslreports test appears less jittery for me.

Kevin



141-cake-disable-sfq-codel.patch

diff --git a/tc/Makefile b/tc/Makefile
index e503c8a..3dce533 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -61,7 +61,7 @@ TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
 TCMODULES += q_nfq_codel.o
 TCMODULES += q_efq_codel.o
-TCMODULES += q_sfq_codel.o
+#TCMODULES += q_sfq_codel.o
 TCMODULES += q_pfq_codel.o
 TCMODULES += q_ns2_codel.o
 TCMODULES += q_ns4_codel.o


140-cake-add-support.patch

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 534b847..a1d7b67 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -845,4 +845,38 @@ struct tc_pie_xstats {
     __u32 maxq;             /* maximum queue size */
     __u32 ecn_mark;         /* packets marked with ecn*/
 };
+
+/* CAKE */
+enum {
+    TCA_CAKE_UNSPEC,
+    TCA_CAKE_BASE_RATE,
+    TCA_CAKE_DIFFSERV_MODE,
+    TCA_CAKE_ATM,
+    TCA_CAKE_FLOW_MODE,
+    __TCA_CAKE_MAX
+};
+#define TCA_CAKE_MAX    (__TCA_CAKE_MAX - 1)
+
+struct tc_cake_xstats {
+    __u16 type;  /* constant magic 0xCAFE */
+    __u16 class_cnt;
+    struct {
+        __u32 rate;
+        __u32 target_us;
+        __u32 packets;
+        __u32 interval_us;
+        __u64 bytes;
+        __u32 dropped;
+        __u32 ecn_marked;
+        __u32 way_indirect_hits;
+        __u32 way_misses;
+        __u32 way_collisions;
+        __u32 backlog_bytes;
+        __u32 peak_delay; /* delay to fat flows */
+        __u32 avge_delay;
+        __u32 base_delay; /* delay to sparse flows */
+        __u32 dummy2;
+    } cls[8];
+};
+
 #endif
diff --git a/tc/Makefile b/tc/Makefile
index d831a15..e503c8a 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -59,8 +59,17 @@ TCMODULES += em_meta.o
 TCMODULES += q_mqprio.o
 TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
+TCMODULES += q_nfq_codel.o
+TCMODULES += q_efq_codel.o
+TCMODULES += q_sfq_codel.o
+TCMODULES += q_pfq_codel.o
+TCMODULES += q_ns2_codel.o
+TCMODULES += q_ns4_codel.o
 TCMODULES += q_fq.o
 TCMODULES += q_pie.o
+TCMODULES += q_cake.o
+TCMODULES += q_cake0.o
+TCMODULES += q_cake2.o
 TCMODULES += q_hhf.o
 
 ifeq ($(TC_CONFIG_IPSET), y)
diff --git a/tc/q_cake.c b/tc/q_cake.c
new file mode 100644
index 0000000..d9415d3
--- /dev/null
+++ b/tc/q_cake.c
@@ -0,0 +1,333 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014-2015 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... cake [ bandwidth RATE | unlimited ]\n"
+                    "                [ besteffort | precedence | diffserv8 | diffserv4 ]\n"
+                    "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+                    "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    int unlimited = 0;
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    int flowmode = -1;
+    int atm = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "bandwidth") == 0) {
+            NEXT_ARG();
+            if (get_rate(&bandwidth, *argv)) {
+                fprintf(stderr, "Illegal \"bandwidth\"\n");
+                return -1;
+            }
+            unlimited = 0;
+        } else if (strcmp(*argv, "unlimited") == 0) {
+            bandwidth = 0;
+            unlimited = 1;
+
+        } else if (strcmp(*argv, "besteffort") == 0) {
+            diffserv = 1;
+        } else if (strcmp(*argv, "precedence") == 0) {
+            diffserv = 2;
+        } else if (strcmp(*argv, "diffserv8") == 0) {
+            diffserv = 3;
+        } else if (strcmp(*argv, "diffserv4") == 0) {
+            diffserv = 4;
+        } else if (strcmp(*argv, "diffserv") == 0) {
+            diffserv = 4;
+
+        } else if (strcmp(*argv, "flowblind") == 0) {
+            flowmode = 0;
+        } else if (strcmp(*argv, "srchost") == 0) {
+            flowmode = 1;
+        } else if (strcmp(*argv, "dsthost") == 0) {
+            flowmode = 2;
+        } else if (strcmp(*argv, "hosts") == 0) {
+            flowmode = 3;
+        } else if (strcmp(*argv, "flows") == 0) {
+            flowmode = 4;
+
+        } else if (strcmp(*argv, "atm") == 0) {
+            atm = 1;
+        } else if (strcmp(*argv, "noatm") == 0) {
+            atm = 0;
+
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (bandwidth || unlimited)
+        addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+    if (diffserv)
+        addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+    if (atm != -1)
+        addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+    if (flowmode != -1)
+        addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CAKE_MAX + 1];
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    unsigned flowmode = 0;
+    int atm = -1;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+    if (tb[TCA_CAKE_BASE_RATE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+        bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+        if(bandwidth)
+            fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+        else
+            fprintf(f, "unlimited ");
+    }
+    if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+        diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+        switch(diffserv) {
+        case 1:
+            fprintf(f, "besteffort ");
+            break;
+        case 2:
+            fprintf(f, "precedence ");
+            break;
+        case 3:
+            fprintf(f, "diffserv8 ");
+            break;
+        case 4:
+            fprintf(f, "diffserv4 ");
+            break;
+        default:
+            fprintf(f, "(?diffserv?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_FLOW_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+        flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+        switch(flowmode) {
+        case 0:
+            fprintf(f, "flowblind ");
+            break;
+        case 1:
+            fprintf(f, "srchost ");
+            break;
+        case 2:
+            fprintf(f, "dsthost ");
+            break;
+        case 3:
+            fprintf(f, "hosts ");
+            break;
+        case 4:
+            fprintf(f, "flows ");
+            break;
+        default:
+            fprintf(f, "(?flowmode?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_ATM] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+        atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+        if (atm)
+            fprintf(f, "atm ");
+    }
+
+    return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    /* fq_codel stats format borrowed */
+    struct tc_fq_codel_xstats *st;
+    struct tc_cake_xstats     *stc;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+        return -1;
+
+    st  = RTA_DATA(xstats);
+    stc = RTA_DATA(xstats);
+
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    } else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    } else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+        int i;
+
+        fprintf(f, "        ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "   Class %u  ", i);
+        fprintf(f, "\n");
+
+        fprintf(f, "  rate  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_rate(stc->cls[i].rate, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  target");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].target_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "interval");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].interval_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "Pk delay");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].peak_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "Av delay");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].avge_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "Sp delay");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].base_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  pkts  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].packets);
+        fprintf(f, "\n");
+
+        fprintf(f, "way inds");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].way_indirect_hits);
+        fprintf(f, "\n");
+
+        fprintf(f, "way miss");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].way_misses);
+        fprintf(f, "\n");
+
+        fprintf(f, "way cols");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].way_collisions);
+        fprintf(f, "\n");
+
+        fprintf(f, "  bytes ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12llu", stc->cls[i].bytes);
+        fprintf(f, "\n");
+
+        fprintf(f, "  drops ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].dropped);
+        fprintf(f, "\n");
+
+        fprintf(f, "  marks ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].ecn_marked);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+struct qdisc_util cake_qdisc_util = {
+    .id        = "cake",
+    .parse_qopt    = cake_parse_opt,
+    .print_qopt    = cake_print_opt,
+    .print_xstats    = cake_print_xstats,
+};
diff --git a/tc/q_cake0.c b/tc/q_cake0.c
new file mode 100644
index 0000000..9fb63ed
--- /dev/null
+++ b/tc/q_cake0.c
@@ -0,0 +1,301 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... cake0 [ bandwidth RATE | unlimited ]\n"
+                    "                 [ besteffort | precedence | diffserv ]\n"
+                    "                 [ flowblind | srchost | dsthost | hosts | flows ]\n"
+                    "                 [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    int unlimited = 0;
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    int flowmode = -1;
+    int atm = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "bandwidth") == 0) {
+            NEXT_ARG();
+            if (get_rate(&bandwidth, *argv)) {
+                fprintf(stderr, "Illegal \"bandwidth\"\n");
+                return -1;
+            }
+            unlimited = 0;
+        } else if (strcmp(*argv, "unlimited") == 0) {
+            bandwidth = 0;
+            unlimited = 1;
+
+        } else if (strcmp(*argv, "besteffort") == 0) {
+            diffserv = 1;
+        } else if (strcmp(*argv, "precedence") == 0) {
+            diffserv = 2;
+        } else if (strcmp(*argv, "diffserv") == 0) {
+            diffserv = 3;
+
+        } else if (strcmp(*argv, "flowblind") == 0) {
+            flowmode = 0;
+        } else if (strcmp(*argv, "srchost") == 0) {
+            flowmode = 1;
+        } else if (strcmp(*argv, "dsthost") == 0) {
+            flowmode = 2;
+        } else if (strcmp(*argv, "hosts") == 0) {
+            flowmode = 3;
+        } else if (strcmp(*argv, "flows") == 0) {
+            flowmode = 4;
+
+        } else if (strcmp(*argv, "atm") == 0) {
+            atm = 1;
+        } else if (strcmp(*argv, "noatm") == 0) {
+            atm = 0;
+
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (bandwidth || unlimited)
+        addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+    if (diffserv)
+        addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+    if (atm != -1)
+        addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+    if (flowmode != -1)
+        addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CAKE_MAX + 1];
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    unsigned flowmode = 0;
+    int atm = -1;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+    if (tb[TCA_CAKE_BASE_RATE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+        bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+        if(bandwidth)
+            fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+        else
+            fprintf(f, "unlimited");
+    }
+    if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+        diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+        switch(diffserv) {
+        case 1:
+            fprintf(f, "besteffort ");
+            break;
+        case 2:
+            fprintf(f, "precedence ");
+            break;
+        case 3:
+            fprintf(f, "diffserv ");
+            break;
+        default:
+            fprintf(f, "(?diffserv?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_FLOW_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+        flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+        switch(flowmode) {
+        case 0:
+            fprintf(f, "flowblind ");
+            break;
+        case 1:
+            fprintf(f, "srchost ");
+            break;
+        case 2:
+            fprintf(f, "dsthost ");
+            break;
+        case 3:
+            fprintf(f, "hosts ");
+            break;
+        case 4:
+            fprintf(f, "flows ");
+            break;
+        default:
+            fprintf(f, "(?flowmode?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_ATM] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+        atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+        if (atm)
+            fprintf(f, "atm ");
+    }
+
+    return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    /* fq_codel stats format borrowed */
+    struct tc_fq_codel_xstats *st;
+    struct tc_cake_xstats     *stc;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+        return -1;
+
+    st  = RTA_DATA(xstats);
+    stc = RTA_DATA(xstats);
+
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    } else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    } else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+        int i;
+
+        fprintf(f, "        ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "  Class %u ", i);
+        fprintf(f, "\n");
+
+        fprintf(f, "  rate  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  target");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "interval");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  delay ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].peak_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  pkts  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].packets);
+        fprintf(f, "\n");
+
+        fprintf(f, "  bytes ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10llu", stc->cls[i].bytes);
+        fprintf(f, "\n");
+
+        fprintf(f, "  drops ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].dropped);
+        fprintf(f, "\n");
+
+        fprintf(f, "  marks ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].ecn_marked);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+struct qdisc_util cake0_qdisc_util = {
+    .id        = "cake0",
+    .parse_qopt    = cake_parse_opt,
+    .print_qopt    = cake_print_opt,
+    .print_xstats    = cake_print_xstats,
+};
diff --git a/tc/q_cake2.c b/tc/q_cake2.c
new file mode 100644
index 0000000..a4d3f7c
--- /dev/null
+++ b/tc/q_cake2.c
@@ -0,0 +1,296 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... cake2 [ bandwidth RATE | unlimited ]\n"
+                    "                [ besteffort | precedence | diffserv ]\n"
+                    "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+                    "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    int unlimited = 0;
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    int flowmode = -1;
+    int atm = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "bandwidth") == 0) {
+            NEXT_ARG();
+            if (get_rate(&bandwidth, *argv)) {
+                fprintf(stderr, "Illegal \"bandwidth\"\n");
+                return -1;
+            }
+            unlimited = 0;
+        } else if (strcmp(*argv, "unlimited") == 0) {
+            bandwidth = 0;
+            unlimited = 1;
+
+        } else if (strcmp(*argv, "besteffort") == 0) {
+            diffserv = 1;
+        } else if (strcmp(*argv, "precedence") == 0) {
+            diffserv = 2;
+        } else if (strcmp(*argv, "diffserv") == 0) {
+            diffserv = 3;
+
+        } else if (strcmp(*argv, "flowblind") == 0) {
+            flowmode = 0;
+        } else if (strcmp(*argv, "srchost") == 0) {
+            flowmode = 1;
+        } else if (strcmp(*argv, "dsthost") == 0) {
+            flowmode = 2;
+        } else if (strcmp(*argv, "hosts") == 0) {
+            flowmode = 3;
+        } else if (strcmp(*argv, "flows") == 0) {
+            flowmode = 4;
+
+        } else if (strcmp(*argv, "atm") == 0) {
+            atm = 1;
+        } else if (strcmp(*argv, "noatm") == 0) {
+            atm = 0;
+
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (bandwidth || unlimited)
+        addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+    if (diffserv)
+        addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+    if (atm != -1)
+        addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+    if (flowmode != -1)
+        addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CAKE_MAX + 1];
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    unsigned flowmode = 0;
+    int atm = -1;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+    if (tb[TCA_CAKE_BASE_RATE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+        bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+        if(bandwidth)
+            fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+        else
+            fprintf(f, "unlimited");
+    }
+    if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+        diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+        switch(diffserv) {
+        case 1:
+            fprintf(f, "besteffort ");
+            break;
+        case 2:
+            fprintf(f, "precedence ");
+            break;
+        case 3:
+            fprintf(f, "diffserv ");
+            break;
+        default:
+            fprintf(f, "(?diffserv?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_FLOW_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+        flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+        switch(flowmode) {
+        case 0:
+            fprintf(f, "flowblind ");
+            break;
+        case 1:
+            fprintf(f, "srchost ");
+            break;
+        case 2:
+            fprintf(f, "dsthost ");
+            break;
+        case 3:
+            fprintf(f, "hosts ");
+            break;
+        case 4:
+            fprintf(f, "flows ");
+            break;
+        default:
+            fprintf(f, "(?flowmode?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_ATM] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+        atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+        if (atm)
+            fprintf(f, "atm ");
+    }
+
+    return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    /* fq_codel stats format borrowed */
+    struct tc_fq_codel_xstats *st;
+    struct tc_cake_xstats     *stc;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+        return -1;
+
+    st  = RTA_DATA(xstats);
+    stc = RTA_DATA(xstats);
+
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    } else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    } else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+        int i;
+
+        fprintf(f, "        ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "  Class %u ", i);
+        fprintf(f, "\n");
+
+        fprintf(f, "  rate  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  target");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "interval");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  pkts  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].packets);
+        fprintf(f, "\n");
+
+        fprintf(f, "  bytes ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10llu", stc->cls[i].bytes);
+        fprintf(f, "\n");
+
+        fprintf(f, "  drops ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].dropped);
+        fprintf(f, "\n");
+
+        fprintf(f, "  marks ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].ecn_marked);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+struct qdisc_util cake2_qdisc_util = {
+    .id        = "cake2",
+    .parse_qopt    = cake_parse_opt,
+    .print_qopt    = cake_print_opt,
+    .print_xstats    = cake_print_xstats,
+};
diff --git a/tc/q_efq_codel.c b/tc/q_efq_codel.c
new file mode 100644
index 0000000..b80e5e4
--- /dev/null
+++ b/tc/q_efq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... efq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+    fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+    fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned flows = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    unsigned quantum = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "flows") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&flows, *argv, 0)) {
+                fprintf(stderr, "Illegal \"flows\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "quantum") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&quantum, *argv, 0)) {
+                fprintf(stderr, "Illegal \"quantum\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+    if (flows)
+        addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+    if (quantum)
+        addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+    if (interval)
+        addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned flows;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    unsigned quantum;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+    if (tb[TCA_FQ_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_FQ_CODEL_FLOWS] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+        flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+        fprintf(f, "flows %u ", flows);
+    }
+    if (tb[TCA_FQ_CODEL_QUANTUM] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+        quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+        fprintf(f, "quantum %u ", quantum);
+    }
+    if (tb[TCA_FQ_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_FQ_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_FQ_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    struct tc_fq_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    }
+    if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    }
+    return 0;
+
+}
+
+struct qdisc_util efq_codel_qdisc_util = {
+    .id        = "efq_codel",
+    .parse_qopt    = fq_codel_parse_opt,
+    .print_qopt    = fq_codel_print_opt,
+    .print_xstats    = fq_codel_print_xstats,
+};
diff --git a/tc/q_nfq_codel.c b/tc/q_nfq_codel.c
new file mode 100644
index 0000000..ef24909
--- /dev/null
+++ b/tc/q_nfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... nfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+    fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+    fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned flows = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    unsigned quantum = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "flows") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&flows, *argv, 0)) {
+                fprintf(stderr, "Illegal \"flows\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "quantum") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&quantum, *argv, 0)) {
+                fprintf(stderr, "Illegal \"quantum\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+    if (flows)
+        addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+    if (quantum)
+        addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+    if (interval)
+        addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned flows;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    unsigned quantum;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+    if (tb[TCA_FQ_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_FQ_CODEL_FLOWS] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+        flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+        fprintf(f, "flows %u ", flows);
+    }
+    if (tb[TCA_FQ_CODEL_QUANTUM] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+        quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+        fprintf(f, "quantum %u ", quantum);
+    }
+    if (tb[TCA_FQ_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_FQ_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_FQ_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    struct tc_fq_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    }
+    if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    }
+    return 0;
+
+}
+
+struct qdisc_util nfq_codel_qdisc_util = {
+    .id        = "nfq_codel",
+    .parse_qopt    = fq_codel_parse_opt,
+    .print_qopt    = fq_codel_print_opt,
+    .print_xstats    = fq_codel_print_xstats,
+};
diff --git a/tc/q_ns2_codel.c b/tc/q_ns2_codel.c
new file mode 100644
index 0000000..223a971
--- /dev/null
+++ b/tc/q_ns2_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... ns2_codel [ limit PACKETS ] [ target TIME]\n");
+    fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+               struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+    if (interval)
+        addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+    if (tb[TCA_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                  struct rtattr *xstats)
+{
+    struct tc_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    fprintf(f, "  count %u lastcount %u ldelay %s",
+        st->count, st->lastcount, sprint_time(st->ldelay, b1));
+    if (st->dropping)
+        fprintf(f, " dropping");
+    if (st->drop_next < 0)
+        fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+    else
+        fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+    fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+        st->maxpacket, st->ecn_mark, st->drop_overlimit);
+    return 0;
+
+}
+
+struct qdisc_util ns2_codel_qdisc_util = {
+    .id        = "ns2_codel",
+    .parse_qopt    = codel_parse_opt,
+    .print_qopt    = codel_print_opt,
+    .print_xstats    = codel_print_xstats,
+};
diff --git a/tc/q_ns4_codel.c b/tc/q_ns4_codel.c
new file mode 100644
index 0000000..0aaa349
--- /dev/null
+++ b/tc/q_ns4_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... ns4_codel [ limit PACKETS ] [ target TIME]\n");
+    fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+               struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+    if (interval)
+        addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+    if (tb[TCA_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                  struct rtattr *xstats)
+{
+    struct tc_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    fprintf(f, "  count %u lastcount %u ldelay %s",
+        st->count, st->lastcount, sprint_time(st->ldelay, b1));
+    if (st->dropping)
+        fprintf(f, " dropping");
+    if (st->drop_next < 0)
+        fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+    else
+        fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+    fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+        st->maxpacket, st->ecn_mark, st->drop_overlimit);
+    return 0;
+
+}
+
+struct qdisc_util ns4_codel_qdisc_util = {
+    .id        = "ns4_codel",
+    .parse_qopt    = codel_parse_opt,
+    .print_qopt    = codel_print_opt,
+    .print_xstats    = codel_print_xstats,
+};
diff --git a/tc/q_pfq_codel.c b/tc/q_pfq_codel.c
new file mode 100644
index 0000000..52c5160
--- /dev/null
+++ b/tc/q_pfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... pfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+    fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+    fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned flows = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    unsigned quantum = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "flows") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&flows, *argv, 0)) {
+                fprintf(stderr, "Illegal \"flows\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "quantum") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&quantum, *argv, 0)) {
+                fprintf(stderr, "Illegal \"quantum\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+    if (flows)
+        addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+    if (quantum)
+        addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+    if (interval)
+        addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned flows;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    unsigned quantum;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+    if (tb[TCA_FQ_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_FQ_CODEL_FLOWS] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+        flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+        fprintf(f, "flows %u ", flows);
+    }
+    if (tb[TCA_FQ_CODEL_QUANTUM] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+        quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+        fprintf(f, "quantum %u ", quantum);
+    }
+    if (tb[TCA_FQ_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_FQ_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_FQ_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    struct tc_fq_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    }
+    if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    }
+    return 0;
+
+}
+
+struct qdisc_util pfq_codel_qdisc_util = {
+    .id        = "pfq_codel",
+    .parse_qopt    = fq_codel_parse_opt,
+    .print_qopt    = fq_codel_print_opt,
+    .print_xstats    = fq_codel_print_xstats,
+};

141-cake-add-support.patch




[-- Attachment #1.2: 140-cake-add-support.patch --]
[-- Type: text/plain, Size: 64464 bytes --]

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 534b847..a1d7b67 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -845,4 +845,38 @@ struct tc_pie_xstats {
 	__u32 maxq;             /* maximum queue size */
 	__u32 ecn_mark;         /* packets marked with ecn*/
 };
+
+/* CAKE */
+enum {
+	TCA_CAKE_UNSPEC,
+	TCA_CAKE_BASE_RATE,
+	TCA_CAKE_DIFFSERV_MODE,
+	TCA_CAKE_ATM,
+	TCA_CAKE_FLOW_MODE,
+	__TCA_CAKE_MAX
+};
+#define TCA_CAKE_MAX	(__TCA_CAKE_MAX - 1)
+
+struct tc_cake_xstats {
+	__u16 type;  /* constant magic 0xCAFE */
+	__u16 class_cnt;
+	struct {
+		__u32 rate;
+		__u32 target_us;
+		__u32 packets;
+		__u32 interval_us;
+		__u64 bytes;
+		__u32 dropped;
+		__u32 ecn_marked;
+		__u32 way_indirect_hits;
+		__u32 way_misses;
+		__u32 way_collisions;
+		__u32 backlog_bytes;
+		__u32 peak_delay; /* delay to fat flows */
+		__u32 avge_delay;
+		__u32 base_delay; /* delay to sparse flows */
+		__u32 dummy2;
+	} cls[8];
+};
+
 #endif
diff --git a/tc/Makefile b/tc/Makefile
index d831a15..e503c8a 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -59,8 +59,17 @@ TCMODULES += em_meta.o
 TCMODULES += q_mqprio.o
 TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
+TCMODULES += q_nfq_codel.o
+TCMODULES += q_efq_codel.o
+TCMODULES += q_sfq_codel.o
+TCMODULES += q_pfq_codel.o
+TCMODULES += q_ns2_codel.o
+TCMODULES += q_ns4_codel.o
 TCMODULES += q_fq.o
 TCMODULES += q_pie.o
+TCMODULES += q_cake.o
+TCMODULES += q_cake0.o
+TCMODULES += q_cake2.o
 TCMODULES += q_hhf.o
 
 ifeq ($(TC_CONFIG_IPSET), y)
diff --git a/tc/q_cake.c b/tc/q_cake.c
new file mode 100644
index 0000000..d9415d3
--- /dev/null
+++ b/tc/q_cake.c
@@ -0,0 +1,333 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014-2015 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... cake [ bandwidth RATE | unlimited ]\n"
+	                "                [ besteffort | precedence | diffserv8 | diffserv4 ]\n"
+	                "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+	                "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	int unlimited = 0;
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	int flowmode = -1;
+	int atm = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&bandwidth, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+			unlimited = 0;
+		} else if (strcmp(*argv, "unlimited") == 0) {
+			bandwidth = 0;
+			unlimited = 1;
+
+		} else if (strcmp(*argv, "besteffort") == 0) {
+			diffserv = 1;
+		} else if (strcmp(*argv, "precedence") == 0) {
+			diffserv = 2;
+		} else if (strcmp(*argv, "diffserv8") == 0) {
+			diffserv = 3;
+		} else if (strcmp(*argv, "diffserv4") == 0) {
+			diffserv = 4;
+		} else if (strcmp(*argv, "diffserv") == 0) {
+			diffserv = 4;
+
+		} else if (strcmp(*argv, "flowblind") == 0) {
+			flowmode = 0;
+		} else if (strcmp(*argv, "srchost") == 0) {
+			flowmode = 1;
+		} else if (strcmp(*argv, "dsthost") == 0) {
+			flowmode = 2;
+		} else if (strcmp(*argv, "hosts") == 0) {
+			flowmode = 3;
+		} else if (strcmp(*argv, "flows") == 0) {
+			flowmode = 4;
+
+		} else if (strcmp(*argv, "atm") == 0) {
+			atm = 1;
+		} else if (strcmp(*argv, "noatm") == 0) {
+			atm = 0;
+
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (bandwidth || unlimited)
+		addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+	if (diffserv)
+		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+	if (atm != -1)
+		addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+	if (flowmode != -1)
+		addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CAKE_MAX + 1];
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	unsigned flowmode = 0;
+	int atm = -1;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+	if (tb[TCA_CAKE_BASE_RATE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+		bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+		if(bandwidth)
+			fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+		else
+			fprintf(f, "unlimited ");
+	}
+	if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+		diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+		switch(diffserv) {
+		case 1:
+			fprintf(f, "besteffort ");
+			break;
+		case 2:
+			fprintf(f, "precedence ");
+			break;
+		case 3:
+			fprintf(f, "diffserv8 ");
+			break;
+		case 4:
+			fprintf(f, "diffserv4 ");
+			break;
+		default:
+			fprintf(f, "(?diffserv?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_FLOW_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+		flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+		switch(flowmode) {
+		case 0:
+			fprintf(f, "flowblind ");
+			break;
+		case 1:
+			fprintf(f, "srchost ");
+			break;
+		case 2:
+			fprintf(f, "dsthost ");
+			break;
+		case 3:
+			fprintf(f, "hosts ");
+			break;
+		case 4:
+			fprintf(f, "flows ");
+			break;
+		default:
+			fprintf(f, "(?flowmode?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_ATM] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+		atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+		if (atm)
+			fprintf(f, "atm ");
+	}
+
+	return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	/* fq_codel stats format borrowed */
+	struct tc_fq_codel_xstats *st;
+	struct tc_cake_xstats     *stc;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+		return -1;
+
+	st  = RTA_DATA(xstats);
+	stc = RTA_DATA(xstats);
+
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	} else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	} else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+		int i;
+
+		fprintf(f, "        ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "   Class %u  ", i);
+		fprintf(f, "\n");
+
+		fprintf(f, "  rate  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_rate(stc->cls[i].rate, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  target");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].target_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "interval");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].interval_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "Pk delay");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].peak_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "Av delay");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].avge_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "Sp delay");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].base_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  pkts  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].packets);
+		fprintf(f, "\n");
+
+		fprintf(f, "way inds");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].way_indirect_hits);
+		fprintf(f, "\n");
+
+		fprintf(f, "way miss");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].way_misses);
+		fprintf(f, "\n");
+
+		fprintf(f, "way cols");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].way_collisions);
+		fprintf(f, "\n");
+
+		fprintf(f, "  bytes ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12llu", stc->cls[i].bytes);
+		fprintf(f, "\n");
+
+		fprintf(f, "  drops ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].dropped);
+		fprintf(f, "\n");
+
+		fprintf(f, "  marks ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].ecn_marked);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+struct qdisc_util cake_qdisc_util = {
+	.id		= "cake",
+	.parse_qopt	= cake_parse_opt,
+	.print_qopt	= cake_print_opt,
+	.print_xstats	= cake_print_xstats,
+};
diff --git a/tc/q_cake0.c b/tc/q_cake0.c
new file mode 100644
index 0000000..9fb63ed
--- /dev/null
+++ b/tc/q_cake0.c
@@ -0,0 +1,301 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... cake0 [ bandwidth RATE | unlimited ]\n"
+	                "                 [ besteffort | precedence | diffserv ]\n"
+	                "                 [ flowblind | srchost | dsthost | hosts | flows ]\n"
+	                "                 [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	int unlimited = 0;
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	int flowmode = -1;
+	int atm = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&bandwidth, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+			unlimited = 0;
+		} else if (strcmp(*argv, "unlimited") == 0) {
+			bandwidth = 0;
+			unlimited = 1;
+
+		} else if (strcmp(*argv, "besteffort") == 0) {
+			diffserv = 1;
+		} else if (strcmp(*argv, "precedence") == 0) {
+			diffserv = 2;
+		} else if (strcmp(*argv, "diffserv") == 0) {
+			diffserv = 3;
+
+		} else if (strcmp(*argv, "flowblind") == 0) {
+			flowmode = 0;
+		} else if (strcmp(*argv, "srchost") == 0) {
+			flowmode = 1;
+		} else if (strcmp(*argv, "dsthost") == 0) {
+			flowmode = 2;
+		} else if (strcmp(*argv, "hosts") == 0) {
+			flowmode = 3;
+		} else if (strcmp(*argv, "flows") == 0) {
+			flowmode = 4;
+
+		} else if (strcmp(*argv, "atm") == 0) {
+			atm = 1;
+		} else if (strcmp(*argv, "noatm") == 0) {
+			atm = 0;
+
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (bandwidth || unlimited)
+		addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+	if (diffserv)
+		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+	if (atm != -1)
+		addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+	if (flowmode != -1)
+		addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CAKE_MAX + 1];
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	unsigned flowmode = 0;
+	int atm = -1;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+	if (tb[TCA_CAKE_BASE_RATE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+		bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+		if(bandwidth)
+			fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+		else
+			fprintf(f, "unlimited");
+	}
+	if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+		diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+		switch(diffserv) {
+		case 1:
+			fprintf(f, "besteffort ");
+			break;
+		case 2:
+			fprintf(f, "precedence ");
+			break;
+		case 3:
+			fprintf(f, "diffserv ");
+			break;
+		default:
+			fprintf(f, "(?diffserv?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_FLOW_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+		flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+		switch(flowmode) {
+		case 0:
+			fprintf(f, "flowblind ");
+			break;
+		case 1:
+			fprintf(f, "srchost ");
+			break;
+		case 2:
+			fprintf(f, "dsthost ");
+			break;
+		case 3:
+			fprintf(f, "hosts ");
+			break;
+		case 4:
+			fprintf(f, "flows ");
+			break;
+		default:
+			fprintf(f, "(?flowmode?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_ATM] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+		atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+		if (atm)
+			fprintf(f, "atm ");
+	}
+
+	return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	/* fq_codel stats format borrowed */
+	struct tc_fq_codel_xstats *st;
+	struct tc_cake_xstats     *stc;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+		return -1;
+
+	st  = RTA_DATA(xstats);
+	stc = RTA_DATA(xstats);
+
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	} else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	} else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+		int i;
+
+		fprintf(f, "        ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "  Class %u ", i);
+		fprintf(f, "\n");
+
+		fprintf(f, "  rate  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  target");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "interval");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  delay ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].peak_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  pkts  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].packets);
+		fprintf(f, "\n");
+
+		fprintf(f, "  bytes ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10llu", stc->cls[i].bytes);
+		fprintf(f, "\n");
+
+		fprintf(f, "  drops ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].dropped);
+		fprintf(f, "\n");
+
+		fprintf(f, "  marks ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].ecn_marked);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+struct qdisc_util cake0_qdisc_util = {
+	.id		= "cake0",
+	.parse_qopt	= cake_parse_opt,
+	.print_qopt	= cake_print_opt,
+	.print_xstats	= cake_print_xstats,
+};
diff --git a/tc/q_cake2.c b/tc/q_cake2.c
new file mode 100644
index 0000000..a4d3f7c
--- /dev/null
+++ b/tc/q_cake2.c
@@ -0,0 +1,296 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... cake2 [ bandwidth RATE | unlimited ]\n"
+	                "                [ besteffort | precedence | diffserv ]\n"
+	                "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+	                "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	int unlimited = 0;
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	int flowmode = -1;
+	int atm = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&bandwidth, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+			unlimited = 0;
+		} else if (strcmp(*argv, "unlimited") == 0) {
+			bandwidth = 0;
+			unlimited = 1;
+
+		} else if (strcmp(*argv, "besteffort") == 0) {
+			diffserv = 1;
+		} else if (strcmp(*argv, "precedence") == 0) {
+			diffserv = 2;
+		} else if (strcmp(*argv, "diffserv") == 0) {
+			diffserv = 3;
+
+		} else if (strcmp(*argv, "flowblind") == 0) {
+			flowmode = 0;
+		} else if (strcmp(*argv, "srchost") == 0) {
+			flowmode = 1;
+		} else if (strcmp(*argv, "dsthost") == 0) {
+			flowmode = 2;
+		} else if (strcmp(*argv, "hosts") == 0) {
+			flowmode = 3;
+		} else if (strcmp(*argv, "flows") == 0) {
+			flowmode = 4;
+
+		} else if (strcmp(*argv, "atm") == 0) {
+			atm = 1;
+		} else if (strcmp(*argv, "noatm") == 0) {
+			atm = 0;
+
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (bandwidth || unlimited)
+		addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+	if (diffserv)
+		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+	if (atm != -1)
+		addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+	if (flowmode != -1)
+		addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CAKE_MAX + 1];
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	unsigned flowmode = 0;
+	int atm = -1;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+	if (tb[TCA_CAKE_BASE_RATE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+		bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+		if(bandwidth)
+			fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+		else
+			fprintf(f, "unlimited");
+	}
+	if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+		diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+		switch(diffserv) {
+		case 1:
+			fprintf(f, "besteffort ");
+			break;
+		case 2:
+			fprintf(f, "precedence ");
+			break;
+		case 3:
+			fprintf(f, "diffserv ");
+			break;
+		default:
+			fprintf(f, "(?diffserv?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_FLOW_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+		flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+		switch(flowmode) {
+		case 0:
+			fprintf(f, "flowblind ");
+			break;
+		case 1:
+			fprintf(f, "srchost ");
+			break;
+		case 2:
+			fprintf(f, "dsthost ");
+			break;
+		case 3:
+			fprintf(f, "hosts ");
+			break;
+		case 4:
+			fprintf(f, "flows ");
+			break;
+		default:
+			fprintf(f, "(?flowmode?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_ATM] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+		atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+		if (atm)
+			fprintf(f, "atm ");
+	}
+
+	return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	/* fq_codel stats format borrowed */
+	struct tc_fq_codel_xstats *st;
+	struct tc_cake_xstats     *stc;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+		return -1;
+
+	st  = RTA_DATA(xstats);
+	stc = RTA_DATA(xstats);
+
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	} else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	} else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+		int i;
+
+		fprintf(f, "        ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "  Class %u ", i);
+		fprintf(f, "\n");
+
+		fprintf(f, "  rate  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  target");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "interval");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  pkts  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].packets);
+		fprintf(f, "\n");
+
+		fprintf(f, "  bytes ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10llu", stc->cls[i].bytes);
+		fprintf(f, "\n");
+
+		fprintf(f, "  drops ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].dropped);
+		fprintf(f, "\n");
+
+		fprintf(f, "  marks ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].ecn_marked);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+struct qdisc_util cake2_qdisc_util = {
+	.id		= "cake2",
+	.parse_qopt	= cake_parse_opt,
+	.print_qopt	= cake_print_opt,
+	.print_xstats	= cake_print_xstats,
+};
diff --git a/tc/q_efq_codel.c b/tc/q_efq_codel.c
new file mode 100644
index 0000000..b80e5e4
--- /dev/null
+++ b/tc/q_efq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... efq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+	fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+	fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned flows = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	unsigned quantum = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "quantum") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&quantum, *argv, 0)) {
+				fprintf(stderr, "Illegal \"quantum\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+	if (flows)
+		addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+	if (quantum)
+		addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+	if (interval)
+		addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned flows;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	unsigned quantum;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+	if (tb[TCA_FQ_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_FQ_CODEL_FLOWS] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+		flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+		fprintf(f, "flows %u ", flows);
+	}
+	if (tb[TCA_FQ_CODEL_QUANTUM] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+		quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+		fprintf(f, "quantum %u ", quantum);
+	}
+	if (tb[TCA_FQ_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_FQ_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_FQ_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	struct tc_fq_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	}
+	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	}
+	return 0;
+
+}
+
+struct qdisc_util efq_codel_qdisc_util = {
+	.id		= "efq_codel",
+	.parse_qopt	= fq_codel_parse_opt,
+	.print_qopt	= fq_codel_print_opt,
+	.print_xstats	= fq_codel_print_xstats,
+};
diff --git a/tc/q_nfq_codel.c b/tc/q_nfq_codel.c
new file mode 100644
index 0000000..ef24909
--- /dev/null
+++ b/tc/q_nfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... nfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+	fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+	fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned flows = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	unsigned quantum = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "quantum") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&quantum, *argv, 0)) {
+				fprintf(stderr, "Illegal \"quantum\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+	if (flows)
+		addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+	if (quantum)
+		addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+	if (interval)
+		addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned flows;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	unsigned quantum;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+	if (tb[TCA_FQ_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_FQ_CODEL_FLOWS] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+		flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+		fprintf(f, "flows %u ", flows);
+	}
+	if (tb[TCA_FQ_CODEL_QUANTUM] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+		quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+		fprintf(f, "quantum %u ", quantum);
+	}
+	if (tb[TCA_FQ_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_FQ_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_FQ_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	struct tc_fq_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	}
+	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	}
+	return 0;
+
+}
+
+struct qdisc_util nfq_codel_qdisc_util = {
+	.id		= "nfq_codel",
+	.parse_qopt	= fq_codel_parse_opt,
+	.print_qopt	= fq_codel_print_opt,
+	.print_xstats	= fq_codel_print_xstats,
+};
diff --git a/tc/q_ns2_codel.c b/tc/q_ns2_codel.c
new file mode 100644
index 0000000..223a971
--- /dev/null
+++ b/tc/q_ns2_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... ns2_codel [ limit PACKETS ] [ target TIME]\n");
+	fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			   struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+	if (interval)
+		addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+	if (tb[TCA_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+			      struct rtattr *xstats)
+{
+	struct tc_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	fprintf(f, "  count %u lastcount %u ldelay %s",
+		st->count, st->lastcount, sprint_time(st->ldelay, b1));
+	if (st->dropping)
+		fprintf(f, " dropping");
+	if (st->drop_next < 0)
+		fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+	else
+		fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+	fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+		st->maxpacket, st->ecn_mark, st->drop_overlimit);
+	return 0;
+
+}
+
+struct qdisc_util ns2_codel_qdisc_util = {
+	.id		= "ns2_codel",
+	.parse_qopt	= codel_parse_opt,
+	.print_qopt	= codel_print_opt,
+	.print_xstats	= codel_print_xstats,
+};
diff --git a/tc/q_ns4_codel.c b/tc/q_ns4_codel.c
new file mode 100644
index 0000000..0aaa349
--- /dev/null
+++ b/tc/q_ns4_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... ns4_codel [ limit PACKETS ] [ target TIME]\n");
+	fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			   struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+	if (interval)
+		addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+	if (tb[TCA_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+			      struct rtattr *xstats)
+{
+	struct tc_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	fprintf(f, "  count %u lastcount %u ldelay %s",
+		st->count, st->lastcount, sprint_time(st->ldelay, b1));
+	if (st->dropping)
+		fprintf(f, " dropping");
+	if (st->drop_next < 0)
+		fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+	else
+		fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+	fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+		st->maxpacket, st->ecn_mark, st->drop_overlimit);
+	return 0;
+
+}
+
+struct qdisc_util ns4_codel_qdisc_util = {
+	.id		= "ns4_codel",
+	.parse_qopt	= codel_parse_opt,
+	.print_qopt	= codel_print_opt,
+	.print_xstats	= codel_print_xstats,
+};
diff --git a/tc/q_pfq_codel.c b/tc/q_pfq_codel.c
new file mode 100644
index 0000000..52c5160
--- /dev/null
+++ b/tc/q_pfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... pfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+	fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+	fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned flows = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	unsigned quantum = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "quantum") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&quantum, *argv, 0)) {
+				fprintf(stderr, "Illegal \"quantum\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+	if (flows)
+		addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+	if (quantum)
+		addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+	if (interval)
+		addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned flows;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	unsigned quantum;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+	if (tb[TCA_FQ_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_FQ_CODEL_FLOWS] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+		flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+		fprintf(f, "flows %u ", flows);
+	}
+	if (tb[TCA_FQ_CODEL_QUANTUM] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+		quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+		fprintf(f, "quantum %u ", quantum);
+	}
+	if (tb[TCA_FQ_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_FQ_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_FQ_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	struct tc_fq_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	}
+	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	}
+	return 0;
+
+}
+
+struct qdisc_util pfq_codel_qdisc_util = {
+	.id		= "pfq_codel",
+	.parse_qopt	= fq_codel_parse_opt,
+	.print_qopt	= fq_codel_print_opt,
+	.print_xstats	= fq_codel_print_xstats,
+};

[-- Attachment #1.3: 141-cake-disable-sfq-codel.patch --]
[-- Type: text/plain, Size: 370 bytes --]

diff --git a/tc/Makefile b/tc/Makefile
index e503c8a..3dce533 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -61,7 +61,7 @@ TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
 TCMODULES += q_nfq_codel.o
 TCMODULES += q_efq_codel.o
-TCMODULES += q_sfq_codel.o
+#TCMODULES += q_sfq_codel.o
 TCMODULES += q_pfq_codel.o
 TCMODULES += q_ns2_codel.o
 TCMODULES += q_ns4_codel.o

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4791 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Cerowrt-devel] [Cake] documentation review request and out of tree cake builds for openwrt/etc.
  2015-04-30 15:17       ` Dave Taht
  2015-05-01  9:50         ` Kevin Darbyshire-Bryant
@ 2015-05-01 11:06         ` Kevin Darbyshire-Bryant
  1 sibling, 0 replies; 8+ messages in thread
From: Kevin Darbyshire-Bryant @ 2015-05-01 11:06 UTC (permalink / raw)
  To: cerowrt-devel

[-- Attachment #1: Type: text/plain, Size: 144628 bytes --]

Resent without attachments:


On 30/04/2015 16:17, Dave Taht wrote:

> On Thu, Apr 30, 2015 at 7:38 AM, Jonathan Morton <chromatix99@gmail.com> wrote:
>> It took me a while to get around to thinking about this, partly because my
>> phone inexplicably refuses to believe snapon exists.
> It has an old dnssec signed dns tree, using the isc dlv, which turned
> out to somewhat break older versions of dnsmasq, if that helps any.

OpenWrt CC trunk is now up to dnsmasq2.73rc7 - the latest fix was for a dnssec fallback to tcp
problem.  Highly recommended.

> So the next step for me is to get cake working in openwrt on hardware fast enough to run at 110Mbit and returning to the yurtlab to try it... but that won't be til sunday at best. Tho I almost got it built, at least, last night. Still sorting through patches....

I have no idea if this will help you guys.  I was fiddling with getting cake into CC a little earlier
based on the web page instructions.  My totally unsubtle approach was to basically copy over
relevant cerowrt-3.10 packages into the CC build tree:

ceropackages-3.10/net/sqm-scripts openwrt/package/feeds/packages/sqm-scripts
ceropackages-3.10/net/kmod-sched-cake openwrt/feeds/packages/net/kmod-sched-cake *

ceropackages-3.10/luci/luci-app-sqm openwrt/package/feeds/packages/luci-app-sqm

iproute2 140&141 patches to openwrt/package/network/utils/iproute2/patches

You'll need to put a symlink from openwrt/package/feeds/packages/kmod-sched-cake to
openwrt/feeds/packages/net/kmod-sched-cake

* note different directory!

Also to get iproute2 tc to understand cake I basically did a git diff on toke's iproute-cake
vs iproute 4.0.0 which is where CC currently is:  Result 2 patches, 140 & 141, 1st is to get
cake in, the 2nd is to do the required tc makefile tweak.  CC builds, runs on Archer C7,
nothing obviously exploded or broken...yet... your mileage may vary!  VERY VERY
UNTESTED, DRAGONS LURK, I'M AN IDIOT AND DON'T REALLY KNOW WHAT I'M DOING.
Is that clear enough? 

Maybe it helps.  What I will say is that a dslreports test appears less jittery for me.

Kevin



141-cake-disable-sfq-codel.patch

diff --git a/tc/Makefile b/tc/Makefile
index e503c8a..3dce533 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -61,7 +61,7 @@ TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
 TCMODULES += q_nfq_codel.o
 TCMODULES += q_efq_codel.o
-TCMODULES += q_sfq_codel.o
+#TCMODULES += q_sfq_codel.o
 TCMODULES += q_pfq_codel.o
 TCMODULES += q_ns2_codel.o
 TCMODULES += q_ns4_codel.o


140-cake-add-support.patch

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 534b847..a1d7b67 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -845,4 +845,38 @@ struct tc_pie_xstats {
     __u32 maxq;             /* maximum queue size */
     __u32 ecn_mark;         /* packets marked with ecn*/
 };
+
+/* CAKE */
+enum {
+    TCA_CAKE_UNSPEC,
+    TCA_CAKE_BASE_RATE,
+    TCA_CAKE_DIFFSERV_MODE,
+    TCA_CAKE_ATM,
+    TCA_CAKE_FLOW_MODE,
+    __TCA_CAKE_MAX
+};
+#define TCA_CAKE_MAX    (__TCA_CAKE_MAX - 1)
+
+struct tc_cake_xstats {
+    __u16 type;  /* constant magic 0xCAFE */
+    __u16 class_cnt;
+    struct {
+        __u32 rate;
+        __u32 target_us;
+        __u32 packets;
+        __u32 interval_us;
+        __u64 bytes;
+        __u32 dropped;
+        __u32 ecn_marked;
+        __u32 way_indirect_hits;
+        __u32 way_misses;
+        __u32 way_collisions;
+        __u32 backlog_bytes;
+        __u32 peak_delay; /* delay to fat flows */
+        __u32 avge_delay;
+        __u32 base_delay; /* delay to sparse flows */
+        __u32 dummy2;
+    } cls[8];
+};
+
 #endif
diff --git a/tc/Makefile b/tc/Makefile
index d831a15..e503c8a 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -59,8 +59,17 @@ TCMODULES += em_meta.o
 TCMODULES += q_mqprio.o
 TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
+TCMODULES += q_nfq_codel.o
+TCMODULES += q_efq_codel.o
+TCMODULES += q_sfq_codel.o
+TCMODULES += q_pfq_codel.o
+TCMODULES += q_ns2_codel.o
+TCMODULES += q_ns4_codel.o
 TCMODULES += q_fq.o
 TCMODULES += q_pie.o
+TCMODULES += q_cake.o
+TCMODULES += q_cake0.o
+TCMODULES += q_cake2.o
 TCMODULES += q_hhf.o
 
 ifeq ($(TC_CONFIG_IPSET), y)
diff --git a/tc/q_cake.c b/tc/q_cake.c
new file mode 100644
index 0000000..d9415d3
--- /dev/null
+++ b/tc/q_cake.c
@@ -0,0 +1,333 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014-2015 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... cake [ bandwidth RATE | unlimited ]\n"
+                    "                [ besteffort | precedence | diffserv8 | diffserv4 ]\n"
+                    "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+                    "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    int unlimited = 0;
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    int flowmode = -1;
+    int atm = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "bandwidth") == 0) {
+            NEXT_ARG();
+            if (get_rate(&bandwidth, *argv)) {
+                fprintf(stderr, "Illegal \"bandwidth\"\n");
+                return -1;
+            }
+            unlimited = 0;
+        } else if (strcmp(*argv, "unlimited") == 0) {
+            bandwidth = 0;
+            unlimited = 1;
+
+        } else if (strcmp(*argv, "besteffort") == 0) {
+            diffserv = 1;
+        } else if (strcmp(*argv, "precedence") == 0) {
+            diffserv = 2;
+        } else if (strcmp(*argv, "diffserv8") == 0) {
+            diffserv = 3;
+        } else if (strcmp(*argv, "diffserv4") == 0) {
+            diffserv = 4;
+        } else if (strcmp(*argv, "diffserv") == 0) {
+            diffserv = 4;
+
+        } else if (strcmp(*argv, "flowblind") == 0) {
+            flowmode = 0;
+        } else if (strcmp(*argv, "srchost") == 0) {
+            flowmode = 1;
+        } else if (strcmp(*argv, "dsthost") == 0) {
+            flowmode = 2;
+        } else if (strcmp(*argv, "hosts") == 0) {
+            flowmode = 3;
+        } else if (strcmp(*argv, "flows") == 0) {
+            flowmode = 4;
+
+        } else if (strcmp(*argv, "atm") == 0) {
+            atm = 1;
+        } else if (strcmp(*argv, "noatm") == 0) {
+            atm = 0;
+
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (bandwidth || unlimited)
+        addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+    if (diffserv)
+        addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+    if (atm != -1)
+        addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+    if (flowmode != -1)
+        addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CAKE_MAX + 1];
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    unsigned flowmode = 0;
+    int atm = -1;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+    if (tb[TCA_CAKE_BASE_RATE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+        bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+        if(bandwidth)
+            fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+        else
+            fprintf(f, "unlimited ");
+    }
+    if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+        diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+        switch(diffserv) {
+        case 1:
+            fprintf(f, "besteffort ");
+            break;
+        case 2:
+            fprintf(f, "precedence ");
+            break;
+        case 3:
+            fprintf(f, "diffserv8 ");
+            break;
+        case 4:
+            fprintf(f, "diffserv4 ");
+            break;
+        default:
+            fprintf(f, "(?diffserv?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_FLOW_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+        flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+        switch(flowmode) {
+        case 0:
+            fprintf(f, "flowblind ");
+            break;
+        case 1:
+            fprintf(f, "srchost ");
+            break;
+        case 2:
+            fprintf(f, "dsthost ");
+            break;
+        case 3:
+            fprintf(f, "hosts ");
+            break;
+        case 4:
+            fprintf(f, "flows ");
+            break;
+        default:
+            fprintf(f, "(?flowmode?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_ATM] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+        atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+        if (atm)
+            fprintf(f, "atm ");
+    }
+
+    return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    /* fq_codel stats format borrowed */
+    struct tc_fq_codel_xstats *st;
+    struct tc_cake_xstats     *stc;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+        return -1;
+
+    st  = RTA_DATA(xstats);
+    stc = RTA_DATA(xstats);
+
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    } else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    } else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+        int i;
+
+        fprintf(f, "        ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "   Class %u  ", i);
+        fprintf(f, "\n");
+
+        fprintf(f, "  rate  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_rate(stc->cls[i].rate, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  target");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].target_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "interval");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].interval_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "Pk delay");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].peak_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "Av delay");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].avge_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "Sp delay");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12s", sprint_time(stc->cls[i].base_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  pkts  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].packets);
+        fprintf(f, "\n");
+
+        fprintf(f, "way inds");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].way_indirect_hits);
+        fprintf(f, "\n");
+
+        fprintf(f, "way miss");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].way_misses);
+        fprintf(f, "\n");
+
+        fprintf(f, "way cols");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].way_collisions);
+        fprintf(f, "\n");
+
+        fprintf(f, "  bytes ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12llu", stc->cls[i].bytes);
+        fprintf(f, "\n");
+
+        fprintf(f, "  drops ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].dropped);
+        fprintf(f, "\n");
+
+        fprintf(f, "  marks ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%12u", stc->cls[i].ecn_marked);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+struct qdisc_util cake_qdisc_util = {
+    .id        = "cake",
+    .parse_qopt    = cake_parse_opt,
+    .print_qopt    = cake_print_opt,
+    .print_xstats    = cake_print_xstats,
+};
diff --git a/tc/q_cake0.c b/tc/q_cake0.c
new file mode 100644
index 0000000..9fb63ed
--- /dev/null
+++ b/tc/q_cake0.c
@@ -0,0 +1,301 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... cake0 [ bandwidth RATE | unlimited ]\n"
+                    "                 [ besteffort | precedence | diffserv ]\n"
+                    "                 [ flowblind | srchost | dsthost | hosts | flows ]\n"
+                    "                 [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    int unlimited = 0;
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    int flowmode = -1;
+    int atm = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "bandwidth") == 0) {
+            NEXT_ARG();
+            if (get_rate(&bandwidth, *argv)) {
+                fprintf(stderr, "Illegal \"bandwidth\"\n");
+                return -1;
+            }
+            unlimited = 0;
+        } else if (strcmp(*argv, "unlimited") == 0) {
+            bandwidth = 0;
+            unlimited = 1;
+
+        } else if (strcmp(*argv, "besteffort") == 0) {
+            diffserv = 1;
+        } else if (strcmp(*argv, "precedence") == 0) {
+            diffserv = 2;
+        } else if (strcmp(*argv, "diffserv") == 0) {
+            diffserv = 3;
+
+        } else if (strcmp(*argv, "flowblind") == 0) {
+            flowmode = 0;
+        } else if (strcmp(*argv, "srchost") == 0) {
+            flowmode = 1;
+        } else if (strcmp(*argv, "dsthost") == 0) {
+            flowmode = 2;
+        } else if (strcmp(*argv, "hosts") == 0) {
+            flowmode = 3;
+        } else if (strcmp(*argv, "flows") == 0) {
+            flowmode = 4;
+
+        } else if (strcmp(*argv, "atm") == 0) {
+            atm = 1;
+        } else if (strcmp(*argv, "noatm") == 0) {
+            atm = 0;
+
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (bandwidth || unlimited)
+        addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+    if (diffserv)
+        addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+    if (atm != -1)
+        addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+    if (flowmode != -1)
+        addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CAKE_MAX + 1];
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    unsigned flowmode = 0;
+    int atm = -1;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+    if (tb[TCA_CAKE_BASE_RATE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+        bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+        if(bandwidth)
+            fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+        else
+            fprintf(f, "unlimited");
+    }
+    if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+        diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+        switch(diffserv) {
+        case 1:
+            fprintf(f, "besteffort ");
+            break;
+        case 2:
+            fprintf(f, "precedence ");
+            break;
+        case 3:
+            fprintf(f, "diffserv ");
+            break;
+        default:
+            fprintf(f, "(?diffserv?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_FLOW_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+        flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+        switch(flowmode) {
+        case 0:
+            fprintf(f, "flowblind ");
+            break;
+        case 1:
+            fprintf(f, "srchost ");
+            break;
+        case 2:
+            fprintf(f, "dsthost ");
+            break;
+        case 3:
+            fprintf(f, "hosts ");
+            break;
+        case 4:
+            fprintf(f, "flows ");
+            break;
+        default:
+            fprintf(f, "(?flowmode?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_ATM] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+        atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+        if (atm)
+            fprintf(f, "atm ");
+    }
+
+    return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    /* fq_codel stats format borrowed */
+    struct tc_fq_codel_xstats *st;
+    struct tc_cake_xstats     *stc;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+        return -1;
+
+    st  = RTA_DATA(xstats);
+    stc = RTA_DATA(xstats);
+
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    } else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    } else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+        int i;
+
+        fprintf(f, "        ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "  Class %u ", i);
+        fprintf(f, "\n");
+
+        fprintf(f, "  rate  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  target");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "interval");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  delay ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].peak_delay, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  pkts  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].packets);
+        fprintf(f, "\n");
+
+        fprintf(f, "  bytes ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10llu", stc->cls[i].bytes);
+        fprintf(f, "\n");
+
+        fprintf(f, "  drops ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].dropped);
+        fprintf(f, "\n");
+
+        fprintf(f, "  marks ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].ecn_marked);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+struct qdisc_util cake0_qdisc_util = {
+    .id        = "cake0",
+    .parse_qopt    = cake_parse_opt,
+    .print_qopt    = cake_print_opt,
+    .print_xstats    = cake_print_xstats,
+};
diff --git a/tc/q_cake2.c b/tc/q_cake2.c
new file mode 100644
index 0000000..a4d3f7c
--- /dev/null
+++ b/tc/q_cake2.c
@@ -0,0 +1,296 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... cake2 [ bandwidth RATE | unlimited ]\n"
+                    "                [ besteffort | precedence | diffserv ]\n"
+                    "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+                    "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    int unlimited = 0;
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    int flowmode = -1;
+    int atm = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "bandwidth") == 0) {
+            NEXT_ARG();
+            if (get_rate(&bandwidth, *argv)) {
+                fprintf(stderr, "Illegal \"bandwidth\"\n");
+                return -1;
+            }
+            unlimited = 0;
+        } else if (strcmp(*argv, "unlimited") == 0) {
+            bandwidth = 0;
+            unlimited = 1;
+
+        } else if (strcmp(*argv, "besteffort") == 0) {
+            diffserv = 1;
+        } else if (strcmp(*argv, "precedence") == 0) {
+            diffserv = 2;
+        } else if (strcmp(*argv, "diffserv") == 0) {
+            diffserv = 3;
+
+        } else if (strcmp(*argv, "flowblind") == 0) {
+            flowmode = 0;
+        } else if (strcmp(*argv, "srchost") == 0) {
+            flowmode = 1;
+        } else if (strcmp(*argv, "dsthost") == 0) {
+            flowmode = 2;
+        } else if (strcmp(*argv, "hosts") == 0) {
+            flowmode = 3;
+        } else if (strcmp(*argv, "flows") == 0) {
+            flowmode = 4;
+
+        } else if (strcmp(*argv, "atm") == 0) {
+            atm = 1;
+        } else if (strcmp(*argv, "noatm") == 0) {
+            atm = 0;
+
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (bandwidth || unlimited)
+        addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+    if (diffserv)
+        addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+    if (atm != -1)
+        addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+    if (flowmode != -1)
+        addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CAKE_MAX + 1];
+    unsigned bandwidth = 0;
+    unsigned diffserv = 0;
+    unsigned flowmode = 0;
+    int atm = -1;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+    if (tb[TCA_CAKE_BASE_RATE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+        bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+        if(bandwidth)
+            fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+        else
+            fprintf(f, "unlimited");
+    }
+    if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+        diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+        switch(diffserv) {
+        case 1:
+            fprintf(f, "besteffort ");
+            break;
+        case 2:
+            fprintf(f, "precedence ");
+            break;
+        case 3:
+            fprintf(f, "diffserv ");
+            break;
+        default:
+            fprintf(f, "(?diffserv?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_FLOW_MODE] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+        flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+        switch(flowmode) {
+        case 0:
+            fprintf(f, "flowblind ");
+            break;
+        case 1:
+            fprintf(f, "srchost ");
+            break;
+        case 2:
+            fprintf(f, "dsthost ");
+            break;
+        case 3:
+            fprintf(f, "hosts ");
+            break;
+        case 4:
+            fprintf(f, "flows ");
+            break;
+        default:
+            fprintf(f, "(?flowmode?) ");
+            break;
+        };
+    }
+    if (tb[TCA_CAKE_ATM] &&
+        RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+        atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+        if (atm)
+            fprintf(f, "atm ");
+    }
+
+    return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    /* fq_codel stats format borrowed */
+    struct tc_fq_codel_xstats *st;
+    struct tc_cake_xstats     *stc;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+        return -1;
+
+    st  = RTA_DATA(xstats);
+    stc = RTA_DATA(xstats);
+
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    } else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    } else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+        int i;
+
+        fprintf(f, "        ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "  Class %u ", i);
+        fprintf(f, "\n");
+
+        fprintf(f, "  rate  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  target");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "interval");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+        fprintf(f, "\n");
+
+        fprintf(f, "  pkts  ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].packets);
+        fprintf(f, "\n");
+
+        fprintf(f, "  bytes ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10llu", stc->cls[i].bytes);
+        fprintf(f, "\n");
+
+        fprintf(f, "  drops ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].dropped);
+        fprintf(f, "\n");
+
+        fprintf(f, "  marks ");
+        for(i=0; i < stc->class_cnt; i++)
+            fprintf(f, "%10u", stc->cls[i].ecn_marked);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+struct qdisc_util cake2_qdisc_util = {
+    .id        = "cake2",
+    .parse_qopt    = cake_parse_opt,
+    .print_qopt    = cake_print_opt,
+    .print_xstats    = cake_print_xstats,
+};
diff --git a/tc/q_efq_codel.c b/tc/q_efq_codel.c
new file mode 100644
index 0000000..b80e5e4
--- /dev/null
+++ b/tc/q_efq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... efq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+    fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+    fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned flows = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    unsigned quantum = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "flows") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&flows, *argv, 0)) {
+                fprintf(stderr, "Illegal \"flows\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "quantum") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&quantum, *argv, 0)) {
+                fprintf(stderr, "Illegal \"quantum\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+    if (flows)
+        addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+    if (quantum)
+        addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+    if (interval)
+        addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned flows;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    unsigned quantum;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+    if (tb[TCA_FQ_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_FQ_CODEL_FLOWS] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+        flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+        fprintf(f, "flows %u ", flows);
+    }
+    if (tb[TCA_FQ_CODEL_QUANTUM] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+        quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+        fprintf(f, "quantum %u ", quantum);
+    }
+    if (tb[TCA_FQ_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_FQ_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_FQ_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    struct tc_fq_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    }
+    if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    }
+    return 0;
+
+}
+
+struct qdisc_util efq_codel_qdisc_util = {
+    .id        = "efq_codel",
+    .parse_qopt    = fq_codel_parse_opt,
+    .print_qopt    = fq_codel_print_opt,
+    .print_xstats    = fq_codel_print_xstats,
+};
diff --git a/tc/q_nfq_codel.c b/tc/q_nfq_codel.c
new file mode 100644
index 0000000..ef24909
--- /dev/null
+++ b/tc/q_nfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... nfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+    fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+    fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned flows = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    unsigned quantum = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "flows") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&flows, *argv, 0)) {
+                fprintf(stderr, "Illegal \"flows\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "quantum") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&quantum, *argv, 0)) {
+                fprintf(stderr, "Illegal \"quantum\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+    if (flows)
+        addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+    if (quantum)
+        addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+    if (interval)
+        addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned flows;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    unsigned quantum;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+    if (tb[TCA_FQ_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_FQ_CODEL_FLOWS] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+        flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+        fprintf(f, "flows %u ", flows);
+    }
+    if (tb[TCA_FQ_CODEL_QUANTUM] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+        quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+        fprintf(f, "quantum %u ", quantum);
+    }
+    if (tb[TCA_FQ_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_FQ_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_FQ_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    struct tc_fq_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    }
+    if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    }
+    return 0;
+
+}
+
+struct qdisc_util nfq_codel_qdisc_util = {
+    .id        = "nfq_codel",
+    .parse_qopt    = fq_codel_parse_opt,
+    .print_qopt    = fq_codel_print_opt,
+    .print_xstats    = fq_codel_print_xstats,
+};
diff --git a/tc/q_ns2_codel.c b/tc/q_ns2_codel.c
new file mode 100644
index 0000000..223a971
--- /dev/null
+++ b/tc/q_ns2_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... ns2_codel [ limit PACKETS ] [ target TIME]\n");
+    fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+               struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+    if (interval)
+        addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+    if (tb[TCA_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                  struct rtattr *xstats)
+{
+    struct tc_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    fprintf(f, "  count %u lastcount %u ldelay %s",
+        st->count, st->lastcount, sprint_time(st->ldelay, b1));
+    if (st->dropping)
+        fprintf(f, " dropping");
+    if (st->drop_next < 0)
+        fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+    else
+        fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+    fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+        st->maxpacket, st->ecn_mark, st->drop_overlimit);
+    return 0;
+
+}
+
+struct qdisc_util ns2_codel_qdisc_util = {
+    .id        = "ns2_codel",
+    .parse_qopt    = codel_parse_opt,
+    .print_qopt    = codel_print_opt,
+    .print_xstats    = codel_print_xstats,
+};
diff --git a/tc/q_ns4_codel.c b/tc/q_ns4_codel.c
new file mode 100644
index 0000000..0aaa349
--- /dev/null
+++ b/tc/q_ns4_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... ns4_codel [ limit PACKETS ] [ target TIME]\n");
+    fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+               struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+    if (interval)
+        addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+    if (tb[TCA_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                  struct rtattr *xstats)
+{
+    struct tc_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    fprintf(f, "  count %u lastcount %u ldelay %s",
+        st->count, st->lastcount, sprint_time(st->ldelay, b1));
+    if (st->dropping)
+        fprintf(f, " dropping");
+    if (st->drop_next < 0)
+        fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+    else
+        fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+    fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+        st->maxpacket, st->ecn_mark, st->drop_overlimit);
+    return 0;
+
+}
+
+struct qdisc_util ns4_codel_qdisc_util = {
+    .id        = "ns4_codel",
+    .parse_qopt    = codel_parse_opt,
+    .print_qopt    = codel_print_opt,
+    .print_xstats    = codel_print_xstats,
+};
diff --git a/tc/q_pfq_codel.c b/tc/q_pfq_codel.c
new file mode 100644
index 0000000..52c5160
--- /dev/null
+++ b/tc/q_pfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+    fprintf(stderr, "Usage: ... pfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+    fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+    fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+                  struct nlmsghdr *n)
+{
+    unsigned limit = 0;
+    unsigned flows = 0;
+    unsigned target = 0;
+    unsigned interval = 0;
+    unsigned quantum = 0;
+    int ecn = -1;
+    struct rtattr *tail;
+
+    while (argc > 0) {
+        if (strcmp(*argv, "limit") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&limit, *argv, 0)) {
+                fprintf(stderr, "Illegal \"limit\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "flows") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&flows, *argv, 0)) {
+                fprintf(stderr, "Illegal \"flows\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "quantum") == 0) {
+            NEXT_ARG();
+            if (get_unsigned(&quantum, *argv, 0)) {
+                fprintf(stderr, "Illegal \"quantum\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "target") == 0) {
+            NEXT_ARG();
+            if (get_time(&target, *argv)) {
+                fprintf(stderr, "Illegal \"target\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "interval") == 0) {
+            NEXT_ARG();
+            if (get_time(&interval, *argv)) {
+                fprintf(stderr, "Illegal \"interval\"\n");
+                return -1;
+            }
+        } else if (strcmp(*argv, "ecn") == 0) {
+            ecn = 1;
+        } else if (strcmp(*argv, "noecn") == 0) {
+            ecn = 0;
+        } else if (strcmp(*argv, "help") == 0) {
+            explain();
+            return -1;
+        } else {
+            fprintf(stderr, "What is \"%s\"?\n", *argv);
+            explain();
+            return -1;
+        }
+        argc--; argv++;
+    }
+
+    tail = NLMSG_TAIL(n);
+    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+    if (limit)
+        addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+    if (flows)
+        addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+    if (quantum)
+        addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+    if (interval)
+        addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+    if (target)
+        addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+    if (ecn != -1)
+        addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+    return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+    struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+    unsigned limit;
+    unsigned flows;
+    unsigned interval;
+    unsigned target;
+    unsigned ecn;
+    unsigned quantum;
+    SPRINT_BUF(b1);
+
+    if (opt == NULL)
+        return 0;
+
+    parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+    if (tb[TCA_FQ_CODEL_LIMIT] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+        limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+        fprintf(f, "limit %up ", limit);
+    }
+    if (tb[TCA_FQ_CODEL_FLOWS] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+        flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+        fprintf(f, "flows %u ", flows);
+    }
+    if (tb[TCA_FQ_CODEL_QUANTUM] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+        quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+        fprintf(f, "quantum %u ", quantum);
+    }
+    if (tb[TCA_FQ_CODEL_TARGET] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+        target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+        fprintf(f, "target %s ", sprint_time(target, b1));
+    }
+    if (tb[TCA_FQ_CODEL_INTERVAL] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+        interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+        fprintf(f, "interval %s ", sprint_time(interval, b1));
+    }
+    if (tb[TCA_FQ_CODEL_ECN] &&
+        RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+        ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+        if (ecn)
+            fprintf(f, "ecn ");
+    }
+
+    return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+                 struct rtattr *xstats)
+{
+    struct tc_fq_codel_xstats *st;
+    SPRINT_BUF(b1);
+
+    if (xstats == NULL)
+        return 0;
+
+    if (RTA_PAYLOAD(xstats) < sizeof(*st))
+        return -1;
+
+    st = RTA_DATA(xstats);
+    if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+        fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+            st->qdisc_stats.maxpacket,
+            st->qdisc_stats.drop_overlimit,
+            st->qdisc_stats.new_flow_count,
+            st->qdisc_stats.ecn_mark);
+        fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+            st->qdisc_stats.new_flows_len,
+            st->qdisc_stats.old_flows_len);
+    }
+    if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+        fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+            st->class_stats.deficit,
+            st->class_stats.count,
+            st->class_stats.lastcount,
+            sprint_time(st->class_stats.ldelay, b1));
+        if (st->class_stats.dropping) {
+            fprintf(f, " dropping");
+            if (st->class_stats.drop_next < 0)
+                fprintf(f, " drop_next -%s",
+                    sprint_time(-st->class_stats.drop_next, b1));
+            else
+                fprintf(f, " drop_next %s",
+                    sprint_time(st->class_stats.drop_next, b1));
+        }
+    }
+    return 0;
+
+}
+
+struct qdisc_util pfq_codel_qdisc_util = {
+    .id        = "pfq_codel",
+    .parse_qopt    = fq_codel_parse_opt,
+    .print_qopt    = fq_codel_print_opt,
+    .print_xstats    = fq_codel_print_xstats,
+};

141-cake-add-support.patch




140-cake-add-support.patch


diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 534b847..a1d7b67 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -845,4 +845,38 @@ struct tc_pie_xstats {
 	__u32 maxq;             /* maximum queue size */
 	__u32 ecn_mark;         /* packets marked with ecn*/
 };
+
+/* CAKE */
+enum {
+	TCA_CAKE_UNSPEC,
+	TCA_CAKE_BASE_RATE,
+	TCA_CAKE_DIFFSERV_MODE,
+	TCA_CAKE_ATM,
+	TCA_CAKE_FLOW_MODE,
+	__TCA_CAKE_MAX
+};
+#define TCA_CAKE_MAX	(__TCA_CAKE_MAX - 1)
+
+struct tc_cake_xstats {
+	__u16 type;  /* constant magic 0xCAFE */
+	__u16 class_cnt;
+	struct {
+		__u32 rate;
+		__u32 target_us;
+		__u32 packets;
+		__u32 interval_us;
+		__u64 bytes;
+		__u32 dropped;
+		__u32 ecn_marked;
+		__u32 way_indirect_hits;
+		__u32 way_misses;
+		__u32 way_collisions;
+		__u32 backlog_bytes;
+		__u32 peak_delay; /* delay to fat flows */
+		__u32 avge_delay;
+		__u32 base_delay; /* delay to sparse flows */
+		__u32 dummy2;
+	} cls[8];
+};
+
 #endif
diff --git a/tc/Makefile b/tc/Makefile
index d831a15..e503c8a 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -59,8 +59,17 @@ TCMODULES += em_meta.o
 TCMODULES += q_mqprio.o
 TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
+TCMODULES += q_nfq_codel.o
+TCMODULES += q_efq_codel.o
+TCMODULES += q_sfq_codel.o
+TCMODULES += q_pfq_codel.o
+TCMODULES += q_ns2_codel.o
+TCMODULES += q_ns4_codel.o
 TCMODULES += q_fq.o
 TCMODULES += q_pie.o
+TCMODULES += q_cake.o
+TCMODULES += q_cake0.o
+TCMODULES += q_cake2.o
 TCMODULES += q_hhf.o
 
 ifeq ($(TC_CONFIG_IPSET), y)
diff --git a/tc/q_cake.c b/tc/q_cake.c
new file mode 100644
index 0000000..d9415d3
--- /dev/null
+++ b/tc/q_cake.c
@@ -0,0 +1,333 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014-2015 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... cake [ bandwidth RATE | unlimited ]\n"
+	                "                [ besteffort | precedence | diffserv8 | diffserv4 ]\n"
+	                "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+	                "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	int unlimited = 0;
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	int flowmode = -1;
+	int atm = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&bandwidth, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+			unlimited = 0;
+		} else if (strcmp(*argv, "unlimited") == 0) {
+			bandwidth = 0;
+			unlimited = 1;
+
+		} else if (strcmp(*argv, "besteffort") == 0) {
+			diffserv = 1;
+		} else if (strcmp(*argv, "precedence") == 0) {
+			diffserv = 2;
+		} else if (strcmp(*argv, "diffserv8") == 0) {
+			diffserv = 3;
+		} else if (strcmp(*argv, "diffserv4") == 0) {
+			diffserv = 4;
+		} else if (strcmp(*argv, "diffserv") == 0) {
+			diffserv = 4;
+
+		} else if (strcmp(*argv, "flowblind") == 0) {
+			flowmode = 0;
+		} else if (strcmp(*argv, "srchost") == 0) {
+			flowmode = 1;
+		} else if (strcmp(*argv, "dsthost") == 0) {
+			flowmode = 2;
+		} else if (strcmp(*argv, "hosts") == 0) {
+			flowmode = 3;
+		} else if (strcmp(*argv, "flows") == 0) {
+			flowmode = 4;
+
+		} else if (strcmp(*argv, "atm") == 0) {
+			atm = 1;
+		} else if (strcmp(*argv, "noatm") == 0) {
+			atm = 0;
+
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (bandwidth || unlimited)
+		addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+	if (diffserv)
+		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+	if (atm != -1)
+		addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+	if (flowmode != -1)
+		addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CAKE_MAX + 1];
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	unsigned flowmode = 0;
+	int atm = -1;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+	if (tb[TCA_CAKE_BASE_RATE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+		bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+		if(bandwidth)
+			fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+		else
+			fprintf(f, "unlimited ");
+	}
+	if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+		diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+		switch(diffserv) {
+		case 1:
+			fprintf(f, "besteffort ");
+			break;
+		case 2:
+			fprintf(f, "precedence ");
+			break;
+		case 3:
+			fprintf(f, "diffserv8 ");
+			break;
+		case 4:
+			fprintf(f, "diffserv4 ");
+			break;
+		default:
+			fprintf(f, "(?diffserv?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_FLOW_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+		flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+		switch(flowmode) {
+		case 0:
+			fprintf(f, "flowblind ");
+			break;
+		case 1:
+			fprintf(f, "srchost ");
+			break;
+		case 2:
+			fprintf(f, "dsthost ");
+			break;
+		case 3:
+			fprintf(f, "hosts ");
+			break;
+		case 4:
+			fprintf(f, "flows ");
+			break;
+		default:
+			fprintf(f, "(?flowmode?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_ATM] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+		atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+		if (atm)
+			fprintf(f, "atm ");
+	}
+
+	return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	/* fq_codel stats format borrowed */
+	struct tc_fq_codel_xstats *st;
+	struct tc_cake_xstats     *stc;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+		return -1;
+
+	st  = RTA_DATA(xstats);
+	stc = RTA_DATA(xstats);
+
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	} else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	} else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+		int i;
+
+		fprintf(f, "        ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "   Class %u  ", i);
+		fprintf(f, "\n");
+
+		fprintf(f, "  rate  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_rate(stc->cls[i].rate, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  target");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].target_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "interval");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].interval_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "Pk delay");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].peak_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "Av delay");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].avge_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "Sp delay");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12s", sprint_time(stc->cls[i].base_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  pkts  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].packets);
+		fprintf(f, "\n");
+
+		fprintf(f, "way inds");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].way_indirect_hits);
+		fprintf(f, "\n");
+
+		fprintf(f, "way miss");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].way_misses);
+		fprintf(f, "\n");
+
+		fprintf(f, "way cols");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].way_collisions);
+		fprintf(f, "\n");
+
+		fprintf(f, "  bytes ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12llu", stc->cls[i].bytes);
+		fprintf(f, "\n");
+
+		fprintf(f, "  drops ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].dropped);
+		fprintf(f, "\n");
+
+		fprintf(f, "  marks ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%12u", stc->cls[i].ecn_marked);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+struct qdisc_util cake_qdisc_util = {
+	.id		= "cake",
+	.parse_qopt	= cake_parse_opt,
+	.print_qopt	= cake_print_opt,
+	.print_xstats	= cake_print_xstats,
+};
diff --git a/tc/q_cake0.c b/tc/q_cake0.c
new file mode 100644
index 0000000..9fb63ed
--- /dev/null
+++ b/tc/q_cake0.c
@@ -0,0 +1,301 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... cake0 [ bandwidth RATE | unlimited ]\n"
+	                "                 [ besteffort | precedence | diffserv ]\n"
+	                "                 [ flowblind | srchost | dsthost | hosts | flows ]\n"
+	                "                 [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	int unlimited = 0;
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	int flowmode = -1;
+	int atm = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&bandwidth, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+			unlimited = 0;
+		} else if (strcmp(*argv, "unlimited") == 0) {
+			bandwidth = 0;
+			unlimited = 1;
+
+		} else if (strcmp(*argv, "besteffort") == 0) {
+			diffserv = 1;
+		} else if (strcmp(*argv, "precedence") == 0) {
+			diffserv = 2;
+		} else if (strcmp(*argv, "diffserv") == 0) {
+			diffserv = 3;
+
+		} else if (strcmp(*argv, "flowblind") == 0) {
+			flowmode = 0;
+		} else if (strcmp(*argv, "srchost") == 0) {
+			flowmode = 1;
+		} else if (strcmp(*argv, "dsthost") == 0) {
+			flowmode = 2;
+		} else if (strcmp(*argv, "hosts") == 0) {
+			flowmode = 3;
+		} else if (strcmp(*argv, "flows") == 0) {
+			flowmode = 4;
+
+		} else if (strcmp(*argv, "atm") == 0) {
+			atm = 1;
+		} else if (strcmp(*argv, "noatm") == 0) {
+			atm = 0;
+
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (bandwidth || unlimited)
+		addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+	if (diffserv)
+		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+	if (atm != -1)
+		addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+	if (flowmode != -1)
+		addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CAKE_MAX + 1];
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	unsigned flowmode = 0;
+	int atm = -1;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+	if (tb[TCA_CAKE_BASE_RATE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+		bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+		if(bandwidth)
+			fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+		else
+			fprintf(f, "unlimited");
+	}
+	if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+		diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+		switch(diffserv) {
+		case 1:
+			fprintf(f, "besteffort ");
+			break;
+		case 2:
+			fprintf(f, "precedence ");
+			break;
+		case 3:
+			fprintf(f, "diffserv ");
+			break;
+		default:
+			fprintf(f, "(?diffserv?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_FLOW_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+		flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+		switch(flowmode) {
+		case 0:
+			fprintf(f, "flowblind ");
+			break;
+		case 1:
+			fprintf(f, "srchost ");
+			break;
+		case 2:
+			fprintf(f, "dsthost ");
+			break;
+		case 3:
+			fprintf(f, "hosts ");
+			break;
+		case 4:
+			fprintf(f, "flows ");
+			break;
+		default:
+			fprintf(f, "(?flowmode?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_ATM] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+		atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+		if (atm)
+			fprintf(f, "atm ");
+	}
+
+	return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	/* fq_codel stats format borrowed */
+	struct tc_fq_codel_xstats *st;
+	struct tc_cake_xstats     *stc;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+		return -1;
+
+	st  = RTA_DATA(xstats);
+	stc = RTA_DATA(xstats);
+
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	} else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	} else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+		int i;
+
+		fprintf(f, "        ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "  Class %u ", i);
+		fprintf(f, "\n");
+
+		fprintf(f, "  rate  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  target");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "interval");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  delay ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].peak_delay, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  pkts  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].packets);
+		fprintf(f, "\n");
+
+		fprintf(f, "  bytes ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10llu", stc->cls[i].bytes);
+		fprintf(f, "\n");
+
+		fprintf(f, "  drops ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].dropped);
+		fprintf(f, "\n");
+
+		fprintf(f, "  marks ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].ecn_marked);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+struct qdisc_util cake0_qdisc_util = {
+	.id		= "cake0",
+	.parse_qopt	= cake_parse_opt,
+	.print_qopt	= cake_print_opt,
+	.print_xstats	= cake_print_xstats,
+};
diff --git a/tc/q_cake2.c b/tc/q_cake2.c
new file mode 100644
index 0000000..a4d3f7c
--- /dev/null
+++ b/tc/q_cake2.c
@@ -0,0 +1,296 @@
+/*
+ * Common Applications Kept Enhanced  --  CAKE
+ *
+ *  Copyright (C) 2014 Jonathan Morton <chromatix99@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... cake2 [ bandwidth RATE | unlimited ]\n"
+	                "                [ besteffort | precedence | diffserv ]\n"
+	                "                [ flowblind | srchost | dsthost | hosts | flows ]\n"
+	                "                [ atm ]\n");
+}
+
+static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	int unlimited = 0;
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	int flowmode = -1;
+	int atm = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "bandwidth") == 0) {
+			NEXT_ARG();
+			if (get_rate(&bandwidth, *argv)) {
+				fprintf(stderr, "Illegal \"bandwidth\"\n");
+				return -1;
+			}
+			unlimited = 0;
+		} else if (strcmp(*argv, "unlimited") == 0) {
+			bandwidth = 0;
+			unlimited = 1;
+
+		} else if (strcmp(*argv, "besteffort") == 0) {
+			diffserv = 1;
+		} else if (strcmp(*argv, "precedence") == 0) {
+			diffserv = 2;
+		} else if (strcmp(*argv, "diffserv") == 0) {
+			diffserv = 3;
+
+		} else if (strcmp(*argv, "flowblind") == 0) {
+			flowmode = 0;
+		} else if (strcmp(*argv, "srchost") == 0) {
+			flowmode = 1;
+		} else if (strcmp(*argv, "dsthost") == 0) {
+			flowmode = 2;
+		} else if (strcmp(*argv, "hosts") == 0) {
+			flowmode = 3;
+		} else if (strcmp(*argv, "flows") == 0) {
+			flowmode = 4;
+
+		} else if (strcmp(*argv, "atm") == 0) {
+			atm = 1;
+		} else if (strcmp(*argv, "noatm") == 0) {
+			atm = 0;
+
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (bandwidth || unlimited)
+		addattr_l(n, 1024, TCA_CAKE_BASE_RATE, &bandwidth, sizeof(bandwidth));
+	if (diffserv)
+		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, sizeof(diffserv));
+	if (atm != -1)
+		addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm));
+	if (flowmode != -1)
+		addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, sizeof(flowmode));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CAKE_MAX + 1];
+	unsigned bandwidth = 0;
+	unsigned diffserv = 0;
+	unsigned flowmode = 0;
+	int atm = -1;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CAKE_MAX, opt);
+
+	if (tb[TCA_CAKE_BASE_RATE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE]) >= sizeof(__u32)) {
+		bandwidth = rta_getattr_u32(tb[TCA_CAKE_BASE_RATE]);
+		if(bandwidth)
+			fprintf(f, "bandwidth %s ", sprint_rate(bandwidth, b1));
+		else
+			fprintf(f, "unlimited");
+	}
+	if (tb[TCA_CAKE_DIFFSERV_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+		diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+		switch(diffserv) {
+		case 1:
+			fprintf(f, "besteffort ");
+			break;
+		case 2:
+			fprintf(f, "precedence ");
+			break;
+		case 3:
+			fprintf(f, "diffserv ");
+			break;
+		default:
+			fprintf(f, "(?diffserv?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_FLOW_MODE] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+		flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
+		switch(flowmode) {
+		case 0:
+			fprintf(f, "flowblind ");
+			break;
+		case 1:
+			fprintf(f, "srchost ");
+			break;
+		case 2:
+			fprintf(f, "dsthost ");
+			break;
+		case 3:
+			fprintf(f, "hosts ");
+			break;
+		case 4:
+			fprintf(f, "flows ");
+			break;
+		default:
+			fprintf(f, "(?flowmode?) ");
+			break;
+		};
+	}
+	if (tb[TCA_CAKE_ATM] &&
+	    RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) {
+		atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+		if (atm)
+			fprintf(f, "atm ");
+	}
+
+	return 0;
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	/* fq_codel stats format borrowed */
+	struct tc_fq_codel_xstats *st;
+	struct tc_cake_xstats     *stc;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(st->type))
+		return -1;
+
+	st  = RTA_DATA(xstats);
+	stc = RTA_DATA(xstats);
+
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	} else if (st->type == TCA_FQ_CODEL_XSTATS_CLASS && RTA_PAYLOAD(xstats) >= sizeof(*st)) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	} else if (stc->type == 0xCAFE && RTA_PAYLOAD(xstats) >= sizeof(*stc)) {
+		int i;
+
+		fprintf(f, "        ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "  Class %u ", i);
+		fprintf(f, "\n");
+
+		fprintf(f, "  rate  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_rate(stc->cls[i].rate, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  target");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].target_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "interval");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10s", sprint_time(stc->cls[i].interval_us, b1));
+		fprintf(f, "\n");
+
+		fprintf(f, "  pkts  ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].packets);
+		fprintf(f, "\n");
+
+		fprintf(f, "  bytes ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10llu", stc->cls[i].bytes);
+		fprintf(f, "\n");
+
+		fprintf(f, "  drops ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].dropped);
+		fprintf(f, "\n");
+
+		fprintf(f, "  marks ");
+		for(i=0; i < stc->class_cnt; i++)
+			fprintf(f, "%10u", stc->cls[i].ecn_marked);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+struct qdisc_util cake2_qdisc_util = {
+	.id		= "cake2",
+	.parse_qopt	= cake_parse_opt,
+	.print_qopt	= cake_print_opt,
+	.print_xstats	= cake_print_xstats,
+};
diff --git a/tc/q_efq_codel.c b/tc/q_efq_codel.c
new file mode 100644
index 0000000..b80e5e4
--- /dev/null
+++ b/tc/q_efq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... efq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+	fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+	fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned flows = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	unsigned quantum = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "quantum") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&quantum, *argv, 0)) {
+				fprintf(stderr, "Illegal \"quantum\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+	if (flows)
+		addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+	if (quantum)
+		addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+	if (interval)
+		addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned flows;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	unsigned quantum;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+	if (tb[TCA_FQ_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_FQ_CODEL_FLOWS] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+		flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+		fprintf(f, "flows %u ", flows);
+	}
+	if (tb[TCA_FQ_CODEL_QUANTUM] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+		quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+		fprintf(f, "quantum %u ", quantum);
+	}
+	if (tb[TCA_FQ_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_FQ_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_FQ_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	struct tc_fq_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+

+	st = RTA_DATA(xstats);
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	}
+	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	}
+	return 0;
+
+}
+
+struct qdisc_util efq_codel_qdisc_util = {
+	.id		= "efq_codel",
+	.parse_qopt	= fq_codel_parse_opt,
+	.print_qopt	= fq_codel_print_opt,
+	.print_xstats	= fq_codel_print_xstats,
+};
diff --git a/tc/q_nfq_codel.c b/tc/q_nfq_codel.c
new file mode 100644
index 0000000..ef24909
--- /dev/null
+++ b/tc/q_nfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... nfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+	fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+	fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned flows = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	unsigned quantum = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "quantum") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&quantum, *argv, 0)) {
+				fprintf(stderr, "Illegal \"quantum\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+	if (flows)
+		addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+	if (quantum)
+		addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+	if (interval)
+		addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned flows;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	unsigned quantum;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+	if (tb[TCA_FQ_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_FQ_CODEL_FLOWS] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+		flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+		fprintf(f, "flows %u ", flows);
+	}
+	if (tb[TCA_FQ_CODEL_QUANTUM] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+		quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+		fprintf(f, "quantum %u ", quantum);
+	}
+	if (tb[TCA_FQ_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_FQ_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_FQ_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	struct tc_fq_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	}
+	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	}
+	return 0;
+
+}
+
+struct qdisc_util nfq_codel_qdisc_util = {
+	.id		= "nfq_codel",
+	.parse_qopt	= fq_codel_parse_opt,
+	.print_qopt	= fq_codel_print_opt,
+	.print_xstats	= fq_codel_print_xstats,
+};
diff --git a/tc/q_ns2_codel.c b/tc/q_ns2_codel.c
new file mode 100644
index 0000000..223a971
--- /dev/null
+++ b/tc/q_ns2_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... ns2_codel [ limit PACKETS ] [ target TIME]\n");
+	fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			   struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+	if (interval)
+		addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+	if (tb[TCA_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+			      struct rtattr *xstats)
+{
+	struct tc_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	fprintf(f, "  count %u lastcount %u ldelay %s",
+		st->count, st->lastcount, sprint_time(st->ldelay, b1));
+	if (st->dropping)
+		fprintf(f, " dropping");
+	if (st->drop_next < 0)
+		fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+	else
+		fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+	fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+		st->maxpacket, st->ecn_mark, st->drop_overlimit);
+	return 0;
+
+}
+
+struct qdisc_util ns2_codel_qdisc_util = {
+	.id		= "ns2_codel",
+	.parse_qopt	= codel_parse_opt,
+	.print_qopt	= codel_print_opt,
+	.print_xstats	= codel_print_xstats,
+};
diff --git a/tc/q_ns4_codel.c b/tc/q_ns4_codel.c
new file mode 100644
index 0000000..0aaa349
--- /dev/null
+++ b/tc/q_ns4_codel.c
@@ -0,0 +1,188 @@
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ *  Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ *  Copyright (C) 2011-2012 Van Jacobson <van@pollere.com>
+ *  Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... ns4_codel [ limit PACKETS ] [ target TIME]\n");
+	fprintf(stderr, "                 [ interval TIME ] [ ecn | noecn ]\n");
+}
+
+static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			   struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_CODEL_LIMIT, &limit, sizeof(limit));
+	if (interval)
+		addattr_l(n, 1024, TCA_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_CODEL_MAX, opt);
+
+	if (tb[TCA_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int codel_print_xstats(struct qdisc_util *qu, FILE *f,
+			      struct rtattr *xstats)
+{
+	struct tc_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	fprintf(f, "  count %u lastcount %u ldelay %s",
+		st->count, st->lastcount, sprint_time(st->ldelay, b1));
+	if (st->dropping)
+		fprintf(f, " dropping");
+	if (st->drop_next < 0)
+		fprintf(f, " drop_next -%s", sprint_time(-st->drop_next, b1));
+	else
+		fprintf(f, " drop_next %s", sprint_time(st->drop_next, b1));
+	fprintf(f, "\n  maxpacket %u ecn_mark %u drop_overlimit %u",
+		st->maxpacket, st->ecn_mark, st->drop_overlimit);
+	return 0;
+
+}
+
+struct qdisc_util ns4_codel_qdisc_util = {
+	.id		= "ns4_codel",
+	.parse_qopt	= codel_parse_opt,
+	.print_qopt	= codel_print_opt,
+	.print_xstats	= codel_print_xstats,
+};
diff --git a/tc/q_pfq_codel.c b/tc/q_pfq_codel.c
new file mode 100644
index 0000000..52c5160
--- /dev/null
+++ b/tc/q_pfq_codel.c
@@ -0,0 +1,232 @@
+/*
+ * Fair Queue Codel
+ *
+ *  Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... pfq_codel [ limit PACKETS ] [ flows NUMBER ]\n");
+	fprintf(stderr, "                    [ target TIME] [ interval TIME ]\n");
+	fprintf(stderr, "                    [ quantum BYTES ] [ [no]ecn ]\n");
+}
+
+static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	unsigned limit = 0;
+	unsigned flows = 0;
+	unsigned target = 0;
+	unsigned interval = 0;
+	unsigned quantum = 0;
+	int ecn = -1;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&limit, *argv, 0)) {
+				fprintf(stderr, "Illegal \"limit\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "flows") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"flows\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "quantum") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&quantum, *argv, 0)) {
+				fprintf(stderr, "Illegal \"quantum\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "target") == 0) {
+			NEXT_ARG();
+			if (get_time(&target, *argv)) {
+				fprintf(stderr, "Illegal \"target\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "interval") == 0) {
+			NEXT_ARG();
+			if (get_time(&interval, *argv)) {
+				fprintf(stderr, "Illegal \"interval\"\n");
+				return -1;
+			}
+		} else if (strcmp(*argv, "ecn") == 0) {
+			ecn = 1;
+		} else if (strcmp(*argv, "noecn") == 0) {
+			ecn = 0;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+	if (limit)
+		addattr_l(n, 1024, TCA_FQ_CODEL_LIMIT, &limit, sizeof(limit));
+	if (flows)
+		addattr_l(n, 1024, TCA_FQ_CODEL_FLOWS, &flows, sizeof(flows));
+	if (quantum)
+		addattr_l(n, 1024, TCA_FQ_CODEL_QUANTUM, &quantum, sizeof(quantum));
+	if (interval)
+		addattr_l(n, 1024, TCA_FQ_CODEL_INTERVAL, &interval, sizeof(interval));
+	if (target)
+		addattr_l(n, 1024, TCA_FQ_CODEL_TARGET, &target, sizeof(target));
+	if (ecn != -1)
+		addattr_l(n, 1024, TCA_FQ_CODEL_ECN, &ecn, sizeof(ecn));
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	return 0;
+}
+
+static int fq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_FQ_CODEL_MAX + 1];
+	unsigned limit;
+	unsigned flows;
+	unsigned interval;
+	unsigned target;
+	unsigned ecn;
+	unsigned quantum;
+	SPRINT_BUF(b1);
+
+	if (opt == NULL)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_FQ_CODEL_MAX, opt);
+
+	if (tb[TCA_FQ_CODEL_LIMIT] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_LIMIT]) >= sizeof(__u32)) {
+		limit = rta_getattr_u32(tb[TCA_FQ_CODEL_LIMIT]);
+		fprintf(f, "limit %up ", limit);
+	}
+	if (tb[TCA_FQ_CODEL_FLOWS] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_FLOWS]) >= sizeof(__u32)) {
+		flows = rta_getattr_u32(tb[TCA_FQ_CODEL_FLOWS]);
+		fprintf(f, "flows %u ", flows);
+	}
+	if (tb[TCA_FQ_CODEL_QUANTUM] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_QUANTUM]) >= sizeof(__u32)) {
+		quantum = rta_getattr_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+		fprintf(f, "quantum %u ", quantum);
+	}
+	if (tb[TCA_FQ_CODEL_TARGET] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_TARGET]) >= sizeof(__u32)) {
+		target = rta_getattr_u32(tb[TCA_FQ_CODEL_TARGET]);
+		fprintf(f, "target %s ", sprint_time(target, b1));
+	}
+	if (tb[TCA_FQ_CODEL_INTERVAL] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
+		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+		fprintf(f, "interval %s ", sprint_time(interval, b1));
+	}
+	if (tb[TCA_FQ_CODEL_ECN] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_ECN]) >= sizeof(__u32)) {
+		ecn = rta_getattr_u32(tb[TCA_FQ_CODEL_ECN]);
+		if (ecn)
+			fprintf(f, "ecn ");
+	}
+
+	return 0;
+}
+
+static int fq_codel_print_xstats(struct qdisc_util *qu, FILE *f,
+				 struct rtattr *xstats)
+{
+	struct tc_fq_codel_xstats *st;
+	SPRINT_BUF(b1);
+
+	if (xstats == NULL)
+		return 0;
+
+	if (RTA_PAYLOAD(xstats) < sizeof(*st))
+		return -1;
+
+	st = RTA_DATA(xstats);
+	if (st->type == TCA_FQ_CODEL_XSTATS_QDISC) {
+		fprintf(f, "  maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u",
+			st->qdisc_stats.maxpacket,
+			st->qdisc_stats.drop_overlimit,
+			st->qdisc_stats.new_flow_count,
+			st->qdisc_stats.ecn_mark);
+		fprintf(f, "\n  new_flows_len %u old_flows_len %u",
+			st->qdisc_stats.new_flows_len,
+			st->qdisc_stats.old_flows_len);
+	}
+	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
+		fprintf(f, "  deficit %d count %u lastcount %u ldelay %s",
+			st->class_stats.deficit,
+			st->class_stats.count,
+			st->class_stats.lastcount,
+			sprint_time(st->class_stats.ldelay, b1));
+		if (st->class_stats.dropping) {
+			fprintf(f, " dropping");
+			if (st->class_stats.drop_next < 0)
+				fprintf(f, " drop_next -%s",
+					sprint_time(-st->class_stats.drop_next, b1));
+			else
+				fprintf(f, " drop_next %s",
+					sprint_time(st->class_stats.drop_next, b1));
+		}
+	}
+	return 0;
+
+}
+
+struct qdisc_util pfq_codel_qdisc_util = {
+	.id		= "pfq_codel",
+	.parse_qopt	= fq_codel_parse_opt,
+	.print_qopt	= fq_codel_print_opt,
+	.print_xstats	= fq_codel_print_xstats,
+};


141-cake-disable-sfq-codel.patch


diff --git a/tc/Makefile b/tc/Makefile
index e503c8a..3dce533 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -61,7 +61,7 @@ TCMODULES += q_codel.o
 TCMODULES += q_fq_codel.o
 TCMODULES += q_nfq_codel.o
 TCMODULES += q_efq_codel.o
-TCMODULES += q_sfq_codel.o
+#TCMODULES += q_sfq_codel.o
 TCMODULES += q_pfq_codel.o
 TCMODULES += q_ns2_codel.o
 TCMODULES += q_ns4_codel.o






[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4791 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2015-05-01 11:07 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-28 18:04 [Cerowrt-devel] documentation review request and out of tree cake builds for openwrt/etc Dave Taht
2015-04-28 20:52 ` [Cerowrt-devel] [Cake] " Jonathan Morton
2015-04-30  0:10   ` Dave Taht
2015-04-30 14:38     ` Jonathan Morton
2015-04-30 15:17       ` Dave Taht
2015-05-01  9:50         ` Kevin Darbyshire-Bryant
2015-05-01 11:06         ` Kevin Darbyshire-Bryant
2015-04-30 14:52     ` Jonathan Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox