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