diff -Naur iproute2-2.6.24-rc7.orig/include/linux/pkt_sched.h iproute2-2.6.24-rc7/include/linux/pkt_sched.h
--- iproute2-2.6.24-rc7.orig/include/linux/pkt_sched.h	2008-01-08 08:59:32.000000000 -0800
+++ iproute2-2.6.24-rc7/include/linux/pkt_sched.h	2008-01-28 01:13:18.000000000 -0800
@@ -142,20 +142,43 @@
 struct tc_sfq_qopt
 {
 	unsigned	quantum;	/* Bytes per round allocated to flow */
-	int		perturb_period;	/* Period of hash perturbation */
+	unsigned	perturb_period;	/* Period of hash perturbation */
 	__u32		limit;		/* Maximal packets in queue */
 	unsigned	divisor;	/* Hash divisor  */
 	unsigned	flows;		/* Maximal number of flows  */
 };
 
-/*
- *  NOTE: limit, divisor and flows are hardwired to code at the moment.
- *
- *	limit=flows=128, divisor=1024;
- *
- *	The only reason for this is efficiency, it is possible
- *	to change these parameters in compile time.
- */
+enum
+{
+	TCA_SFQ_UNSPEC,
+	TCA_SFQ_COMPAT,
+	TCA_SFQ_QUANTUM,
+	TCA_SFQ_PERTURB,
+	TCA_SFQ_LIMIT,
+	TCA_SFQ_DIVISOR,
+	TCA_SFQ_FLOWS,
+	TCA_SFQ_HASH,
+	__TCA_SFQ_MAX,
+};
+
+#define TCA_SFQ_MAX (__TCA_SFQ_MAX - 1)
+
+enum
+{
+        TCA_SFQ_HASH_CLASSIC,
+        TCA_SFQ_HASH_DST,
+        TCA_SFQ_HASH_SRC,
+        TCA_SFQ_HASH_FWMARK,
+	/* conntrack */
+        TCA_SFQ_HASH_CTORIGDST,
+        TCA_SFQ_HASH_CTORIGSRC,
+        TCA_SFQ_HASH_CTREPLDST,
+        TCA_SFQ_HASH_CTREPLSRC,
+        TCA_SFQ_HASH_CTNATCHG,
+	__TCA_SFQ_HASH_MAX,
+};
+
+#define TCA_SFQ_HASH_MAX (__TCA_SFQ_HASH_MAX - 1)
 
 /* RED section */
 
diff -Naur iproute2-2.6.24-rc7.orig/tc/q_sfq.c iproute2-2.6.24-rc7/tc/q_sfq.c
--- iproute2-2.6.24-rc7.orig/tc/q_sfq.c	2008-01-08 08:59:32.000000000 -0800
+++ iproute2-2.6.24-rc7/tc/q_sfq.c	2008-01-28 01:13:18.000000000 -0800
@@ -25,7 +25,11 @@
 
 static void explain(void)
 {
-	fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n");
+	fprintf(stderr,
+	    "Usage: ... sfq [ limit NUMBER ] [ depth FLOWS ] [ divisor HASHBITS ] [ perturb SECS ] [ quantum BYTES ] [ hash HASHTYPE ]\n"
+	    "Where: \n"
+	    "HASHTYPE := { classic | src | dst | fwmark | ctorigdst | ctorigsrc | ctrepldst | ctreplsrc | ctnatchg}\n"
+	);
 }
 
 #define usage() return(-1)
