mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-09 06:33:34 +00:00
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:
parent
11fca931d3
commit
2bcc34bb98
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user