diff --git a/include/Makefile.am b/include/Makefile.am
index 8df81eb..c5e0a90 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -54,6 +54,7 @@ nobase_libnlinclude_HEADERS = \
 	netlink/route/qdisc/red.h \
 	netlink/route/qdisc/sfq.h \
 	netlink/route/qdisc/tbf.h \
+	netlink/route/qdisc/bf.h \
 	netlink/route/qdisc/hfsc.h \
 	netlink/route/qdisc/codel.h \
 	netlink/route/qdisc/fq_codel.h \
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index a38e1b9..fdc441a 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -701,4 +701,59 @@ struct tc_fq_codel_xstats {
 	       struct tc_fq_codel_cl_stats class_stats;
         };
 };
+
+
+/* BF section */
+
+enum {
+	TCA_BF_UNSPEC = 0,
+	TCA_BF_PARAMS,
+	TCA_BF_INIT,
+	__TCA_BF_MAX,
+};
+
+enum TC_BF_STRATA {
+	TC_BF_STRATUM_RT = 0,
+	TC_BF_STRATUM_NOMINAL,
+	TC_BF_STRATUM_OPTIMAL,
+	TC_BF_STRATUM_BULK,
+	__TC_BF_STRATA_COUNT,
+};
+#define TC_BF_STRATUM_NONE		TC_BF_STRATUM_BULK
+
+#define TC_BF_MAX_PRIORITY_POW2	8
+
+enum BF_PRIORITY_CALC {
+        BF_PRIORITY_CALC_DEFAULT = 0,
+        BF_PRIORITY_CALC_FLOW_NODE,
+        BF_PRIORITY_CALC_NODE_FLOW,
+        BF_PRIORITY_CALC_FLOW_ONLY,
+        BF_PRIORITY_CALC_NODE_ONLY,
+        __BF_PRIORITY_CALC_COUNT,
+};
+
+struct tc_bf_glob {
+	__u32 defcls;		/* default class number */
+	__u32 flow_priorities_pow2;	/* Range of flow priorities log2 */
+	__u32 node_priorities_pow2;	/* Range of node priorities log2 */
+	enum BF_PRIORITY_CALC calc;
+	__u32 total_bw;
+	__u32 direct_pkts;
+};
+
+
+struct tc_bf_opt {
+	__u32 flow_priority;
+	__u32 node_priority;
+	__u32 bytes_per_sec_limits[__TC_BF_STRATA_COUNT];
+};
+
+struct tc_bf_xstats {
+	enum TC_BF_STRATA oversub_strata;
+};
+
+
+#define TCA_BF_MAX      (__TCA_BF_MAX - 1)
+
+
 #endif
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index de9382d..fe97fa4 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -616,6 +616,25 @@ struct rtnl_fq_codel_qdisc
 	uint32_t        qcq_mask;
 };
 
