sch_choke: use skb_flow_dissect()

Instead of using a custom flow dissector, use skb_flow_dissect() and
benefit from tunnelling support.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2011-11-29 04:22:15 +00:00 committed by David S. Miller
parent 11fca931d3
commit 2bcc34bb98
1 changed files with 31 additions and 89 deletions

View File

@ -19,10 +19,7 @@
#include <net/pkt_sched.h>
#include <net/inet_ecn.h>
#include <net/red.h>
#include <linux/ip.h>
#include <net/ip.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include <net/flow_keys.h>
/*
CHOKe stateless AQM for fair bandwidth allocation
@ -142,92 +139,10 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)
--sch->q.qlen;
}
/*
* Compare flow of two packets
* Returns true only if source and destination address and port match.
* false for special cases
*/
static bool choke_match_flow(struct sk_buff *skb1,
struct sk_buff *skb2)
{
int off1, off2, poff;
const u32 *ports1, *ports2;
u32 _ports1, _ports2;
u8 ip_proto;
__u32 hash1;
if (skb1->protocol != skb2->protocol)
return false;
/* Use rxhash value as quick check */
hash1 = skb_get_rxhash(skb1);
if (!hash1 || hash1 != skb_get_rxhash(skb2))
return false;
/* Probably match, but be sure to avoid hash collisions */
off1 = skb_network_offset(skb1);
off2 = skb_network_offset(skb2);
switch (skb1->protocol) {
case __constant_htons(ETH_P_IP): {
const struct iphdr *ip1, *ip2;
struct iphdr _ip1, _ip2;
ip1 = skb_header_pointer(skb1, off1, sizeof(_ip1), &_ip1);
ip2 = skb_header_pointer(skb2, off2, sizeof(_ip2), &_ip2);
if (!ip1 || !ip2)
return false;
ip_proto = ip1->protocol;
if (ip_proto != ip2->protocol ||
ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr)
return false;
if (ip_is_fragment(ip1) | ip_is_fragment(ip2))
ip_proto = 0;
off1 += ip1->ihl * 4;
off2 += ip2->ihl * 4;
break;
}
case __constant_htons(ETH_P_IPV6): {
const struct ipv6hdr *ip1, *ip2;
struct ipv6hdr _ip1, _ip2;
ip1 = skb_header_pointer(skb1, off1, sizeof(_ip1), &_ip1);
ip2 = skb_header_pointer(skb2, off2, sizeof(_ip2), &_ip2);
if (!ip1 || !ip2)
return false;
ip_proto = ip1->nexthdr;
if (ip_proto != ip2->nexthdr ||
ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) ||
ipv6_addr_cmp(&ip1->daddr, &ip2->daddr))
return false;
off1 += 40;
off2 += 40;
}
default: /* Maybe compare MAC header here? */
return false;
}
poff = proto_ports_offset(ip_proto);
if (poff < 0)
return true;
off1 += poff;
off2 += poff;
ports1 = skb_header_pointer(skb1, off1, sizeof(_ports1), &_ports1);
ports2 = skb_header_pointer(skb2, off2, sizeof(_ports2), &_ports2);
if (!ports1 || !ports2)
return false;
return *ports1 == *ports2;
}
struct choke_skb_cb {
u16 classid;
u16 classid;
u8 keys_valid;
struct flow_keys keys;
};
static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb)
@ -247,6 +162,32 @@ static u16 choke_get_classid(const struct sk_buff *skb)
return choke_skb_cb(skb)->classid;
}
/*
* Compare flow of two packets
* Returns true only if source and destination address and port match.
* false for special cases
*/
static bool choke_match_flow(struct sk_buff *skb1,
struct sk_buff *skb2)
{
if (skb1->protocol != skb2->protocol)
return false;
if (!choke_skb_cb(skb1)->keys_valid) {
choke_skb_cb(skb1)->keys_valid = 1;
skb_flow_dissect(skb1, &choke_skb_cb(skb1)->keys);
}
if (!choke_skb_cb(skb2)->keys_valid) {
choke_skb_cb(skb2)->keys_valid = 1;
skb_flow_dissect(skb2, &choke_skb_cb(skb2)->keys);
}
return !memcmp(&choke_skb_cb(skb1)->keys,
&choke_skb_cb(skb2)->keys,
sizeof(struct flow_keys));
}
/*
* Classify flow using either:
* 1. pre-existing classification result in skb
@ -333,6 +274,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
goto other_drop; /* Packet was eaten by filter */
}
choke_skb_cb(skb)->keys_valid = 0;
/* Compute average queue usage (see RED) */
p->qavg = red_calc_qavg(p, sch->q.qlen);
if (red_is_idling(p))