batman-adv: iv_ogm_send_to_if, declare char* as const
[deliverable/linux.git] / net / core / flow_dissector.c
index d885e282908e91a1bba5e57b2a4c4edbb4757c5d..1f2d89300b1afd3d2319f10035e74ba35038d816 100644 (file)
@@ -1,3 +1,4 @@
+#include <linux/kernel.h>
 #include <linux/skbuff.h>
 #include <linux/export.h>
 #include <linux/ip.h>
 #include <linux/if_tunnel.h>
 #include <linux/if_pppox.h>
 #include <linux/ppp_defs.h>
+#include <linux/stddef.h>
+#include <linux/if_ether.h>
 #include <net/flow_dissector.h>
 #include <scsi/fc/fc_fcoe.h>
 
-/* 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)
+static bool skb_flow_dissector_uses_key(struct flow_dissector *flow_dissector,
+                                       enum flow_dissector_key_id key_id)
+{
+       return flow_dissector->used_keys & (1 << key_id);
+}
+
+static void skb_flow_dissector_set_key(struct flow_dissector *flow_dissector,
+                                      enum flow_dissector_key_id key_id)
+{
+       flow_dissector->used_keys |= (1 << key_id);
+}
+
+static void *skb_flow_dissector_target(struct flow_dissector *flow_dissector,
+                                      enum flow_dissector_key_id key_id,
+                                      void *target_container)
 {
-       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));
+       return ((char *) target_container) + flow_dissector->offset[key_id];
 }
 
+void skb_flow_dissector_init(struct flow_dissector *flow_dissector,
+                            const struct flow_dissector_key *key,
+                            unsigned int key_count)
+{
+       unsigned int i;
+
+       memset(flow_dissector, 0, sizeof(*flow_dissector));
+
+       for (i = 0; i < key_count; i++, key++) {
+               /* User should make sure that every key target offset is withing
+                * boundaries of unsigned short.
+                */
+               BUG_ON(key->offset > USHRT_MAX);
+               BUG_ON(skb_flow_dissector_uses_key(flow_dissector,
+                                                  key->key_id));
+
+               skb_flow_dissector_set_key(flow_dissector, key->key_id);
+               flow_dissector->offset[key->key_id] = key->offset;
+       }
+
+       /* Ensure that the dissector always includes basic key. That way
+        * we are able to avoid handling lack of it in fast path.
+        */
+       BUG_ON(!skb_flow_dissector_uses_key(flow_dissector,
+                                           FLOW_DISSECTOR_KEY_BASIC));
+}
+EXPORT_SYMBOL(skb_flow_dissector_init);
+
 /**
  * __skb_flow_get_ports - extract the upper layer ports and return them
  * @skb: sk_buff to extract the ports from
@@ -63,17 +102,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) {
@@ -83,7 +132,23 @@ 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);
+
+       if (skb_flow_dissector_uses_key(flow_dissector,
+                                       FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct ethhdr *eth = eth_hdr(skb);
+               struct flow_dissector_key_eth_addrs *key_eth_addrs;
+
+               key_eth_addrs = skb_flow_dissector_target(flow_dissector,
+                                                         FLOW_DISSECTOR_KEY_ETH_ADDRS,
+                                                         target_container);
+               memcpy(key_eth_addrs, &eth->h_dest, sizeof(*key_eth_addrs));
+       }
 
 again:
        switch (proto) {
@@ -100,14 +165,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): {
@@ -123,23 +187,47 @@ ipv6:
                ip_proto = iph->nexthdr;
                nhoff += sizeof(struct ipv6hdr);
 
-               /* see comment above in IPv4 section */
-               if (!skb)
-                       break;
+               if (skb_flow_dissector_uses_key(flow_dissector,
+                                               FLOW_DISSECTOR_KEY_IPV6_HASH_ADDRS)) {
+                       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);
+                       goto flow_label;
+               }
+               if (skb_flow_dissector_uses_key(flow_dissector,
+                                               FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+                       struct flow_dissector_key_ipv6_addrs *key_ipv6_addrs;
+
+                       key_ipv6_addrs = skb_flow_dissector_target(flow_dissector,
+                                                                  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+                                                                  target_container);
 
+                       memcpy(key_ipv6_addrs, &iph->saddr, sizeof(*key_ipv6_addrs));
+                       goto flow_label;
+               }
+               break;
+flow_label:
                flow_label = ip6_flowlabel(iph);
                if (flow_label) {
                        /* Awesome, IPv6 packet has a flow label so we can
                         * 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)) {
+                               key_ports = skb_flow_dissector_target(flow_dissector,
+                                                                     FLOW_DISSECTOR_KEY_PORTS,
+                                                                     target_container);
+                               key_ports->ports = flow_label;
+                       }
 
                        return true;
                }
@@ -186,14 +274,21 @@ 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)) {
+                       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;
@@ -248,14 +343,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;
 }
@@ -277,16 +382,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.dst < (__force u16)keys->ports.src))) {
+               swap(keys->addrs.dst, keys->addrs.src);
+               swap(keys->ports.src, keys->ports.dst);
        }
 
-       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;
@@ -304,7 +409,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);
@@ -329,11 +434,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);
 
@@ -356,7 +461,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;
@@ -374,9 +479,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;
@@ -430,8 +535,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);
This page took 0.028362 seconds and 5 git commands to generate.