]>
Commit | Line | Data |
---|---|---|
00959ade DK |
1 | /* |
2 | * GRE over IPv4 demultiplexer driver | |
3 | * | |
4 | * Authors: Dmitry Kozlov ([email protected]) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | */ | |
12 | ||
afd46503 JP |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | ||
00959ade DK |
15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> | |
17 | #include <linux/kmod.h> | |
18 | #include <linux/skbuff.h> | |
19 | #include <linux/in.h> | |
559fafb9 | 20 | #include <linux/ip.h> |
00959ade | 21 | #include <linux/netdevice.h> |
68c33163 | 22 | #include <linux/if_tunnel.h> |
00959ade DK |
23 | #include <linux/spinlock.h> |
24 | #include <net/protocol.h> | |
25 | #include <net/gre.h> | |
26 | ||
27 | ||
6f0bcf15 | 28 | static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; |
00959ade | 29 | static DEFINE_SPINLOCK(gre_proto_lock); |
68c33163 PS |
30 | struct gre_base_hdr { |
31 | __be16 flags; | |
32 | __be16 protocol; | |
33 | }; | |
34 | #define GRE_HEADER_SECTION 4 | |
00959ade DK |
35 | |
36 | int gre_add_protocol(const struct gre_protocol *proto, u8 version) | |
37 | { | |
38 | if (version >= GREPROTO_MAX) | |
39 | goto err_out; | |
40 | ||
41 | spin_lock(&gre_proto_lock); | |
42 | if (gre_proto[version]) | |
43 | goto err_out_unlock; | |
44 | ||
a9b3cd7f | 45 | RCU_INIT_POINTER(gre_proto[version], proto); |
00959ade DK |
46 | spin_unlock(&gre_proto_lock); |
47 | return 0; | |
48 | ||
49 | err_out_unlock: | |
50 | spin_unlock(&gre_proto_lock); | |
51 | err_out: | |
52 | return -1; | |
53 | } | |
54 | EXPORT_SYMBOL_GPL(gre_add_protocol); | |
55 | ||
56 | int gre_del_protocol(const struct gre_protocol *proto, u8 version) | |
57 | { | |
58 | if (version >= GREPROTO_MAX) | |
59 | goto err_out; | |
60 | ||
61 | spin_lock(&gre_proto_lock); | |
6f0bcf15 ED |
62 | if (rcu_dereference_protected(gre_proto[version], |
63 | lockdep_is_held(&gre_proto_lock)) != proto) | |
00959ade | 64 | goto err_out_unlock; |
a9b3cd7f | 65 | RCU_INIT_POINTER(gre_proto[version], NULL); |
00959ade DK |
66 | spin_unlock(&gre_proto_lock); |
67 | synchronize_rcu(); | |
68 | return 0; | |
69 | ||
70 | err_out_unlock: | |
71 | spin_unlock(&gre_proto_lock); | |
72 | err_out: | |
73 | return -1; | |
74 | } | |
75 | EXPORT_SYMBOL_GPL(gre_del_protocol); | |
76 | ||
77 | static int gre_rcv(struct sk_buff *skb) | |
78 | { | |
79 | const struct gre_protocol *proto; | |
80 | u8 ver; | |
81 | int ret; | |
82 | ||
83 | if (!pskb_may_pull(skb, 12)) | |
84 | goto drop; | |
85 | ||
86 | ver = skb->data[1]&0x7f; | |
87 | if (ver >= GREPROTO_MAX) | |
88 | goto drop; | |
89 | ||
90 | rcu_read_lock(); | |
91 | proto = rcu_dereference(gre_proto[ver]); | |
92 | if (!proto || !proto->handler) | |
93 | goto drop_unlock; | |
94 | ret = proto->handler(skb); | |
95 | rcu_read_unlock(); | |
96 | return ret; | |
97 | ||
98 | drop_unlock: | |
99 | rcu_read_unlock(); | |
100 | drop: | |
101 | kfree_skb(skb); | |
102 | return NET_RX_DROP; | |
103 | } | |
104 | ||
105 | static void gre_err(struct sk_buff *skb, u32 info) | |
106 | { | |
107 | const struct gre_protocol *proto; | |
559fafb9 | 108 | const struct iphdr *iph = (const struct iphdr *)skb->data; |
109 | u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f; | |
00959ade | 110 | |
00959ade | 111 | if (ver >= GREPROTO_MAX) |
559fafb9 | 112 | return; |
00959ade DK |
113 | |
114 | rcu_read_lock(); | |
115 | proto = rcu_dereference(gre_proto[ver]); | |
559fafb9 | 116 | if (proto && proto->err_handler) |
117 | proto->err_handler(skb, info); | |
00959ade | 118 | rcu_read_unlock(); |
00959ade DK |
119 | } |
120 | ||
68c33163 PS |
121 | static struct sk_buff *gre_gso_segment(struct sk_buff *skb, |
122 | netdev_features_t features) | |
123 | { | |
124 | struct sk_buff *segs = ERR_PTR(-EINVAL); | |
125 | netdev_features_t enc_features; | |
126 | int ghl = GRE_HEADER_SECTION; | |
127 | struct gre_base_hdr *greh; | |
128 | int mac_len = skb->mac_len; | |
129 | int tnl_hlen; | |
130 | bool csum; | |
131 | ||
132 | if (unlikely(skb_shinfo(skb)->gso_type & | |
133 | ~(SKB_GSO_TCPV4 | | |
134 | SKB_GSO_TCPV6 | | |
135 | SKB_GSO_UDP | | |
136 | SKB_GSO_DODGY | | |
137 | SKB_GSO_TCP_ECN | | |
138 | SKB_GSO_GRE))) | |
139 | goto out; | |
140 | ||
141 | if (unlikely(!pskb_may_pull(skb, sizeof(*greh)))) | |
142 | goto out; | |
143 | ||
144 | greh = (struct gre_base_hdr *)skb_transport_header(skb); | |
145 | ||
146 | if (greh->flags & GRE_KEY) | |
147 | ghl += GRE_HEADER_SECTION; | |
148 | if (greh->flags & GRE_SEQ) | |
149 | ghl += GRE_HEADER_SECTION; | |
150 | if (greh->flags & GRE_CSUM) { | |
151 | ghl += GRE_HEADER_SECTION; | |
152 | csum = true; | |
153 | } else | |
154 | csum = false; | |
155 | ||
156 | /* setup inner skb. */ | |
157 | if (greh->protocol == htons(ETH_P_TEB)) { | |
158 | struct ethhdr *eth = eth_hdr(skb); | |
159 | skb->protocol = eth->h_proto; | |
160 | } else { | |
161 | skb->protocol = greh->protocol; | |
162 | } | |
163 | ||
164 | skb->encapsulation = 0; | |
165 | ||
166 | if (unlikely(!pskb_may_pull(skb, ghl))) | |
167 | goto out; | |
168 | __skb_pull(skb, ghl); | |
169 | skb_reset_mac_header(skb); | |
170 | skb_set_network_header(skb, skb_inner_network_offset(skb)); | |
171 | skb->mac_len = skb_inner_network_offset(skb); | |
172 | ||
173 | /* segment inner packet. */ | |
174 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); | |
175 | segs = skb_mac_gso_segment(skb, enc_features); | |
176 | if (!segs || IS_ERR(segs)) | |
177 | goto out; | |
178 | ||
179 | skb = segs; | |
180 | tnl_hlen = skb_tnl_header_len(skb); | |
181 | do { | |
182 | __skb_push(skb, ghl); | |
183 | if (csum) { | |
184 | __be32 *pcsum; | |
185 | ||
186 | if (skb_has_shared_frag(skb)) { | |
187 | int err; | |
188 | ||
189 | err = __skb_linearize(skb); | |
190 | if (err) { | |
191 | kfree_skb(segs); | |
192 | segs = ERR_PTR(err); | |
193 | goto out; | |
194 | } | |
195 | } | |
196 | ||
197 | greh = (struct gre_base_hdr *)(skb->data); | |
198 | pcsum = (__be32 *)(greh + 1); | |
199 | *pcsum = 0; | |
200 | *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0)); | |
201 | } | |
202 | __skb_push(skb, tnl_hlen - ghl); | |
203 | ||
204 | skb_reset_mac_header(skb); | |
205 | skb_set_network_header(skb, mac_len); | |
206 | skb->mac_len = mac_len; | |
207 | } while ((skb = skb->next)); | |
208 | out: | |
209 | return segs; | |
210 | } | |
211 | ||
212 | static int gre_gso_send_check(struct sk_buff *skb) | |
213 | { | |
214 | if (!skb->encapsulation) | |
215 | return -EINVAL; | |
216 | return 0; | |
217 | } | |
218 | ||
00959ade DK |
219 | static const struct net_protocol net_gre_protocol = { |
220 | .handler = gre_rcv, | |
221 | .err_handler = gre_err, | |
222 | .netns_ok = 1, | |
223 | }; | |
224 | ||
68c33163 PS |
225 | static const struct net_offload gre_offload = { |
226 | .callbacks = { | |
227 | .gso_send_check = gre_gso_send_check, | |
228 | .gso_segment = gre_gso_segment, | |
229 | }, | |
230 | }; | |
231 | ||
00959ade DK |
232 | static int __init gre_init(void) |
233 | { | |
afd46503 | 234 | pr_info("GRE over IPv4 demultiplexor driver\n"); |
00959ade DK |
235 | |
236 | if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { | |
afd46503 | 237 | pr_err("can't add protocol\n"); |
00959ade DK |
238 | return -EAGAIN; |
239 | } | |
240 | ||
68c33163 PS |
241 | if (inet_add_offload(&gre_offload, IPPROTO_GRE)) { |
242 | pr_err("can't add protocol offload\n"); | |
243 | inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); | |
244 | return -EAGAIN; | |
245 | } | |
246 | ||
00959ade DK |
247 | return 0; |
248 | } | |
249 | ||
250 | static void __exit gre_exit(void) | |
251 | { | |
68c33163 | 252 | inet_del_offload(&gre_offload, IPPROTO_GRE); |
00959ade DK |
253 | inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); |
254 | } | |
255 | ||
256 | module_init(gre_init); | |
257 | module_exit(gre_exit); | |
258 | ||
259 | MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver"); | |
260 | MODULE_AUTHOR("D. Kozlov ([email protected])"); | |
261 | MODULE_LICENSE("GPL"); | |
262 |