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