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

« back to all changes in this revision

Viewing changes to datapath/linux/compat/gre.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
 
 
19
#include <linux/kconfig.h>
 
20
#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
 
21
 
 
22
#include <linux/module.h>
 
23
#include <linux/if.h>
 
24
#include <linux/if_tunnel.h>
 
25
#include <linux/icmp.h>
 
26
#include <linux/in.h>
 
27
#include <linux/ip.h>
 
28
#include <linux/kernel.h>
 
29
#include <linux/kmod.h>
 
30
#include <linux/netdevice.h>
 
31
#include <linux/skbuff.h>
 
32
#include <linux/spinlock.h>
 
33
 
 
34
#include <net/gre.h>
 
35
#include <net/icmp.h>
 
36
#include <net/protocol.h>
 
37
#include <net/route.h>
 
38
#include <net/xfrm.h>
 
39
 
 
40
#include "gso.h"
 
41
 
 
42
static struct gre_cisco_protocol __rcu *gre_cisco_proto;
 
43
 
 
44
static void gre_csum_fix(struct sk_buff *skb)
 
45
{
 
46
        struct gre_base_hdr *greh;
 
47
        __be32 *options;
 
48
        int gre_offset = skb_transport_offset(skb);
 
49
 
 
50
        greh = (struct gre_base_hdr *)skb_transport_header(skb);
 
51
        options = ((__be32 *)greh + 1);
 
52
 
 
53
        *options = 0;
 
54
        *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset,
 
55
                                                     skb->len - gre_offset, 0));
 
56
}
 
57
 
 
58
struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
 
59
{
 
60
        int err;
 
61
 
 
62
        skb_reset_inner_headers(skb);
 
63
 
 
64
        if (skb_is_gso(skb)) {
 
65
                if (gre_csum)
 
66
                        OVS_GSO_CB(skb)->fix_segment = gre_csum_fix;
 
67
        } else {
 
68
                if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) {
 
69
                        err = skb_checksum_help(skb);
 
70
                        if (err)
 
71
                                goto error;
 
72
 
 
73
                } else if (skb->ip_summed != CHECKSUM_PARTIAL)
 
74
                        skb->ip_summed = CHECKSUM_NONE;
 
75
        }
 
76
        return skb;
 
77
error:
 
78
        kfree_skb(skb);
 
79
        return ERR_PTR(err);
 
80
}
 
81
 
 
82
static bool is_gre_gso(struct sk_buff *skb)
 
83
{
 
84
        return skb_is_gso(skb);
 
85
}
 
86
 
 
87
void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
 
88
                      int hdr_len)
 
89
{
 
90
        struct gre_base_hdr *greh;
 
91
 
 
92
        __skb_push(skb, hdr_len);
 
93
 
 
94
        greh = (struct gre_base_hdr *)skb->data;
 
95
        greh->flags = tnl_flags_to_gre_flags(tpi->flags);
 
96
        greh->protocol = tpi->proto;
 
97
 
 
98
        if (tpi->flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) {
 
99
                __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
 
100
 
 
101
                if (tpi->flags & TUNNEL_SEQ) {
 
102
                        *ptr = tpi->seq;
 
103
                        ptr--;
 
104
                }
 
105
                if (tpi->flags & TUNNEL_KEY) {
 
106
                        *ptr = tpi->key;
 
107
                        ptr--;
 
108
                }
 
109
                if (tpi->flags & TUNNEL_CSUM && !is_gre_gso(skb)) {
 
110
                        *ptr = 0;
 
111
                        *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
 
112
                                                skb->len, 0));
 
113
                }
 
114
        }
 
115
}
 
116
 
 
117
static __sum16 check_checksum(struct sk_buff *skb)
 
118
{
 
119
        __sum16 csum = 0;
 
120
 
 
121
        switch (skb->ip_summed) {
 
122
        case CHECKSUM_COMPLETE:
 
123
                csum = csum_fold(skb->csum);
 
124
 
 
125
                if (!csum)
 
126
                        break;
 
127
                /* Fall through. */
 
128
 
 
129
        case CHECKSUM_NONE:
 
130
                skb->csum = 0;
 
131
                csum = __skb_checksum_complete(skb);
 
132
                skb->ip_summed = CHECKSUM_COMPLETE;
 
133
                break;
 
134
        }
 
135
 
 
136
        return csum;
 
137
}
 
138
 
 
139
static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 
140
                            bool *csum_err)
 
141
{
 
142
        unsigned int ip_hlen = ip_hdrlen(skb);
 
143
        struct gre_base_hdr *greh;
 
144
        __be32 *options;
 
145
        int hdr_len;
 
146
 
 
147
        if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
 
148
                return -EINVAL;
 
149
 
 
150
        greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
 
151
        if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
 
152
                return -EINVAL;
 
153
 
 
154
        tpi->flags = gre_flags_to_tnl_flags(greh->flags);
 
155
        hdr_len = ip_gre_calc_hlen(tpi->flags);
 
156
 
 
157
        if (!pskb_may_pull(skb, hdr_len))
 
158
                return -EINVAL;
 
159
 
 
160
        greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
 
161
        tpi->proto = greh->protocol;
 
162
 
 
163
        options = (__be32 *)(greh + 1);
 
164
        if (greh->flags & GRE_CSUM) {
 
165
                if (check_checksum(skb)) {
 
166
                        *csum_err = true;
 
167
                        return -EINVAL;
 
168
                }
 
169
                options++;
 
170
        }
 
171
 
 
172
        if (greh->flags & GRE_KEY) {
 
173
                tpi->key = *options;
 
174
                options++;
 
175
        } else
 
176
                tpi->key = 0;
 
177
 
 
178
        if (unlikely(greh->flags & GRE_SEQ)) {
 
179
                tpi->seq = *options;
 
180
                options++;
 
181
        } else
 
182
                tpi->seq = 0;
 
183
 
 
184
        /* WCCP version 1 and 2 protocol decoding.
 
185
         * - Change protocol to IP
 
186
         * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
 
187
         */
 
188
        if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
 
189
                tpi->proto = htons(ETH_P_IP);
 
190
                if ((*(u8 *)options & 0xF0) != 0x40) {
 
191
                        hdr_len += 4;
 
192
                        if (!pskb_may_pull(skb, hdr_len))
 
193
                                return -EINVAL;
 
194
                }
 
195
        }
 
