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