Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
flow_dissector.c
Go to the documentation of this file.
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  */
17 static 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 
24 bool 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 
32 again:
33  switch (proto) {
34  case __constant_htons(ETH_P_IP): {
35  const struct iphdr *iph;
36  struct iphdr _iph;
37 ip:
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  }
51  const struct ipv6hdr *iph;
52  struct ipv6hdr _iph;
53 ipv6:
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  }
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  }
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) {
88  goto ip;
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 }