196
 
 
197
        return iptunnel_pull_header(skb, hdr_len, tpi->proto);
 
198
}
 
199
 
 
200
static int gre_cisco_rcv(struct sk_buff *skb)
 
201
{
 
202
        struct tnl_ptk_info tpi;
 
203
        bool csum_err = false;
 
204
        struct gre_cisco_protocol *proto;
 
205
 
 
206
        rcu_read_lock();
 
207
        proto = rcu_dereference(gre_cisco_proto);
 
208
        if (!proto)
 
209
                goto drop;
 
210
 
 
211
        if (parse_gre_header(skb, &tpi, &csum_err) < 0)
 
212
                goto drop;
 
213
        proto->handler(skb, &tpi);
 
214
        rcu_read_unlock();
 
215
        return 0;
 
216
 
 
217
drop:
 
218
        rcu_read_unlock();
 
219
        kfree_skb(skb);
 
220
        return 0;
 
221
}
 
222
 
 
223
static const struct gre_protocol ipgre_protocol = {
 
224
        .handler        =       gre_cisco_rcv,
 
225
};
 
226
 
 
227
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
 
228
static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
 
229
 
 
230
int gre_add_protocol(const struct gre_protocol *proto, u8 version)
 
231
{
 
232
        if (version >= GREPROTO_MAX)
 
233
                return -EINVAL;
 
234
 
 
235
        return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
 
236
                0 : -EBUSY;
 
237
}
 
238
 
 
239
int gre_del_protocol(const struct gre_protocol *proto, u8 version)
 
240
{
 
241
        int ret;
 
242
 
 
243
        if (version >= GREPROTO_MAX)
 
244
                return -EINVAL;
 
245
 
 
246
        ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
 
247
                0 : -EBUSY;
 
248
 
 
249
        if (ret)
 
250
                return ret;
 
251
 
 
252
        synchronize_net();
 
253
        return 0;
 
254
}
 
255
 
 
256
static int gre_rcv(struct sk_buff *skb)
 
257
{
 
258
        const struct gre_protocol *proto;
 
259
        u8 ver;
 
260
        int ret;
 
261
 
 
262
        if (!pskb_may_pull(skb, 12))
 
263
                goto drop;
 
264
 
 
265
        ver = skb->data[1] & 0x7f;
 
266
        if (ver >= GREPROTO_MAX)
 
267
                goto drop;
 
268
 
 
269
        rcu_read_lock();
 
270
        proto = rcu_dereference(gre_proto[ver]);
 
271
        if (!proto || !proto->handler)
 
272
                goto drop_unlock;
 
273
        ret = proto->handler(skb);
 
274
        rcu_read_unlock();
 
275
        return ret;
 
276
 
 
277
drop_unlock:
 
278
        rcu_read_unlock();
 
279
drop:
 
280
        kfree_skb(skb);
 
281
        return NET_RX_DROP;
 
282
}
 
283
 
 
284
static const struct net_protocol net_gre_protocol = {
 
285
        .handler     = gre_rcv,
 
286
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
 
287
        .netns_ok    = 1,
 
288
#endif
 
289
};
 
290
#endif
 
291
 
 
292
static int gre_compat_init(void)
 
293
{
 
294
        int err;
 
295
 
 
296
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
 
297
        if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
 
298
                pr_err("%s: cannot register gre protocol handler\n", __func__);
 
299
                return -EAGAIN;
 
300
        }
 
301
#endif
 
302
        err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
 
303
        if (err) {
 
304
                pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__);
 
305
 
 
306
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
 
307
                inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
 
308
#endif
 
309
        }
 
310
 
 
311
        return err;
 
312
}
 
313
 
 
314
static int gre_compat_exit(void)
 
315
{
 
316
        int ret;
 
317
 
 
318
        ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
 
319
        if (ret)
 
320
                return ret;
 
321
 
 
322
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
 
323
        ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
 
324
        if (ret)
 
325
                return ret;
 
326
#endif
 
327
        return 0;
 
328
}
 
329
 
 
330
int gre_cisco_register(struct gre_cisco_protocol *newp)
 
331
{
 
332
        int err;
 
333
 
 
334
        err = gre_compat_init();
 
335
        if (err)
 
336
                return err;
 
337
 
 
338
        return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ?
 
339
                0 : -EBUSY;
 
340
}
 
341
 
 
342
int gre_cisco_unregister(struct gre_cisco_protocol *proto)
 
343
{
 
344
        int ret;
 
345
 
 
346
        ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ?
 
347
                0 : -EINVAL;
 
348
 
 
349
        if (ret)
 
350
                return ret;
 
351
 
 
352
        synchronize_net();
 
353
        ret = gre_compat_exit();
 
354
        return ret;
 
355
}
 
356
 
 
357
#endif /* CONFIG_NET_IPGRE_DEMUX */