~james-page/ubuntu/saucy/openvswitch/1.12-snapshot

« back to all changes in this revision

Viewing changes to datapath/linux/compat/flow_dissector.c

  • Committer: James Page
  • Date: 2013-08-21 10:16:57 UTC
  • mfrom: (1.1.20)
  • Revision ID: james.page@canonical.com-20130821101657-3o0z0qeiv5zkwlzi
New upstream snapshot

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2007-2013 Nicira, Inc.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of version 2 of the GNU General Public
 
6
 * License as published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
11
 * General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program; if not, write to the Free Software
 
15
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
16
 * 02110-1301, USA
 
17
 *
 
18
 * This code is derived from kernel flow_dissector.c
 
19
 */
 
20
 
 
21
#include <linux/version.h>
 
22
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
 
23
#include <linux/ip.h>
 
24
#include <linux/ipv6.h>
 
25
#include <linux/if_vlan.h>
 
26
#include <net/ip.h>
 
27
#include <net/ipv6.h>
 
28
#include <linux/igmp.h>
 
29
#include <linux/icmp.h>
 
30
#include <linux/sctp.h>
 
31
#include <linux/dccp.h>
 
32
#include <linux/if_tunnel.h>
 
33
#include <linux/if_pppox.h>
 
34
#include <linux/ppp_defs.h>
 
35
#include <net/flow_keys.h>
 
36
 
 
37
 
 
38
/* copy saddr & daddr, possibly using 64bit load/store
 
39
 * Equivalent to :      flow->src = iph->saddr;
 
40
 *                      flow->dst = iph->daddr;
 
41
 */
 
42
static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph)
 
43
{
 
44
        BUILD_BUG_ON(offsetof(typeof(*flow), dst) !=
 
45
                     offsetof(typeof(*flow), src) + sizeof(flow->src));
 
46
        memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
 
47
}
 
48
 
 
49
static bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow)
 
50
{
 
51
        int poff, nhoff = skb_network_offset(skb);
 
52
        u8 ip_proto;
 
53
        __be16 proto = skb->protocol;
 
54
 
 
55
        memset(flow, 0, sizeof(*flow));
 
56
 
 
57
again:
 
58
        switch (proto) {
 
59
        case __constant_htons(ETH_P_IP): {
 
60
                const struct iphdr *iph;
 
61
                struct iphdr _iph;
 
62
ip:
 
63
                iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
 
64
                if (!iph)
 
65
                        return false;
 
66
 
 
67
                if (ip_is_fragment(iph))
 
68
                        ip_proto = 0;
 
69
                else
 
70
                        ip_proto = iph->protocol;
 
71
                iph_to_flow_copy_addrs(flow, iph);
 
72
                nhoff += iph->ihl * 4;
 
73
                break;
 
74
        }
 
75
        case __constant_htons(ETH_P_IPV6): {
 
76
                const struct ipv6hdr *iph;
 
77
                struct ipv6hdr _iph;
 
78
ipv6:
 
79
                iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
 
80
                if (!iph)
 
81
                        return false;
 
82
 
 
83
                ip_proto = iph->nexthdr;
 
84
                flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
 
85
                flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
 
86
                nhoff += sizeof(struct ipv6hdr);
 
87
                break;
 
88
        }
 
89
        case __constant_htons(ETH_P_8021Q): {
 
90
                const struct vlan_hdr *vlan;
 
91
                struct vlan_hdr _vlan;
 
92
 
 
93
                vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan);
 
94
                if (!vlan)
 
95
                        return false;
 
96
 
 
97
                proto = vlan->h_vlan_encapsulated_proto;
 
98
                nhoff += sizeof(*vlan);
 
99
                goto again;
 
100
        }
 
101
        case __constant_htons(ETH_P_PPP_SES): {
 
102
                struct {
 
103
                        struct pppoe_hdr hdr;
 
104
                        __be16 proto;
 
105
                } *hdr, _hdr;
 
106
                hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr);
 
107
                if (!hdr)
 
108
                        return false;
 
109
                proto = hdr->proto;
 
110
                nhoff += PPPOE_SES_HLEN;
 
111
                switch (proto) {
 
112
                case __constant_htons(PPP_IP):
 
113
                        goto ip;
 
114
                case __constant_htons(PPP_IPV6):
 
115
                        goto ipv6;
 
116
                default:
 
117
                        return false;
 
118
                }
 
119
        }
 
