summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-09-01 15:06:24 -0700
committerDavid S. Miller <davem@davemloft.net>2015-09-01 15:06:24 -0700
commit41ecc3d390266acc1aa911d2ec477928a5248f75 (patch)
treeaf5886ed49d500ad34a2d0a8d69e92c6d3cf84fe /net
parentd3d11fe08ccc9bff174fc958722b5661f0932486 (diff)
parent6db61d79c1e1b2346e2142d6c950a8d2e8380b82 (diff)
downloadlinux-rpi3-41ecc3d390266acc1aa911d2ec477928a5248f75.tar.gz
linux-rpi3-41ecc3d390266acc1aa911d2ec477928a5248f75.tar.bz2
linux-rpi3-41ecc3d390266acc1aa911d2ec477928a5248f75.zip
Merge branch 'flow-dissector-features'
Tom Herbert says: ==================== flow_dissector: Paramterize dissection and other features This patch set adds some new capabilities to flow_dissector: - Add flags to flow dissector functions to control dissection - Flag to stop dissection when L3 header is seen (don't dissect L4) - Flag to stop dissection when encapsulation is detected - Flag to parse first fragment of fragmented packet. This may provide L4 ports - Added new reporting in key_control - Packet is a fragment - Packet is a first fragment - Packet has encapsulation Also: - Make __skb_set_sw_hash a general function - Create functions to get a flow hash based on flowi4 or flowi6 structures without an reference to an skbuff - Ignore flow dissector return value from ___skb_get_hash. Just use whatever key fields are found to make a hash Tested: Ran 200 netperf TCP_RR instances for IPv6 and IPv4. Did not see any regression. Ran UDP_RR with 10000 byte request and response size for IPv4 and IPv6, no regression observed however I did see better performance with IPv6 flow labels due to use of flow labels for L4 hash. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/flow.c36
-rw-r--r--net/core/flow_dissector.c146
-rw-r--r--net/ethernet/eth.c2
-rw-r--r--net/sched/cls_flow.c2
-rw-r--r--net/sched/cls_flower.c2
-rw-r--r--net/sched/sch_choke.c4
6 files changed, 138 insertions, 54 deletions
diff --git a/net/core/flow.c b/net/core/flow.c
index 1033725be40b..61930bb0eb59 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -22,6 +22,7 @@
#include <linux/cpumask.h>
#include <linux/mutex.h>
#include <net/flow.h>
+#include <net/flow_dissector.h>
#include <linux/atomic.h>
#include <linux/security.h>
#include <net/net_namespace.h>
@@ -509,3 +510,38 @@ void flow_cache_fini(struct net *net)
fc->percpu = NULL;
}
EXPORT_SYMBOL(flow_cache_fini);
+
+__u32 __get_hash_from_flowi6(struct flowi6 *fl6, struct flow_keys *keys)
+{
+ memset(keys, 0, sizeof(*keys));
+
+ memcpy(&keys->addrs.v6addrs.src, &fl6->saddr,
+ sizeof(keys->addrs.v6addrs.src));
+ memcpy(&keys->addrs.v6addrs.dst, &fl6->daddr,
+ sizeof(keys->addrs.v6addrs.dst));
+ keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+ keys->ports.src = fl6->fl6_sport;
+ keys->ports.dst = fl6->fl6_dport;
+ keys->keyid.keyid = fl6->fl6_gre_key;
+ keys->tags.flow_label = (__force u32)fl6->flowlabel;
+ keys->basic.ip_proto = fl6->flowi6_proto;
+
+ return flow_hash_from_keys(keys);
+}
+EXPORT_SYMBOL(__get_hash_from_flowi6);
+
+__u32 __get_hash_from_flowi4(struct flowi4 *fl4, struct flow_keys *keys)
+{
+ memset(keys, 0, sizeof(*keys));
+
+ keys->addrs.v4addrs.src = fl4->saddr;
+ keys->addrs.v4addrs.dst = fl4->daddr;
+ keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
+ keys->ports.src = fl4->fl4_sport;
+ keys->ports.dst = fl4->fl4_dport;
+ keys->keyid.keyid = fl4->fl4_gre_key;
+ keys->basic.ip_proto = fl4->flowi4_proto;
+
+ return flow_hash_from_keys(keys);
+}
+EXPORT_SYMBOL(__get_hash_from_flowi4);
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 11e6540fa386..b563339436d0 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -121,7 +121,8 @@ EXPORT_SYMBOL(__skb_flow_get_ports);
bool __skb_flow_dissect(const struct sk_buff *skb,
struct flow_dissector *flow_dissector,
void *target_container,
- void *data, __be16 proto, int nhoff, int hlen)
+ void *data, __be16 proto, int nhoff, int hlen,
+ unsigned int flags)
{
struct flow_dissector_key_control *key_control;
struct flow_dissector_key_basic *key_basic;
@@ -130,6 +131,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
struct flow_dissector_key_tags *key_tags;
struct flow_dissector_key_keyid *key_keyid;
u8 ip_proto = 0;
+ bool ret = false;
if (!data) {
data = skb->data;
@@ -171,12 +173,10 @@ again:
ip:
iph = __skb_header_pointer(skb, nhoff, sizeof(_iph), data, hlen, &_iph);
if (!iph || iph->ihl < 5)
- return false;
+ goto out_bad;
nhoff += iph->ihl * 4;
ip_proto = iph->protocol;
- if (ip_is_fragment(iph))
- ip_proto = 0;
if (!skb_flow_dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_IPV4_ADDRS))
@@ -187,6 +187,22 @@ ip:
memcpy(&key_addrs->v4addrs, &iph->saddr,
sizeof(key_addrs->v4addrs));
key_control->addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
+
+ if (ip_is_fragment(iph)) {
+ key_control->is_fragment = 1;
+
+ if (iph->frag_off & htons(IP_OFFSET)) {
+ goto out_good;
+ } else {
+ key_control->first_frag = 1;
+ if (!(flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
+ goto out_good;
+ }
+ }
+
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_L3)
+ goto out_good;
+
break;
}
case htons(ETH_P_IPV6): {
@@ -197,7 +213,7 @@ ip:
ipv6:
iph = __skb_header_pointer(skb, nhoff, sizeof(_iph), data, hlen, &_iph);
if (!iph)
- return false;
+ goto out_bad;
ip_proto = iph->nexthdr;
nhoff += sizeof(struct ipv6hdr);
@@ -223,8 +239,13 @@ ipv6:
target_container);
key_tags->flow_label = ntohl(flow_label);
}
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL)
+ goto out_good;
}
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_L3)
+ goto out_good;
+
break;
}
case htons(ETH_P_8021AD):
@@ -234,7 +255,7 @@ ipv6:
vlan = __skb_header_pointer(skb, nhoff, sizeof(_vlan), data, hlen, &_vlan);
if (!vlan)
- return false;
+ goto out_bad;
if (skb_flow_dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_VLANID)) {
@@ -256,7 +277,7 @@ ipv6:
} *hdr, _hdr;
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
if (!hdr)
- return false;
+ goto out_bad;
proto = hdr->proto;
nhoff += PPPOE_SES_HLEN;
switch (proto) {
@@ -265,7 +286,7 @@ ipv6:
case htons(PPP_IPV6):
goto ipv6;
default:
- return false;
+ goto out_bad;
}
}
case htons(ETH_P_TIPC): {
@@ -275,9 +296,7 @@ ipv6:
} *hdr, _hdr;
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
if (!hdr)
- return false;
- key_basic->n_proto = proto;
- key_control->thoff = (u16)nhoff;
+ goto out_bad;
if (skb_flow_dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_TIPC_ADDRS)) {
@@ -287,7 +306,7 @@ ipv6:
key_addrs->tipcaddrs.srcnode = hdr->srcnode;
key_control->addr_type = FLOW_DISSECTOR_KEY_TIPC_ADDRS;
}
- return true;
+ goto out_good;
}
case htons(ETH_P_MPLS_UC):
@@ -297,7 +316,7 @@ mpls:
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data,
hlen, &_hdr);
if (!hdr)
- return false;
+ goto out_bad;
if ((ntohl(hdr[0].entry) & MPLS_LS_LABEL_MASK) >>
MPLS_LS_LABEL_SHIFT == MPLS_LABEL_ENTROPY) {
@@ -310,21 +329,17 @@ mpls:
htonl(MPLS_LS_LABEL_MASK);
}
- key_basic->n_proto = proto;
- key_basic->ip_proto = ip_proto;
- key_control->thoff = (u16)nhoff;
-
- return true;
+ goto out_good;
}
- return true;
+ goto out_good;
}
case htons(ETH_P_FCOE):
key_control->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
/* fall through */
default:
- return false;
+ goto out_bad;
}
ip_proto_again:
@@ -337,7 +352,7 @@ ip_proto_again:
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
if (!hdr)
- return false;
+ goto out_bad;
/*
* Only look inside GRE if version zero and no
* routing
@@ -357,7 +372,7 @@ ip_proto_again:
data, hlen, &_keyid);
if (!keyid)
- return false;
+ goto out_bad;
if (skb_flow_dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_GRE_KEYID)) {
@@ -378,10 +393,15 @@ ip_proto_again:
sizeof(_eth),
data, hlen, &_eth);
if (!eth)
- return false;
+ goto out_bad;
proto = eth->h_proto;
nhoff += sizeof(*eth);
}
+
+ key_control->encapsulation = 1;
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
+ goto out_good;
+
goto again;
}
case NEXTHDR_HOP:
@@ -395,18 +415,53 @@ ip_proto_again:
opthdr = __skb_header_pointer(skb, nhoff, sizeof(_opthdr),
data, hlen, &_opthdr);
if (!opthdr)
- return false;
+ goto out_bad;
ip_proto = opthdr[0];
nhoff += (opthdr[1] + 1) << 3;
goto ip_proto_again;
}
+ case NEXTHDR_FRAGMENT: {
+ struct frag_hdr _fh, *fh;
+
+ if (proto != htons(ETH_P_IPV6))
+ break;
+
+ fh = __skb_header_pointer(skb, nhoff, sizeof(_fh),
+ data, hlen, &_fh);
+
+ if (!fh)
+ goto out_bad;
+
+ key_control->is_fragment = 1;
+
+ nhoff += sizeof(_fh);
+
+ if (!(fh->frag_off & htons(IP6_OFFSET))) {
+ key_control->first_frag = 1;
+ if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG) {
+ ip_proto = fh->nexthdr;
+ goto ip_proto_again;
+ }
+ }
+ goto out_good;
+ }
case IPPROTO_IPIP:
proto = htons(ETH_P_IP);
+
+ key_control->encapsulation = 1;
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
+ goto out_good;
+
goto ip;
case IPPROTO_IPV6:
proto = htons(ETH_P_IPV6);
+
+ key_control->encapsulation = 1;
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
+ goto out_good;
+
goto ipv6;
case IPPROTO_MPLS:
proto = htons(ETH_P_MPLS_UC);
@@ -415,10 +470,6 @@ ip_proto_again:
break;
}
- key_basic->n_proto = proto;
- key_basic->ip_proto = ip_proto;
- key_control->thoff = (u16)nhoff;
-
if (skb_flow_dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_PORTS)) {
key_ports = skb_flow_dissector_target(flow_dissector,
@@ -428,7 +479,15 @@ ip_proto_again:
data, hlen);
}
- return true;
+out_good:
+ ret = true;
+
+out_bad:
+ key_basic->n_proto = proto;
+ key_basic->ip_proto = ip_proto;
+ key_control->thoff = (u16)nhoff;
+
+ return ret;
}
EXPORT_SYMBOL(__skb_flow_dissect);
@@ -557,8 +616,8 @@ EXPORT_SYMBOL(flow_hash_from_keys);
static inline u32 ___skb_get_hash(const struct sk_buff *skb,
struct flow_keys *keys, u32 keyval)
{
- if (!skb_flow_dissect_flow_keys(skb, keys))
- return 0;
+ skb_flow_dissect_flow_keys(skb, keys,
+ FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
return __flow_hash_from_keys(keys, keyval);
}
@@ -590,15 +649,6 @@ void make_flow_keys_digest(struct flow_keys_digest *digest,
}
EXPORT_SYMBOL(make_flow_keys_digest);
-static inline void __skb_set_sw_hash(struct sk_buff *skb, u32 hash,
- struct flow_keys *keys)
-{
- if (keys->ports.ports)
- skb->l4_hash = 1;
- skb->sw_hash = 1;
- skb->hash = hash;
-}
-
/**
* __skb_get_hash: calculate a flow hash
* @skb: sk_buff to calculate flow hash from
@@ -611,15 +661,11 @@ static inline void __skb_set_sw_hash(struct sk_buff *skb, u32 hash,
void __skb_get_hash(struct sk_buff *skb)
{
struct flow_keys keys;
- u32 hash;
__flow_hash_secret_init();
- hash = ___skb_get_hash(skb, &keys, hashrnd);
- if (!hash)
- return;
-
- __skb_set_sw_hash(skb, hash, &keys);
+ __skb_set_sw_hash(skb, ___skb_get_hash(skb, &keys, hashrnd),
+ flow_keys_have_l4(&keys));
}
EXPORT_SYMBOL(__skb_get_hash);
@@ -648,7 +694,8 @@ __u32 __skb_get_hash_flowi6(struct sk_buff *skb, struct flowi6 *fl6)
keys.tags.flow_label = (__force u32)fl6->flowlabel;
keys.basic.ip_proto = fl6->flowi6_proto;
- __skb_set_sw_hash(skb, flow_hash_from_keys(&keys), &keys);
+ __skb_set_sw_hash(skb, flow_hash_from_keys(&keys),
+ flow_keys_have_l4(&keys));
return skb->hash;
}
@@ -668,7 +715,8 @@ __u32 __skb_get_hash_flowi4(struct sk_buff *skb, struct flowi4 *fl4)
keys.keyid.keyid = fl4->fl4_gre_key;
keys.basic.ip_proto = fl4->flowi4_proto;
- __skb_set_sw_hash(skb, flow_hash_from_keys(&keys), &keys);
+ __skb_set_sw_hash(skb, flow_hash_from_keys(&keys),
+ flow_keys_have_l4(&keys));
return skb->hash;
}
@@ -733,7 +781,7 @@ u32 skb_get_poff(const struct sk_buff *skb)
{
struct flow_keys keys;
- if (!skb_flow_dissect_flow_keys(skb, &keys))
+ if (!skb_flow_dissect_flow_keys(skb, &keys, 0))
return 0;
return __skb_get_poff(skb, skb->data, &keys, skb_headlen(skb));
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 217127c3a3ef..d850fdc828f9 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -132,7 +132,7 @@ u32 eth_get_headlen(void *data, unsigned int len)
/* parse any remaining L2/L3 headers, check for L4 */
if (!skb_flow_dissect_flow_keys_buf(&keys, data, eth->h_proto,
- sizeof(*eth), len))
+ sizeof(*eth), len, 0))
return max_t(u32, keys.control.thoff, sizeof(*eth));
/* parse for any L4 headers */
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index bb2a0f529c1f..536838b657bf 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -301,7 +301,7 @@ static int flow_classify(struct sk_buff *skb, const struct tcf_proto *tp,
keymask = f->keymask;
if (keymask & FLOW_KEYS_NEEDED)
- skb_flow_dissect_flow_keys(skb, &flow_keys);
+ skb_flow_dissect_flow_keys(skb, &flow_keys, 0);
for (n = 0; n < f->nkeys; n++) {
key = ffs(keymask) - 1;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 2f3d03f99487..57692947ebbe 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -129,7 +129,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
* so do it rather here.
*/
skb_key.basic.n_proto = skb->protocol;
- skb_flow_dissect(skb, &head->dissector, &skb_key);
+ skb_flow_dissect(skb, &head->dissector, &skb_key, 0);
fl_set_masked_key(&skb_mkey, &skb_key, &head->mask);
diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c
index 665bde07916b..02bfd3d1c4f0 100644
--- a/net/sched/sch_choke.c
+++ b/net/sched/sch_choke.c
@@ -170,13 +170,13 @@ static bool choke_match_flow(struct sk_buff *skb1,
if (!choke_skb_cb(skb1)->keys_valid) {
choke_skb_cb(skb1)->keys_valid = 1;
- skb_flow_dissect_flow_keys(skb1, &temp);
+ skb_flow_dissect_flow_keys(skb1, &temp, 0);
make_flow_keys_digest(&choke_skb_cb(skb1)->keys, &temp);
}
if (!choke_skb_cb(skb2)->keys_valid) {
choke_skb_cb(skb2)->keys_valid = 1;
- skb_flow_dissect_flow_keys(skb2, &temp);
+ skb_flow_dissect_flow_keys(skb2, &temp, 0);
make_flow_keys_digest(&choke_skb_cb(skb2)->keys, &temp);
}