|
@@ -13,6 +13,7 @@
|
|
|
#include <linux/if_tunnel.h>
|
|
|
#include <linux/if_pppox.h>
|
|
|
#include <linux/ppp_defs.h>
|
|
|
+#include <linux/stddef.h>
|
|
|
#include <net/flow_dissector.h>
|
|
|
#include <scsi/fc/fc_fcoe.h>
|
|
|
|
|
@@ -63,17 +64,6 @@ void skb_flow_dissector_init(struct flow_dissector *flow_dissector,
|
|
|
}
|
|
|
EXPORT_SYMBOL(skb_flow_dissector_init);
|
|
|
|
|
|
-/* copy saddr & daddr, possibly using 64bit load/store
|
|
|
- * Equivalent to : flow->src = iph->saddr;
|
|
|
- * flow->dst = iph->daddr;
|
|
|
- */
|
|
|
-static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph)
|
|
|
-{
|
|
|
- BUILD_BUG_ON(offsetof(typeof(*flow), dst) !=
|
|
|
- offsetof(typeof(*flow), src) + sizeof(flow->src));
|
|
|
- memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* __skb_flow_get_ports - extract the upper layer ports and return them
|
|
|
* @skb: sk_buff to extract the ports from
|
|
@@ -111,17 +101,27 @@ EXPORT_SYMBOL(__skb_flow_get_ports);
|
|
|
/**
|
|
|
* __skb_flow_dissect - extract the flow_keys struct and return it
|
|
|
* @skb: sk_buff to extract the flow from, can be NULL if the rest are specified
|
|
|
+ * @flow_dissector: list of keys to dissect
|
|
|
+ * @target_container: target structure to put dissected values into
|
|
|
* @data: raw buffer pointer to the packet, if NULL use skb->data
|
|
|
* @proto: protocol for which to get the flow, if @data is NULL use skb->protocol
|
|
|
* @nhoff: network header offset, if @data is NULL use skb_network_offset(skb)
|
|
|
* @hlen: packet header length, if @data is NULL use skb_headlen(skb)
|
|
|
*
|
|
|
- * The function will try to retrieve the struct flow_keys from either the skbuff
|
|
|
- * or a raw buffer specified by the rest parameters
|
|
|
+ * The function will try to retrieve individual keys into target specified
|
|
|
+ * by flow_dissector from either the skbuff or a raw buffer specified by the
|
|
|
+ * rest parameters.
|
|
|
+ *
|
|
|
+ * Caller must take care of zeroing target container memory.
|
|
|
*/
|
|
|
-bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
|
|
+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)
|
|
|
{
|
|
|
+ struct flow_dissector_key_basic *key_basic;
|
|
|
+ struct flow_dissector_key_addrs *key_addrs;
|
|
|
+ struct flow_dissector_key_ports *key_ports;
|
|
|
u8 ip_proto;
|
|
|
|
|
|
if (!data) {
|
|
@@ -131,7 +131,12 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
|
|
hlen = skb_headlen(skb);
|
|
|
}
|
|
|
|
|
|
- memset(flow, 0, sizeof(*flow));
|
|
|
+ /* It is ensured by skb_flow_dissector_init() that basic key will
|
|
|
+ * be always present.
|
|
|
+ */
|
|
|
+ key_basic = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_BASIC,
|
|
|
+ target_container);
|
|
|
|
|
|
again:
|
|
|
switch (proto) {
|
|
@@ -148,14 +153,13 @@ ip:
|
|
|
if (ip_is_fragment(iph))
|
|
|
ip_proto = 0;
|
|
|
|
|
|
- /* skip the address processing if skb is NULL. The assumption
|
|
|
- * here is that if there is no skb we are not looking for flow
|
|
|
- * info but lengths and protocols.
|
|
|
- */
|
|
|
- if (!skb)
|
|
|
+ if (!skb_flow_dissector_uses_key(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS))
|
|
|
break;
|
|
|
-
|
|
|
- iph_to_flow_copy_addrs(flow, iph);
|
|
|
+ key_addrs = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS,
|
|
|
+ target_container);
|
|
|
+ memcpy(key_addrs, &iph->saddr, sizeof(*key_addrs));
|
|
|
break;
|
|
|
}
|
|
|
case htons(ETH_P_IPV6): {
|
|
@@ -171,12 +175,15 @@ ipv6:
|
|
|
ip_proto = iph->nexthdr;
|
|
|
nhoff += sizeof(struct ipv6hdr);
|
|
|
|
|
|
- /* see comment above in IPv4 section */
|
|
|
- if (!skb)
|
|
|
+ if (!skb_flow_dissector_uses_key(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_IPV6_HASH_ADDRS))
|
|
|
break;
|
|
|
+ key_addrs = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_IPV6_HASH_ADDRS,
|
|
|
+ target_container);
|
|
|
|
|
|
- flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
|
|
|
- flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
|
|
|
+ key_addrs->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
|
|
|
+ key_addrs->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
|
|
|
|
|
|
flow_label = ip6_flowlabel(iph);
|
|
|
if (flow_label) {
|
|
@@ -184,10 +191,18 @@ ipv6:
|
|
|
* use that to represent the ports without any
|
|
|
* further dissection.
|
|
|
*/
|
|
|
- flow->n_proto = proto;
|
|
|
- flow->ip_proto = ip_proto;
|
|
|
- flow->ports = flow_label;
|
|
|
- flow->thoff = (u16)nhoff;
|
|
|
+
|
|
|
+ key_basic->n_proto = proto;
|
|
|
+ key_basic->ip_proto = ip_proto;
|
|
|
+ key_basic->thoff = (u16)nhoff;
|
|
|
+
|
|
|
+ if (!skb_flow_dissector_uses_key(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_PORTS))
|
|
|
+ break;
|
|
|
+ key_ports = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_PORTS,
|
|
|
+ target_container);
|
|
|
+ key_ports->ports = flow_label;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -234,14 +249,22 @@ ipv6:
|
|
|
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
|
|
|
if (!hdr)
|
|
|
return false;
|
|
|
- flow->src = hdr->srcnode;
|
|
|
- flow->dst = 0;
|
|
|
- flow->n_proto = proto;
|
|
|
- flow->thoff = (u16)nhoff;
|
|
|
+ key_basic->n_proto = proto;
|
|
|
+ key_basic->thoff = (u16)nhoff;
|
|
|
+
|
|
|
+ if (skb_flow_dissector_uses_key(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_IPV6_HASH_ADDRS)) {
|
|
|
+ return true;
|
|
|
+ key_addrs = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_IPV6_HASH_ADDRS,
|
|
|
+ target_container);
|
|
|
+ key_addrs->src = hdr->srcnode;
|
|
|
+ key_addrs->dst = 0;
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
case htons(ETH_P_FCOE):
|
|
|
- flow->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
|
|
|
+ key_basic->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
|
|
|
/* fall through */
|
|
|
default:
|
|
|
return false;
|
|
@@ -296,14 +319,24 @@ ipv6:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- flow->n_proto = proto;
|
|
|
- flow->ip_proto = ip_proto;
|
|
|
- flow->thoff = (u16) nhoff;
|
|
|
-
|
|
|
- /* unless skb is set we don't need to record port info */
|
|
|
- if (skb)
|
|
|
- flow->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
|
|
|
- data, hlen);
|
|
|
+ /* It is ensured by skb_flow_dissector_init() that basic key will
|
|
|
+ * be always present.
|
|
|
+ */
|
|
|
+ key_basic = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_BASIC,
|
|
|
+ target_container);
|
|
|
+ key_basic->n_proto = proto;
|
|
|
+ key_basic->ip_proto = ip_proto;
|
|
|
+ key_basic->thoff = (u16) nhoff;
|
|
|
+
|
|
|
+ if (skb_flow_dissector_uses_key(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_PORTS)) {
|
|
|
+ key_ports = skb_flow_dissector_target(flow_dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_PORTS,
|
|
|
+ target_container);
|
|
|
+ key_ports->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
|
|
|
+ data, hlen);
|
|
|
+ }
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -325,16 +358,16 @@ static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
|
|
|
u32 hash;
|
|
|
|
|
|
/* get a consistent hash (same value on both flow directions) */
|
|
|
- if (((__force u32)keys->dst < (__force u32)keys->src) ||
|
|
|
- (((__force u32)keys->dst == (__force u32)keys->src) &&
|
|
|
- ((__force u16)keys->port16[1] < (__force u16)keys->port16[0]))) {
|
|
|
- swap(keys->dst, keys->src);
|
|
|
- swap(keys->port16[0], keys->port16[1]);
|
|
|
+ if (((__force u32)keys->addrs.dst < (__force u32)keys->addrs.src) ||
|
|
|
+ (((__force u32)keys->addrs.dst == (__force u32)keys->addrs.src) &&
|
|
|
+ ((__force u16)keys->ports.port16[1] < (__force u16)keys->ports.port16[0]))) {
|
|
|
+ swap(keys->addrs.dst, keys->addrs.src);
|
|
|
+ swap(keys->ports.port16[0], keys->ports.port16[1]);
|
|
|
}
|
|
|
|
|
|
- hash = __flow_hash_3words((__force u32)keys->dst,
|
|
|
- (__force u32)keys->src,
|
|
|
- (__force u32)keys->ports,
|
|
|
+ hash = __flow_hash_3words((__force u32)keys->addrs.dst,
|
|
|
+ (__force u32)keys->addrs.src,
|
|
|
+ (__force u32)keys->ports.ports,
|
|
|
keyval);
|
|
|
if (!hash)
|
|
|
hash = 1;
|
|
@@ -352,7 +385,7 @@ 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(skb, keys))
|
|
|
+ if (!skb_flow_dissect_flow_keys(skb, keys))
|
|
|
return 0;
|
|
|
|
|
|
return __flow_hash_from_keys(keys, keyval);
|
|
@@ -377,11 +410,11 @@ void make_flow_keys_digest(struct flow_keys_digest *digest,
|
|
|
|
|
|
memset(digest, 0, sizeof(*digest));
|
|
|
|
|
|
- data->n_proto = flow->n_proto;
|
|
|
- data->ip_proto = flow->ip_proto;
|
|
|
- data->ports = flow->ports;
|
|
|
- data->src = flow->src;
|
|
|
- data->dst = flow->dst;
|
|
|
+ data->n_proto = flow->basic.n_proto;
|
|
|
+ data->ip_proto = flow->basic.ip_proto;
|
|
|
+ data->ports = flow->ports.ports;
|
|
|
+ data->src = flow->addrs.src;
|
|
|
+ data->dst = flow->addrs.dst;
|
|
|
}
|
|
|
EXPORT_SYMBOL(make_flow_keys_digest);
|
|
|
|
|
@@ -404,7 +437,7 @@ void __skb_get_hash(struct sk_buff *skb)
|
|
|
hash = ___skb_get_hash(skb, &keys, hashrnd);
|
|
|
if (!hash)
|
|
|
return;
|
|
|
- if (keys.ports)
|
|
|
+ if (keys.ports.ports)
|
|
|
skb->l4_hash = 1;
|
|
|
skb->sw_hash = 1;
|
|
|
skb->hash = hash;
|
|
@@ -422,9 +455,9 @@ EXPORT_SYMBOL(skb_get_hash_perturb);
|
|
|
u32 __skb_get_poff(const struct sk_buff *skb, void *data,
|
|
|
const struct flow_keys *keys, int hlen)
|
|
|
{
|
|
|
- u32 poff = keys->thoff;
|
|
|
+ u32 poff = keys->basic.thoff;
|
|
|
|
|
|
- switch (keys->ip_proto) {
|
|
|
+ switch (keys->basic.ip_proto) {
|
|
|
case IPPROTO_TCP: {
|
|
|
/* access doff as u8 to avoid unaligned access */
|
|
|
const u8 *doff;
|
|
@@ -478,8 +511,52 @@ u32 skb_get_poff(const struct sk_buff *skb)
|
|
|
{
|
|
|
struct flow_keys keys;
|
|
|
|
|
|
- if (!skb_flow_dissect(skb, &keys))
|
|
|
+ if (!skb_flow_dissect_flow_keys(skb, &keys))
|
|
|
return 0;
|
|
|
|
|
|
return __skb_get_poff(skb, skb->data, &keys, skb_headlen(skb));
|
|
|
}
|
|
|
+
|
|
|
+static const struct flow_dissector_key flow_keys_dissector_keys[] = {
|
|
|
+ {
|
|
|
+ .key_id = FLOW_DISSECTOR_KEY_BASIC,
|
|
|
+ .offset = offsetof(struct flow_keys, basic),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .key_id = FLOW_DISSECTOR_KEY_IPV4_ADDRS,
|
|
|
+ .offset = offsetof(struct flow_keys, addrs),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .key_id = FLOW_DISSECTOR_KEY_IPV6_HASH_ADDRS,
|
|
|
+ .offset = offsetof(struct flow_keys, addrs),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .key_id = FLOW_DISSECTOR_KEY_PORTS,
|
|
|
+ .offset = offsetof(struct flow_keys, ports),
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static const struct flow_dissector_key flow_keys_buf_dissector_keys[] = {
|
|
|
+ {
|
|
|
+ .key_id = FLOW_DISSECTOR_KEY_BASIC,
|
|
|
+ .offset = offsetof(struct flow_keys, basic),
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+struct flow_dissector flow_keys_dissector __read_mostly;
|
|
|
+EXPORT_SYMBOL(flow_keys_dissector);
|
|
|
+
|
|
|
+struct flow_dissector flow_keys_buf_dissector __read_mostly;
|
|
|
+
|
|
|
+static int __init init_default_flow_dissectors(void)
|
|
|
+{
|
|
|
+ skb_flow_dissector_init(&flow_keys_dissector,
|
|
|
+ flow_keys_dissector_keys,
|
|
|
+ ARRAY_SIZE(flow_keys_dissector_keys));
|
|
|
+ skb_flow_dissector_init(&flow_keys_buf_dissector,
|
|
|
+ flow_keys_buf_dissector_keys,
|
|
|
+ ARRAY_SIZE(flow_keys_buf_dissector_keys));
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+late_initcall_sync(init_default_flow_dissectors);
|