mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-08 14:23:19 +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/pkt_sched.h>
|
||||||
#include <net/inet_ecn.h>
|
#include <net/inet_ecn.h>
|
||||||
#include <net/red.h>
|
#include <net/red.h>
|
||||||
#include <linux/ip.h>
|
#include <net/flow_keys.h>
|
||||||
#include <net/ip.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <net/ipv6.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CHOKe stateless AQM for fair bandwidth allocation
|
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;
|
--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 {
|
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)
|
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;
|
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:
|
* Classify flow using either:
|
||||||
* 1. pre-existing classification result in skb
|
* 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 */
|
goto other_drop; /* Packet was eaten by filter */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
choke_skb_cb(skb)->keys_valid = 0;
|
||||||
/* Compute average queue usage (see RED) */
|
/* Compute average queue usage (see RED) */
|
||||||
p->qavg = red_calc_qavg(p, sch->q.qlen);
|
p->qavg = red_calc_qavg(p, sch->q.qlen);
|
||||||
if (red_is_idling(p))
|
if (red_is_idling(p))
|
||||||
|
Loading…
Reference in New Issue
Block a user