+struct rtnl_bf_qdisc
+{
+	uint32_t		qb_defcls;
+	uint32_t		qb_flow_priorities_pow2;
+	uint32_t		qb_node_priorities_pow2;
+	enum BF_PRIORITY_CALC	qb_calc_method;
+	uint32_t		qb_total_bw;
+	uint32_t		qb_direct_pkts;
+	uint32_t		qb_mask;
+};
+
+struct rtnl_bf_class
+{
+	uint32_t	cb_flow_prio;
+	uint32_t	cb_node_prio;
+	uint32_t	cb_limits[__TC_BF_STRATA_COUNT];
+	uint32_t	cb_mask;
+};
+
 struct rtnl_sfq
 {
 	uint32_t	qs_quantum;
diff --git a/include/netlink/route/qdisc/bf.h b/include/netlink/route/qdisc/bf.h
new file mode 100644
index 0000000..c5558ef
--- /dev/null
+++ b/include/netlink/route/qdisc/bf.h
@@ -0,0 +1,53 @@
+/*
+ * netlink/route/sch/bf.h	BF Qdisc
+ */
+/*
+ **************************************************************************
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#ifndef NETLINK_BF_H_
+#define NETLINK_BF_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/tc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint32_t	rtnl_bf_get_defcls(struct rtnl_qdisc *);
+extern int	rtnl_bf_set_defcls(struct rtnl_qdisc *, uint32_t);
+extern uint32_t rtnl_bf_get_flow_priorities(struct rtnl_qdisc *);
+extern uint32_t rtnl_bf_get_node_priorities(struct rtnl_qdisc *);
+extern int rtnl_bf_set_priorities(struct rtnl_qdisc *, uint32_t flow_prios,
+				  uint32_t node_prios);
+extern enum BF_PRIORITY_CALC rtnl_bf_get_prio_calc_method(struct rtnl_qdisc *);
+extern int rtnl_bf_set_prio_calc_method(struct rtnl_qdisc *,
+					enum BF_PRIORITY_CALC);
+extern uint32_t rtnl_bf_get_total_bandwidth(struct rtnl_qdisc *);
+extern int rtnl_bf_set_total_bandwidth(struct rtnl_qdisc *, uint32_t);
+
+extern uint32_t	rtnl_bf_get_flow_prio(struct rtnl_class *);
+extern int	rtnl_bf_set_flow_prio(struct rtnl_class *, uint32_t);
+extern uint32_t	rtnl_bf_get_node_prio(struct rtnl_class *);
+extern int	rtnl_bf_set_node_prio(struct rtnl_class *, uint32_t);
+extern uint32_t	rtnl_bf_get_rate(struct rtnl_class *, uint32_t);
+extern int	rtnl_bf_set_rates(struct rtnl_class *, uint32_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a1b5ddb..c6f5639 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -80,6 +80,7 @@ libnl_route_3_la_SOURCES = \
 	route/qdisc/hfsc.c \
 	route/qdisc/drr.c \
 	route/qdisc/sfb.c \
+	route/qdisc/bf.c \
 	\
 	fib_lookup/lookup.c fib_lookup/request.c \
 	\
@@ -106,6 +107,7 @@ EXTRA_DIST = \
 if ENABLE_CLI
 nobase_pkglib_LTLIBRARIES = \
 	cli/qdisc/htb.la \
+	cli/qdisc/bf.la \
 	cli/qdisc/hfsc.la \
 	cli/qdisc/blackhole.la \
 	cli/qdisc/pfifo.la \
@@ -115,6 +117,7 @@ nobase_pkglib_LTLIBRARIES = \
 	cli/cls/cgroup.la
 
 cli_qdisc_htb_la_LDFLAGS = -module -avoid-version
+cli_qdisc_bf_la_LDFLAGS = -module -avoid-version
 cli_qdisc_hfsc_la_LDFLAGS = -module -avoid-version
 cli_qdisc_blackhole_la_LDFLAGS = -module -avoid-version
 cli_qdisc_pfifo_la_LDFLAGS = -module -avoid-version
diff --git a/lib/cli/qdisc/bf.c b/lib/cli/qdisc/bf.c
new file mode 100644
index 0000000..8b83b07
--- /dev/null
+++ b/lib/cli/qdisc/bf.c
@@ -0,0 +1,218 @@
+/*
+ * lib/cli/qdisc/bf.c     	Bigfoot module for CLI lib
+ */
+/*
+ **************************************************************************
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/bf.h>
+
+static void print_qdisc_usage(void)
+{
+	printf(
+"Usage: nl-qdisc-add [...] bf [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+"     --help                Show this help text.\n"
+"     --default=ID          Default class for unclassified traffic.\n"
+"     --flow-prios=FPRIOS   Range from 1 to NPRIOS available for flows.\n"
+"     --node-prios=NPRIOS   Range from 1 to FPRIOS available for nodes.\n"
+"     --calc-method=METHOD  ID of calc method for combining node/flow prios.\n"
+"     --total-bw=BYTES/SEC  Total available bandwidth in bytes per second.\n"
+"\n"
+"CALC METHOD\n"
+"   The available methods are:\n"
+"     0 Default             This is the FLOW/NODE method\n"
+"     1 Flow/Node	    Strict priority with flow priority precedence.\n"
+"     2 Node/Flow	    Strict priority with node priority precedence.\n"
+"     3 Flow Only	    Strict priority, flow only, node not considered.\n"
+"     4 Node Only	    Strict priority, node only, flow not considered.\n"
+"NOTE\n"
+"   Due to internal limitations, 0 is not a valid value for either FPRIOS or\n"
+"   NPRIOS. "
+"EXAMPLE\n"
+"    # Create bf root qdisc 1: and direct unclassified traffic to class 1:10\n"
+"    nl-qdisc-add --dev=eth1 --parent=root --handle=1: bf --default=10 --flow-prios=4 --node-prios=16 --calc-method=0 --total-bw=1000000\n");
+}
+
+static void bf_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+	unsigned int flow_prios = 0;
+	unsigned int node_prios = 0;
+	long rate;
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_DEFAULT = 257,
+			ARG_FLOW_PRIOS = 258,
+			ARG_NODE_PRIOS = 259,
+			ARG_CALC_METHOD = 260,
+			ARG_TOTAL_BW = 261,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "default", 1, 0, ARG_DEFAULT },
+			{ "flow-prios", 1, 0, ARG_FLOW_PRIOS },
+			{ "node-prios", 1, 0, ARG_NODE_PRIOS },
+			{ "calc-method", 1, 0, ARG_CALC_METHOD },
+			{ "total-bw", 1, 0, ARG_TOTAL_BW },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "hv", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_qdisc_usage();
+			return;
+		case ARG_DEFAULT:
+			rtnl_bf_set_defcls(qdisc, nl_cli_parse_u32(optarg));
+			break;
+		case ARG_FLOW_PRIOS:
+			flow_prios = nl_cli_parse_u32(optarg);
+			break;
+		case ARG_NODE_PRIOS:
+			node_prios = nl_cli_parse_u32(optarg);
+			break;
+		case ARG_CALC_METHOD:
+			rtnl_bf_set_prio_calc_method(qdisc,
+						     nl_cli_parse_u32(optarg));
+			break;
+		case ARG_TOTAL_BW:
+			rate = nl_size2int(optarg);
+			if (rate < 0) {
+				nl_cli_fatal(rate, "Unable to parse total-bw "
+					     "\"%s\": Invalid format.", optarg);
+			}
+			rtnl_bf_set_total_bandwidth(qdisc, rate);
+			break;
+
+		}
+	}
+
+	if ((flow_prios != 0) || (node_prios != 0))
+		rtnl_bf_set_priorities(qdisc, flow_prios, node_prios);
+}
+
+static void print_class_usage(void)
+{
+	printf(
+"Usage: nl-class-add [...] bf [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+"     --help                Show this help text.\n"
+"     --realtime=RATE	    Realtime rate limit (default: 0).\n"
+"     --nominal=RATE        Nominal rate limit (default: rt rate).\n"
+"     --optimal=RATE        Optimal rate limit (default: nom rate).\n"
+"     --flow-prio=FPRIO     App Priority, lower is served first (default: 1).\n"
+"     --node-prio=NPRIO     Device Priority, lower is first (default: 1).\n"
+"     --total-bw=BW         Total BW available for the interface (default: 0)\n"
+"\n"
+"EXAMPLE"
+"    # Attach class 1:1 to bf qdisc 1: and nominal rate limit it to 20mbit\n"
+"    nl-class-add --dev=eth1 --parent=1: --classid=1:1 bf --nom=20mbit\n");
+}
+
+static void bf_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+	struct rtnl_class *cls = (struct rtnl_class *) tc;
+	unsigned int rates[__TC_BF_STRATA_COUNT] = {0};
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_RT = 257,
+			ARG_NOM = 258,
+			ARG_OPT = 259,
+			ARG_FPRIO = 260,
+			ARG_NPRIO = 261,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "realtime", 1, 0, ARG_RT },
+			{ "nominal", 1, 0, ARG_NOM },
+			{ "optimal", 1, 0, ARG_OPT },
+			{ "flow-prio", 1, 0, ARG_FPRIO },
+			{ "node-prio", 1, 0, ARG_NPRIO },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "h", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_class_usage();
+			return;
+		case ARG_RT:
+			rates[TC_BF_STRATUM_RT] = nl_cli_parse_u32(optarg);
+			break;
+		case ARG_NOM:
+			rates[TC_BF_STRATUM_NOMINAL] = nl_cli_parse_u32(optarg);
+			break;
+		case ARG_OPT:
+			rates[TC_BF_STRATUM_OPTIMAL] = nl_cli_parse_u32(optarg);
+			break;
+		case ARG_FPRIO:
+			rtnl_bf_set_flow_prio(cls, nl_cli_parse_u32(optarg));
+			break;
+		case ARG_NPRIO:
+			rtnl_bf_set_node_prio(cls, nl_cli_parse_u32(optarg));
+			break;
+
+		}
+	}
+
+	if (rates[TC_BF_STRATUM_NOMINAL] == 0)
+		rates[TC_BF_STRATUM_NOMINAL] = rates[TC_BF_STRATUM_RT];
+
+	if (rates[TC_BF_STRATUM_OPTIMAL] == 0)
+		rates[TC_BF_STRATUM_OPTIMAL] = rates[TC_BF_STRATUM_NOMINAL];
+
+	rtnl_bf_set_rates(cls, rates);
+}
+
+static struct nl_cli_tc_module bf_qdisc_module =
+{
+	.tm_name		= "bf",
+	.tm_type		= RTNL_TC_TYPE_QDISC,
+	.tm_parse_argv		= bf_parse_qdisc_argv,
+};
+
+static struct nl_cli_tc_module bf_class_module =
+{
+	.tm_name		= "bf",
+	.tm_type		= RTNL_TC_TYPE_CLASS,
+	.tm_parse_argv		= bf_parse_class_argv,
+};
+
+static void __init bf_init(void)
+{
+	nl_cli_tc_register(&bf_qdisc_module);
+	nl_cli_tc_register(&bf_class_module);
+}
+
+static void __exit bf_exit(void)
+{
+	nl_cli_tc_unregister(&bf_class_module);
+	nl_cli_tc_unregister(&bf_qdisc_module);
+}
diff --git a/lib/route/qdisc/bf.c b/lib/route/qdisc/bf.c
new file mode 100644
index 0000000..845ec88
--- /dev/null
+++ b/lib/route/qdisc/bf.c
@@ -0,0 +1,543 @@
+/*
+ * lib/route/qdisc/bf.c		BF Qdisc
+ */
+/*
+ **************************************************************************
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/**
+ * @ingroup qdisc
+ * @ingroup class
+ * @defgroup qdisc_bf Bigfoot Scheduler (BF)
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+#include <netlink/route/link.h>
+#include <netlink/route/qdisc/bf.h>
+
+/** @cond SKIP */
+#define SCH_BF_HAS_DEFCLS		0x001
+#define SCH_BF_HAS_FLOW_PRIO_LIMIT	0x002
+#define SCH_BF_HAS_NODE_PRIO_LIMIT	0x004
+#define SCH_BF_HAS_PRIO_CALC_METHOD	0x008
+#define SCH_BF_HAS_TOTAL_BW		0x010
+#define SCH_BF_HAS_FLOW_PRIO		0x020
+#define SCH_BF_HAS_NODE_PRIO		0x040
+#define SCH_BF_HAS_RATES		0x080
+/** @endcond */
+
+static char *strata_names[__TC_BF_STRATA_COUNT] = {
+	"realtime", "nominal", "optimal", "bulk",
+};
+
+static char *prio_calc_method_names[__BF_PRIORITY_CALC_COUNT] = {
+	"default", "flow-node", "node-flow", "flow-only", "node-only",
+};
+
+static struct nla_policy bf_policy[TCA_BF_MAX+1] = {
+	[TCA_BF_INIT]	= { .minlen = sizeof(struct tc_bf_glob) },
+	[TCA_BF_PARAMS]	= { .minlen = sizeof(struct tc_bf_opt) },
+};
+
+static int bf_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct nlattr *tb[TCA_BF_MAX + 1];
+	struct rtnl_bf_qdisc *bf = data;
+	int err;
+
+	if ((err = tca_parse(tb, TCA_BF_MAX, tc, bf_policy)) < 0)
+		return err;
+
+	if (tb[TCA_BF_INIT]) {
+		struct tc_bf_glob opts;
+
+		bf->qb_mask = 0;
+		nla_memcpy(&opts, tb[TCA_BF_INIT], sizeof(opts));
+
+		bf->qb_defcls = opts.defcls;
+		bf->qb_mask |= SCH_BF_HAS_DEFCLS;
+
+		bf->qb_flow_priorities_pow2	= opts.flow_priorities_pow2;
+		bf->qb_mask |= SCH_BF_HAS_FLOW_PRIO_LIMIT;
+
+		bf->qb_node_priorities_pow2	= opts.node_priorities_pow2;
+		bf->qb_mask |= SCH_BF_HAS_NODE_PRIO_LIMIT;
+
+		bf->qb_calc_method		= opts.calc;
+		bf->qb_mask |= SCH_BF_HAS_PRIO_CALC_METHOD;
+
+		bf->qb_total_bw			= opts.total_bw;
+		bf->qb_mask |= SCH_BF_HAS_TOTAL_BW;
+	}
+
+	return 0;
+}
+
+static int bf_class_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	int err;
+	struct nlattr *tb[TCA_BF_MAX + 1];
+	struct rtnl_bf_class *bf = data;
+
+	if ((err = tca_parse(tb, TCA_BF_MAX, tc, bf_policy)) < 0)
+		return err;
+
+	if (tb[TCA_BF_PARAMS]) {
+		unsigned int i;
+		struct tc_bf_opt opts;
+
+		bf->cb_mask = 0;
+		nla_memcpy(&opts, tb[TCA_BF_PARAMS], sizeof(opts));
+		bf->cb_flow_prio = opts.flow_priority;
+		bf->cb_mask |= SCH_BF_HAS_FLOW_PRIO;
+
+		bf->cb_node_prio = opts.node_priority;
+		bf->cb_mask |= SCH_BF_HAS_NODE_PRIO;
+
+		for (i = 0; i < __TC_BF_STRATA_COUNT; i++) {
+			bf->cb_limits[i] = opts.bytes_per_sec_limits[i];
+		}
+		bf->cb_mask |= SCH_BF_HAS_RATES;
+	}
+
+	return 0;
+}
+
+static void bf_qdisc_dump_line(struct rtnl_tc *tc, void *data,
+			       struct nl_dump_params *p)
+{
+	struct rtnl_bf_qdisc *bf = data;
+
+	if (!bf)
+		return;
+
+	if (bf->qb_mask & SCH_BF_HAS_DEFCLS) {
+		char buf[64];
+		nl_dump(p, " default-class %s",
+			rtnl_tc_handle2str(bf->qb_defcls,
+			buf, sizeof(buf)));
+	}
+
+	if (bf->qb_mask & SCH_BF_HAS_FLOW_PRIO_LIMIT) {
+		nl_dump(p, " flow-priorities %u",
+			(1 << bf->qb_flow_priorities_pow2));
+	}
+
+	if (bf->qb_mask & SCH_BF_HAS_NODE_PRIO_LIMIT) {
+		nl_dump(p, " node-priorities %u",
+			(1 << bf->qb_node_priorities_pow2));
+	}
+
+	if (bf->qb_mask & SCH_BF_HAS_PRIO_CALC_METHOD) {
+		nl_dump(p, " priority-calculation %s",
+			prio_calc_method_names[bf->qb_calc_method]);
+	}
+
+	if (bf->qb_mask & SCH_BF_HAS_TOTAL_BW) {
+		nl_dump(p, " total-bw %u", bf->qb_total_bw);
+	}
+}
+
+static void bf_class_dump_line(struct rtnl_tc *tc, void *data,
+			       struct nl_dump_params *p)
+{
+	struct rtnl_bf_class *bf = data;
+
+	if (!bf)
+		return;
+
+	if (bf->cb_mask & SCH_BF_HAS_FLOW_PRIO) {
+		nl_dump(p, " flow_prio %u", bf->cb_node_prio);
+	}
+
+	if (bf->cb_mask & SCH_BF_HAS_NODE_PRIO) {
+		nl_dump(p, " node_prio %u", bf->cb_node_prio);
+	}
+
+	if (bf->cb_mask & SCH_BF_HAS_RATES) {
+		unsigned int i;
+
+		for (i = 0; i < __TC_BF_STRATA_COUNT; i++) {
+			nl_dump(p, " %s_limit %u", strata_names[i],
+				bf->cb_limits[i]);
+		}
+	}
+}
+
+static void bf_class_dump_stats(struct rtnl_tc *tc, void *data,
+				struct nl_dump_params *p)
+{
+	enum TC_BF_STRATA stratum;
+	struct tc_bf_xstats *x;
+
+	if (!(x = tca_xstats(tc)))
+		return;
+
+	stratum = x->oversub_strata;
+	if (stratum == TC_BF_STRATUM_BULK) {
+		nl_dump(p, " No strata");
+	} else {
+		while(stratum < TC_BF_STRATUM_BULK) {
+			nl_dump(p, " %s", strata_names[stratum]);
+			stratum++;
+		}
+	}
+	nl_dump_line(p, " limited (oversubscription)\n");
+}
+
+static int bf_qdisc_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	struct rtnl_bf_qdisc *bf = data;
+	struct tc_bf_glob opts = {0};
+
+	if (bf) {
+		if (bf->qb_mask & SCH_BF_HAS_DEFCLS)
+			opts.defcls = bf->qb_defcls;
+
+		if (bf->qb_mask & SCH_BF_HAS_FLOW_PRIO_LIMIT)
+			opts.flow_priorities_pow2 = bf->qb_flow_priorities_pow2;
+
+		if (bf->qb_mask & SCH_BF_HAS_NODE_PRIO_LIMIT)
+			opts.node_priorities_pow2 = bf->qb_node_priorities_pow2;
+
+		if (bf->qb_mask & SCH_BF_HAS_PRIO_CALC_METHOD)
+			opts.calc = bf->qb_calc_method;
+
+		if (bf->qb_mask & SCH_BF_HAS_TOTAL_BW)
+			opts.total_bw = bf->qb_total_bw;
+	}
+
+	return nla_put(msg, TCA_BF_INIT, sizeof(opts), &opts);
+}
+
+
+static int bf_class_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	unsigned int i;
+	struct rtnl_bf_class *bf = data;
+	struct tc_bf_opt opts = {0};
+
+	if ((!bf) || !(bf->cb_mask & (SCH_BF_HAS_RATES)))
+		BUG();
+
+	if (bf->cb_mask & SCH_BF_HAS_FLOW_PRIO) {
+		opts.flow_priority = bf->cb_flow_prio;
+	}
+
+	if (bf->cb_mask & SCH_BF_HAS_NODE_PRIO) {
+		opts.node_priority = bf->cb_node_prio;
+	}
+
+	for (i = 0; i < __TC_BF_STRATA_COUNT; i++) {
+		opts.bytes_per_sec_limits[i] = bf->cb_limits[i];
+	}
+
+	NLA_PUT(msg, TCA_BF_PARAMS, sizeof(opts), &opts);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static struct rtnl_tc_ops bf_qdisc_ops;
+static struct rtnl_tc_ops bf_class_ops;
+
+static struct rtnl_bf_qdisc *bf_qdisc_data(struct rtnl_qdisc *qdisc)
+{
+	return rtnl_tc_data_check(TC_CAST(qdisc), &bf_qdisc_ops);
+}
+
+static struct rtnl_bf_class *bf_class_data(struct rtnl_class *class)
+{
+	return rtnl_tc_data_check(TC_CAST(class), &bf_class_ops);
+}
+
+/*
+ * Calculate the next highest power of 2.  0 is an illegal value to pass to
+ * this function.
+ */
+static unsigned int next_pow2(unsigned int value)
+{
+        unsigned int retval;
+        unsigned int bits;
+
+        if (value == 0)
+                return 0;
+
+        bits = 0;
+        retval = 0;
+        do {
+                retval++;
+                bits += value & 1;
+                value >>= 1;
+        } while (value != 0);
+
+        return (bits > 1) ? retval : retval - 1;
+}
+
+
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+uint32_t rtnl_bf_get_defcls(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if ((bf = bf_qdisc_data(qdisc)) && bf->qb_mask & SCH_BF_HAS_DEFCLS)
+		return bf->qb_defcls;
+
+	return TC_H_UNSPEC;
+}
+
+
+int rtnl_bf_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if (!(bf = bf_qdisc_data(qdisc)))
+		return -NLE_OPNOTSUPP;
+
+	bf->qb_defcls = defcls;
+	bf->qb_mask |= SCH_BF_HAS_DEFCLS;
+
+	return 0;
+}
+
+uint32_t rtnl_bf_get_flow_priorities(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if ((bf = bf_qdisc_data(qdisc)) &&
+	    (bf->qb_mask & SCH_BF_HAS_FLOW_PRIO_LIMIT))
+		return (1 << bf->qb_flow_priorities_pow2);
+
+	return TC_H_UNSPEC;
+}
+
+uint32_t rtnl_bf_get_node_priorities(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if ((bf = bf_qdisc_data(qdisc)) &&
+	    (bf->qb_mask & SCH_BF_HAS_NODE_PRIO_LIMIT))
+		return (1 << bf->qb_node_priorities_pow2);
+
+	return TC_H_UNSPEC;
+}
+
+int rtnl_bf_set_priorities(struct rtnl_qdisc *qdisc, uint32_t flow_prios,
+			   uint32_t node_prios)
+{
+	unsigned int flow_pow2, node_pow2;
+	struct rtnl_bf_qdisc *bf;
+
+	if (!(bf = bf_qdisc_data(qdisc)))
+		return -NLE_OPNOTSUPP;
+
+	if ((flow_prios == 0) || (node_prios == 0))
+		return -NLE_RANGE;
+
+	flow_pow2 = next_pow2(flow_prios);
+	node_pow2 = next_pow2(node_prios);
+
+	if ((flow_pow2 + node_pow2) > TC_BF_MAX_PRIORITY_POW2)
+		return -NLE_RANGE;
+
+	bf->qb_node_priorities_pow2 = node_pow2;
+	bf->qb_flow_priorities_pow2 = flow_pow2;
+
+	bf->qb_mask |= SCH_BF_HAS_NODE_PRIO_LIMIT;
+	bf->qb_mask |= SCH_BF_HAS_FLOW_PRIO_LIMIT;
+
+	return 0;
+}
+
+enum BF_PRIORITY_CALC rtnl_bf_get_prio_calc_method(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if ((bf = bf_qdisc_data(qdisc)) &&
+            (bf->qb_mask & SCH_BF_HAS_PRIO_CALC_METHOD))
+			return bf->qb_calc_method;
+
+	return TC_H_UNSPEC;
+}
+
+int rtnl_bf_set_prio_calc_method(struct rtnl_qdisc *qdisc,
+				 enum BF_PRIORITY_CALC calc)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if (!(bf = bf_qdisc_data(qdisc)))
+		return -NLE_OPNOTSUPP;
+
+	bf->qb_calc_method = calc;
+	bf->qb_mask |= SCH_BF_HAS_PRIO_CALC_METHOD;
+
+	return 0;
+}
+
+uint32_t rtnl_bf_get_total_bandwidth(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if ((bf = bf_qdisc_data(qdisc)) &&
+	    (bf->qb_mask & SCH_BF_HAS_TOTAL_BW))
+		return bf->qb_total_bw;
+
+	return TC_H_UNSPEC;
+}
+
+int rtnl_bf_set_total_bandwidth(struct rtnl_qdisc *qdisc, uint32_t total_bw)
+{
+	struct rtnl_bf_qdisc *bf;
+
+	if (!(bf = bf_qdisc_data(qdisc)))
+		return -NLE_OPNOTSUPP;
+
+	bf->qb_total_bw = total_bw;
+	bf->qb_mask |= SCH_BF_HAS_TOTAL_BW;
+
+	return 0;
+}
+
+
+uint32_t rtnl_bf_get_flow_prio(struct rtnl_class *cls)
+{
+	struct rtnl_bf_class *bf;
+
+	if ((bf = bf_class_data(cls)) && bf->cb_mask & SCH_BF_HAS_FLOW_PRIO)
+		return bf->cb_flow_prio;
+
+	return 0;
+}
+
+int rtnl_bf_set_flow_prio(struct rtnl_class *class, uint32_t flow_prio)
+{
+	struct rtnl_bf_class *bf;
+
+	if (!(bf = bf_class_data(class)))
+		return -NLE_OPNOTSUPP;
+
+	bf->cb_flow_prio = flow_prio;
+	bf->cb_mask |= SCH_BF_HAS_FLOW_PRIO;
+
+	return 0;
+}
+
+uint32_t rtnl_bf_get_node_prio(struct rtnl_class *class)
+{
+	struct rtnl_bf_class *bf;
+
+	if ((bf = bf_class_data(class)) && bf->cb_mask & SCH_BF_HAS_NODE_PRIO)
+		return bf->cb_node_prio;
+
+	return 0;
+}
+
+int rtnl_bf_set_node_prio(struct rtnl_class *class, uint32_t node_prio)
+{
+	struct rtnl_bf_class *bf;
+
+	if (!(bf = bf_class_data(class)))
+		return -NLE_OPNOTSUPP;
+
+	bf->cb_node_prio = node_prio;
+	bf->cb_mask |= SCH_BF_HAS_NODE_PRIO;
+
+	return 0;
+}
+
+
+uint32_t rtnl_bf_get_rate(struct rtnl_class *class, uint32_t strata)
+{
+	struct rtnl_bf_class *bf;
+
+	if ((bf = bf_class_data(class)) && bf->cb_mask & SCH_BF_HAS_RATES)
+		return bf->cb_limits[strata];
+
+	return 0;
+}
+
+int rtnl_bf_set_rates(struct rtnl_class *class, uint32_t *rates)
+{
+	unsigned int i;
+	struct rtnl_bf_class *bf;
+
+	if (!(bf = bf_class_data(class)))
+		return -NLE_OPNOTSUPP;
+
+	if (!rates)
+		return -ENOENT;
+
+	for (i = 0; i < __TC_BF_STRATA_COUNT; i++) {
+		bf->cb_limits[i] = rates[i];
+	}
+	bf->cb_mask |= SCH_BF_HAS_RATES;
+
+	return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops bf_qdisc_ops = {
+	.to_kind		= "bf",
+	.to_type		= RTNL_TC_TYPE_QDISC,
+	.to_size		= sizeof(struct rtnl_bf_qdisc),
+	.to_msg_parser		= bf_qdisc_msg_parser,
+	.to_dump = {
+	    [NL_DUMP_LINE]	= bf_qdisc_dump_line,
+	    [NL_DUMP_DETAILS]	= NULL,
+	},
+	.to_msg_fill		= bf_qdisc_msg_fill,
+};
+
+static struct rtnl_tc_ops bf_class_ops = {
+	.to_kind		= "bf",
+	.to_type		= RTNL_TC_TYPE_CLASS,
+	.to_size		= sizeof(struct rtnl_bf_class),
+	.to_msg_parser		= bf_class_msg_parser,
+	.to_dump = {
+	    [NL_DUMP_LINE]	= bf_class_dump_line,
+	    [NL_DUMP_DETAILS]	= NULL,
+	    [NL_DUMP_STATS]	= bf_class_dump_stats,
+	},
+	.to_msg_fill		= bf_class_msg_fill,
+};
+
+static void __init bf_init(void)
+{
+	rtnl_tc_register(&bf_qdisc_ops);
+	rtnl_tc_register(&bf_class_ops);
+}
+
+static void __exit bf_exit(void)
+{
+	rtnl_tc_unregister(&bf_class_ops);
+	rtnl_tc_unregister(&bf_qdisc_ops);
+}
+
+/** @} */
