linux/net/core/flow_dissector.c
<<
>>
Prefs
   1#include <linux/skbuff.h>
   2#include <linux/export.h>
   3#include <linux/ip.h>
   4#include <linux/ipv6.h>
   5#include <linux/if_vlan.h>
   6#include <net/ip.h>
   7#include <net/ipv6.h>
   8#include <linux/if_tunnel.h>
   9#include <linux/if_pppox.h>
  10#include <linux/ppp_defs.h>
  11#include <net/flow_keys.h>
  12
  13/* copy saddr & daddr, possibly using 64bit load/store
  14 * Equivalent to :      flow->src = iph->saddr;
  15 *                      flow->dst = iph->daddr;
  16 */
  17static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph)
  18{
  19        BUILD_BUG_ON(offsetof(typeof(*flow), dst) !=
  20                     offsetof(typeof(*flow), src) + sizeof(flow->src));
  21        memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
  22}
  23
  24bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow)
  25{
  26        int poff, nhoff = skb_network_offset(skb);
  27        u8 ip_proto;
  28        __be16 proto = skb->protocol;
  29
  30        memset(flow, 0, sizeof(*flow));
  31
  32again:
  33        switch (proto) {
  34        case __constant_htons(ETH_P_IP): {
  35                const struct iphdr *iph;
  36                struct iphdr _iph;
  37ip:
  38                iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
  39                if (!iph)
  40                        return false;
  41
  42                if (ip_is_fragment(iph))
  43                        ip_proto = 0;
  44                else
  45                        ip_proto = iph->protocol;
  46                iph_to_flow_copy_addrs(flow, iph);
  47                nhoff += iph->ihl * 4;
  48                break;
  49        }
  50        case __constant_htons(ETH_P_IPV6): {
  51                const struct ipv6hdr *iph;
  52                struct ipv6hdr _iph;
  53ipv6:
  54                iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
  55                if (!iph)
  56                        return false;
  57
  58                ip_proto = iph->nexthdr;
  59                flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
  60                flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
  61                nhoff += sizeof(struct ipv6hdr);
  62                break;
  63        }
  64        case __constant_htons(ETH_P_8021Q): {
  65                const struct vlan_hdr *vlan;
  66                struct vlan_hdr _vlan;
  67
  68                vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan);
  69                if (!vlan)
  70                        return false;
  71
  72                proto = vlan->h_vlan_encapsulated_proto;
  73                nhoff += sizeof(*vlan);
  74                goto again;
  75        }
  76        case __constant_htons(ETH_P_PPP_SES): {
  77                struct {
  78                        struct pppoe_hdr hdr;
  79                        __be16 proto;
  80                } *hdr, _hdr;
  81                hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr);
  82                if (!hdr)
  83                        return false;
  84                proto = hdr->proto;
  85                nhoff += PPPOE_SES_HLEN;
  86                switch (proto) {
  87                case __constant_htons(PPP_IP):
  88                        goto ip;
  89                case __constant_htons(PPP_IPV6):
  90                        goto ipv6;
  91                default:
  92                        return false;
  93                }
  94        }
  95        default:
  96                return false;
  97        }
  98
  99        switch (ip_proto) {
 100        case IPPROTO_GRE: {
 101                struct gre_hdr {
 102                        __be16 flags;
 103                        __be16 proto;
 104                } *hdr, _hdr;
 105
 106                hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr);
 107                if (!hdr)
 108                        return false;
 109                /*
 110                 * Only look inside GRE if version zero and no
 111                 * routing
 112                 */
 113                if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) {
 114                        proto = hdr->proto;
 115                        nhoff += 4;
 116                        if (hdr->flags & GRE_CSUM)
 117                                nhoff += 4;
 118                        if (hdr->flags & GRE_KEY)
 119                                nhoff += 4;
 120                        if (hdr->flags & GRE_SEQ)
 121                                nhoff += 4;
 122                        goto again;
 123                }
 124                break;
 125        }
 126        case IPPROTO_IPIP:
 127                goto again;
 128        default:
 129                break;
 130        }
 131
 132        flow->ip_proto = ip_proto;
 133        poff = proto_ports_offset(ip_proto);
 134        if (poff >= 0) {
 135                __be32 *ports, _ports;
 136
 137                nhoff += poff;
 138                ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports);
 139                if (ports)
 140                        flow->ports = *ports;
 141        }
 142
 143        return true;
 144}
 145EXPORT_SYMBOL(skb_flow_dissect);
 146