Index: linux/include/linux/pkt_sched.h
===================================================================
--- linux.orig/include/linux/pkt_sched.h	2006-06-17 18:49:35.000000000 -0700
+++ linux/include/linux/pkt_sched.h	2006-11-16 22:23:10.000000000 -0800
@@ -137,6 +137,9 @@
 	__u32		limit;		/* Maximal packets in queue */
 	unsigned	divisor;	/* Hash divisor  */
 	unsigned	flows;		/* Maximal number of flows  */
+	unsigned char	flags;
+#define TC_SFQ_NOPORTS	(1)		/* ignore src/dst ports in hash */
+#define TC_SFQ_NOSRCIP	(2)		/* ignore src ip address in hash */
 };
 
 /*
Index: linux/net/sched/sch_sfq.c
===================================================================
--- linux.orig/net/sched/sch_sfq.c	2006-11-16 22:22:40.000000000 -0800
+++ linux/net/sched/sch_sfq.c	2006-11-16 22:23:10.000000000 -0800
@@ -35,6 +35,7 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
+#include <linux/jhash.h>
 
 
 /*	Stochastic Fairness Queuing algorithm.
@@ -105,10 +106,11 @@
 	int		perturb_period;
 	unsigned	quantum;	/* Allotment per round: MUST BE >= MTU */
 	int		limit;
+	unsigned	flags;
 
 /* Variables */
 	struct timer_list perturb_timer;
-	int		perturbation;
+	u32		perturbation;
 	sfq_index	tail;		/* Index of current slot in round */
 	sfq_index	max_depth;	/* Maximal depth */
 
@@ -120,53 +122,51 @@
 	struct sfq_head	dep[SFQ_DEPTH*2];	/* Linked list of slots, indexed by depth */
 };
 
-static __inline__ unsigned sfq_fold_hash(struct sfq_sched_data *q, u32 h, u32 h1)
-{
-	int pert = q->perturbation;
-
-	/* Have we any rotation primitives? If not, WHY? */
-	h ^= (h1<<pert) ^ (h1>>(0x1F - pert));
-	h ^= h>>10;
-	return h & 0x3FF;
-}
-
 static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb)
 {
-	u32 h, h2;
+	u32 h1, h2, h3;
 
+	h3 = 0;
 	switch (skb->protocol) {
 	case __constant_htons(ETH_P_IP):
 	{
 		struct iphdr *iph = skb->nh.iph;
-		h = iph->daddr;
-		h2 = iph->saddr^iph->protocol;
-		if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) &&
+		h1 = iph->daddr;
+		h2 = (q->flags & TC_SFQ_NOSRCIP)
+			? 0 : (iph->saddr^iph->protocol);
+		if (!(q->flags & TC_SFQ_NOPORTS) &&
+		    !(iph->frag_off&htons(IP_MF|IP_OFFSET)) &&
 		    (iph->protocol == IPPROTO_TCP ||
 		     iph->protocol == IPPROTO_UDP ||
 		     iph->protocol == IPPROTO_SCTP ||
 		     iph->protocol == IPPROTO_DCCP ||
 		     iph->protocol == IPPROTO_ESP))
-			h2 ^= *(((u32*)iph) + iph->ihl);
+			h3 ^= *(((u32*)iph) + iph->ihl);
 		break;
 	}
 	case __constant_htons(ETH_P_IPV6):
 	{
 		struct ipv6hdr *iph = skb->nh.ipv6h;
-		h = iph->daddr.s6_addr32[3];
-		h2 = iph->saddr.s6_addr32[3]^iph->nexthdr;
-		if (iph->nexthdr == IPPROTO_TCP ||
+		h1 = iph->daddr.s6_addr32[3];
+		h2 = (q->flags & TC_SFQ_NOSRCIP)
+			? 0 : (iph->saddr.s6_addr32[3]^iph->nexthdr);
+		if (!(q->flags & TC_SFQ_NOPORTS) &&
+		   (iph->nexthdr == IPPROTO_TCP ||
 		    iph->nexthdr == IPPROTO_UDP ||
 		    iph->nexthdr == IPPROTO_SCTP ||
 		    iph->nexthdr == IPPROTO_DCCP ||
-		    iph->nexthdr == IPPROTO_ESP)
-			h2 ^= *(u32*)&iph[1];
+		    iph->nexthdr == IPPROTO_ESP))
+			h3 ^= *(u32*)&iph[1];
 		break;
 	}
 	default:
-		h = (u32)(unsigned long)skb->dst^skb->protocol;
-		h2 = (u32)(unsigned long)skb->sk;
+		h1 = (u32)(unsigned long)skb->dst;
+		h2 = (q->flags & TC_SFQ_NOSRCIP)
+			? 0 : (u32)(unsigned long)skb->sk;
+		h3 = skb->protocol;
 	}
-	return sfq_fold_hash(q, h, h2);
+	return jhash_3words(h1, h2, h3, q->perturbation)
+		& (SFQ_HASH_DIVISOR-1);
 }
 
 static inline void sfq_link(struct sfq_sched_data *q, sfq_index x)
@@ -381,7 +381,7 @@
 	struct Qdisc *sch = (struct Qdisc*)arg;
 	struct sfq_sched_data *q = qdisc_priv(sch);
 
-	q->perturbation = net_random()&0x1F;
+	q->perturbation = net_random();
 
 	if (q->perturb_period) {
 		q->perturb_timer.expires = jiffies + q->perturb_period;
@@ -394,7 +394,8 @@
 	struct sfq_sched_data *q = qdisc_priv(sch);
 	struct tc_sfq_qopt *ctl = RTA_DATA(opt);
 
-	if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
+	/* support older tc_sfq_qopt without a flags field */
+	if (opt->rta_len < RTA_LENGTH(offsetof(struct tc_sfq_qopt, flags)))
 		return -EINVAL;
 
 	sch_tree_lock(sch);
@@ -411,6 +412,14 @@
 		q->perturb_timer.expires = jiffies + q->perturb_period;
 		add_timer(&q->perturb_timer);
 	}
+
+	/* must check if flags field is present */
+	if (opt->rta_len >= RTA_LENGTH(sizeof(struct tc_sfq_qopt))) {
+		q->flags = ctl->flags;
+	}
+	else {
+		q->flags = 0;
+	}
 	sch_tree_unlock(sch);
 	return 0;
 }
@@ -465,6 +474,7 @@
 	opt.limit = q->limit;
 	opt.divisor = SFQ_HASH_DIVISOR;
 	opt.flows = q->limit;
+	opt.flags = q->flags;
 
 	RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 