120
        default:
 
121
                return false;
 
122
        }
 
123
 
 
124
        switch (ip_proto) {
 
125
        case IPPROTO_GRE: {
 
126
                struct gre_hdr {
 
127
                        __be16 flags;
 
128
                        __be16 proto;
 
129
                } *hdr, _hdr;
 
130
 
 
131
                hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr);
 
132
                if (!hdr)
 
133
                        return false;
 
134
                /*
 
135
                 * Only look inside GRE if version zero and no
 
136
                 * routing
 
137
                 */
 
138
                if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) {
 
139
                        proto = hdr->proto;
 
140
                        nhoff += 4;
 
141
                        if (hdr->flags & GRE_CSUM)
 
142
                                nhoff += 4;
 
143
                        if (hdr->flags & GRE_KEY)
 
144
                                nhoff += 4;
 
145
                        if (hdr->flags & GRE_SEQ)
 
146
                                nhoff += 4;
 
147
                        if (proto == htons(ETH_P_TEB)) {
 
148
                                const struct ethhdr *eth;
 
149
                                struct ethhdr _eth;
 
150
 
 
151
                                eth = skb_header_pointer(skb, nhoff,
 
152
                                                         sizeof(_eth), &_eth);
 
153
                                if (!eth)
 
154
                                        return false;
 
155
                                proto = eth->h_proto;
 
156
                                nhoff += sizeof(*eth);
 
157
                        }
 
158
                        goto again;
 
159
                }
 
160
                break;
 
161
        }
 
162
        case IPPROTO_IPIP:
 
163
                goto again;
 
164
        default:
 
165
                break;
 
166
        }
 
167
 
 
168
        flow->ip_proto = ip_proto;
 
169
        poff = proto_ports_offset(ip_proto);
 
170
        if (poff >= 0) {
 
171
                __be32 *ports, _ports;
 
172
 
 
173
                nhoff += poff;
 
174
                ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports);
 
175
                if (ports)
 
176
                        flow->ports = *ports;
 
177
        }
 
178
 
 
179
        flow->thoff = (u16) nhoff;
 
180
 
 
181
        return true;
 
182
}
 
183
 
 
184
static u32 hashrnd __read_mostly;
 
185
 
 
186
static void init_hashrnd(void)
 
187
{
 
188
        if (likely(hashrnd))
 
189
                return;
 
190
        get_random_bytes(&hashrnd, sizeof(hashrnd));
 
191
}
 
192
 
 
193
u32 __skb_get_rxhash(struct sk_buff *skb)
 
194
{
 
195
        struct flow_keys keys;
 
196
        u32 hash;
 
197
 
 
198
        if (!skb_flow_dissect(skb, &keys))
 
199
                return 0;
 
200
 
 
201
        /* get a consistent hash (same value on both flow directions) */
 
202
        if (((__force u32)keys.dst < (__force u32)keys.src) ||
 
203
            (((__force u32)keys.dst == (__force u32)keys.src) &&
 
204
             ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) {
 
205
                swap(keys.dst, keys.src);
 
206
                swap(keys.port16[0], keys.port16[1]);
 
207
        }
 
208
 
 
209
        init_hashrnd();
 
210
 
 
211
        hash = jhash_3words((__force u32)keys.dst,
 
212
                            (__force u32)keys.src,
 
213
                            (__force u32)keys.ports, hashrnd);
 
214
        if (!hash)
 
215
                hash = 1;
 
216
 
 
217
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
 
218
        skb->rxhash = hash;
 
219
#endif
 
220
        return hash;
 
221
}
 
222
#endif