Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2bcc34bb authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 11fca931
Loading
Loading
Loading
Loading
+31 −89
Original line number Diff line number Diff line
@@ -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;
	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))