@@ -34,9 +38,13 @@
 {
 	int ok=0;
 	struct tc_sfq_qopt opt;
+	struct rtattr *nest;
 
 	memset(&opt, 0, sizeof(opt));
 
+	/* put blank data in rtattr so there is a "hole" to fill later */
+	nest = addattr_nest_compat(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+
 	while (argc > 0) {
 		if (strcmp(*argv, "quantum") == 0) {
 			NEXT_ARG();
@@ -44,13 +52,15 @@
 				fprintf(stderr, "Illegal \"limit\"\n");
 				return -1;
 			}
+			addattr32(n, 1024, TCA_SFQ_QUANTUM, opt.quantum);
 			ok++;
 		} else if (strcmp(*argv, "perturb") == 0) {
 			NEXT_ARG();
-			if (get_integer(&opt.perturb_period, *argv, 0)) {
+			if (get_u32(&opt.perturb_period, *argv, 0)) {
 				fprintf(stderr, "Illegal \"perturb\"\n");
 				return -1;
 			}
+			addattr32(n, 1024, TCA_SFQ_PERTURB, opt.perturb_period);
 			ok++;
 		} else if (strcmp(*argv, "limit") == 0) {
 			NEXT_ARG();
@@ -62,6 +72,53 @@
 				fprintf(stderr, "Illegal \"limit\", must be > 1\n");
 				return -1;
 			}
+			addattr32(n, 1024, TCA_SFQ_LIMIT, opt.limit);
+			ok++;
+		} else if (strcmp(*argv, "depth") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"depth\"\n");
+				return -1;
+			}
+			addattr32(n, 1024, TCA_SFQ_FLOWS, opt.flows);
+			ok++;
+		} else if (strcmp(*argv, "divisor") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.divisor, *argv, 0)) {
+				fprintf(stderr, "Illegal \"divisor\"\n");
+				return -1;
+			}
+			if (opt.divisor >= 15) {
+				fprintf(stderr, "Illegal \"divisor\", must be < 15\n");
+				return -1;
+			}
+			addattr32(n, 1024, TCA_SFQ_DIVISOR, opt.divisor);
+			ok++;
+		} else if (strcmp(*argv, "hash") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "classic") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_CLASSIC);
+			} else if (strcmp(*argv, "dst") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_DST);
+			} else if (strcmp(*argv, "src") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_SRC);
+			} else if (strcmp(*argv, "fwmark") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_FWMARK);
+			} else if (strcmp(*argv, "ctorigsrc") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_CTORIGSRC);
+			} else if (strcmp(*argv, "ctorigdst") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_CTORIGDST);
+			} else if (strcmp(*argv, "ctreplsrc") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_CTREPLSRC);
+			} else if (strcmp(*argv, "ctrepldst") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_CTREPLDST);
+			} else if (strcmp(*argv, "ctnatchg") == 0) {
+				addattr32(n, 1024, TCA_SFQ_HASH, TCA_SFQ_HASH_CTNATCHG);
+			} else {
+				fprintf(stderr, "Illegal \"hash\"\n");
+				explain();
+				return -1;
+			}
 			ok++;
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
@@ -74,29 +131,79 @@
 		argc--; argv++;
 	}
 
-	if (ok)
-		addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+	if (ok) {
+		/* fill the "hole" we left earlier with real compat data */
+		memcpy(RTA_DATA(nest), &opt, sizeof(opt));
+		addattr_nest_compat_end(n, nest);
+	}
+	else
+		nest->rta_len = 0;
 	return 0;
 }
 
 static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	struct tc_sfq_qopt *qopt;
+	struct rtattr *tb[TCA_SFQ_MAX+1];
 	SPRINT_BUF(b1);
 
 	if (opt == NULL)
 		return 0;
 
-	if (RTA_PAYLOAD(opt)  < sizeof(*qopt))
+	if (parse_rtattr_nested_compat(tb, TCA_SFQ_MAX, opt, qopt,
+	    sizeof(*qopt)))
 		return -1;
-	qopt = RTA_DATA(opt);
+
 	fprintf(f, "limit %up ", qopt->limit);
 	fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1));
 	if (show_details) {
 		fprintf(f, "flows %u/%u ", qopt->flows, qopt->divisor);
 	}
 	if (qopt->perturb_period)
-		fprintf(f, "perturb %dsec ", qopt->perturb_period);
+		fprintf(f, "perturb %usec ", qopt->perturb_period);
+	fprintf(f, "hash: ");
+	if (RTA_PAYLOAD(tb[TCA_SFQ_HASH]) < sizeof(unsigned))
+		fprintf(f, "unknown");
+	else {
+		unsigned hash = *(unsigned *)RTA_DATA(tb[TCA_SFQ_HASH]);
+		switch (hash) {
+		case TCA_SFQ_HASH_CLASSIC:
+			fprintf(f, "classic");
+			break;
+		case TCA_SFQ_HASH_DST:
+			fprintf(f, "dst");
+			break;
+
+		case TCA_SFQ_HASH_SRC:
+			fprintf(f, "src");
+			break;
+
+		case TCA_SFQ_HASH_FWMARK:
+			fprintf(f, "fwmark");
+			break;
+		case TCA_SFQ_HASH_CTORIGDST:
+			fprintf(f, "ctorigdst");
+			break;
+
+		case TCA_SFQ_HASH_CTORIGSRC:
+			fprintf(f, "ctorigsrc");
+			break;
+
+		case TCA_SFQ_HASH_CTREPLDST:
+			fprintf(f, "ctrepldst");
+			break;
+
+		case TCA_SFQ_HASH_CTREPLSRC:
+			fprintf(f, "ctreplsrc");
+			break;
+
+		case TCA_SFQ_HASH_CTNATCHG:
+			fprintf(f, "ctnatchg");
+			break;
+		default:
+			fprintf(f, "unknown_%u", hash);
+		}
+	}
 	return 0;
 }
 
