diff options
Diffstat (limited to 'net/xfrm/xfrm_input.c')
-rw-r--r-- | net/xfrm/xfrm_input.c | 185 |
1 files changed, 184 insertions, 1 deletions
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 0edf3fb73585..e0fd9561ffe5 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c | |||
@@ -21,6 +21,8 @@ | |||
21 | #include <net/ip_tunnels.h> | 21 | #include <net/ip_tunnels.h> |
22 | #include <net/ip6_tunnel.h> | 22 | #include <net/ip6_tunnel.h> |
23 | 23 | ||
24 | #include "xfrm_inout.h" | ||
25 | |||
24 | struct xfrm_trans_tasklet { | 26 | struct xfrm_trans_tasklet { |
25 | struct tasklet_struct tasklet; | 27 | struct tasklet_struct tasklet; |
26 | struct sk_buff_head queue; | 28 | struct sk_buff_head queue; |
@@ -166,6 +168,187 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq) | |||
166 | } | 168 | } |
167 | EXPORT_SYMBOL(xfrm_parse_spi); | 169 | EXPORT_SYMBOL(xfrm_parse_spi); |
168 | 170 | ||
171 | static int xfrm4_remove_beet_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
172 | { | ||
173 | struct iphdr *iph; | ||
174 | int optlen = 0; | ||
175 | int err = -EINVAL; | ||
176 | |||
177 | if (unlikely(XFRM_MODE_SKB_CB(skb)->protocol == IPPROTO_BEETPH)) { | ||
178 | struct ip_beet_phdr *ph; | ||
179 | int phlen; | ||
180 | |||
181 | if (!pskb_may_pull(skb, sizeof(*ph))) | ||
182 | goto out; | ||
183 | |||
184 | ph = (struct ip_beet_phdr *)skb->data; | ||
185 | |||
186 | phlen = sizeof(*ph) + ph->padlen; | ||
187 | optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen); | ||
188 | if (optlen < 0 || optlen & 3 || optlen > 250) | ||
189 | goto out; | ||
190 | |||
191 | XFRM_MODE_SKB_CB(skb)->protocol = ph->nexthdr; | ||
192 | |||
193 | if (!pskb_may_pull(skb, phlen)) | ||
194 | goto out; | ||
195 | __skb_pull(skb, phlen); | ||
196 | } | ||
197 | |||
198 | skb_push(skb, sizeof(*iph)); | ||
199 | skb_reset_network_header(skb); | ||
200 | skb_mac_header_rebuild(skb); | ||
201 | |||
202 | xfrm4_beet_make_header(skb); | ||
203 | |||
204 | iph = ip_hdr(skb); | ||
205 | |||
206 | iph->ihl += optlen / 4; | ||
207 | iph->tot_len = htons(skb->len); | ||
208 | iph->daddr = x->sel.daddr.a4; | ||
209 | iph->saddr = x->sel.saddr.a4; | ||
210 | iph->check = 0; | ||
211 | iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); | ||
212 | err = 0; | ||
213 | out: | ||
214 | return err; | ||
215 | } | ||
216 | |||
217 | static void ipip_ecn_decapsulate(struct sk_buff *skb) | ||
218 | { | ||
219 | struct iphdr *inner_iph = ipip_hdr(skb); | ||
220 | |||
221 | if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos)) | ||
222 | IP_ECN_set_ce(inner_iph); | ||
223 | } | ||
224 | |||
225 | static int xfrm4_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
226 | { | ||
227 | int err = -EINVAL; | ||
228 | |||
229 | if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP) | ||
230 | goto out; | ||
231 | |||
232 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
233 | goto out; | ||
234 | |||
235 | err = skb_unclone(skb, GFP_ATOMIC); | ||
236 | if (err) | ||
237 | goto out; | ||
238 | |||
239 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) | ||
240 | ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb)); | ||
241 | if (!(x->props.flags & XFRM_STATE_NOECN)) | ||
242 | ipip_ecn_decapsulate(skb); | ||
243 | |||
244 | skb_reset_network_header(skb); | ||
245 | skb_mac_header_rebuild(skb); | ||
246 | if (skb->mac_len) | ||
247 | eth_hdr(skb)->h_proto = skb->protocol; | ||
248 | |||
249 | err = 0; | ||
250 | |||
251 | out: | ||
252 | return err; | ||
253 | } | ||
254 | |||
255 | static void ipip6_ecn_decapsulate(struct sk_buff *skb) | ||
256 | { | ||
257 | struct ipv6hdr *inner_iph = ipipv6_hdr(skb); | ||
258 | |||
259 | if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos)) | ||
260 | IP6_ECN_set_ce(skb, inner_iph); | ||
261 | } | ||
262 | |||
263 | static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
264 | { | ||
265 | int err = -EINVAL; | ||
266 | |||
267 | if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6) | ||
268 | goto out; | ||
269 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) | ||
270 | goto out; | ||
271 | |||
272 | err = skb_unclone(skb, GFP_ATOMIC); | ||
273 | if (err) | ||
274 | goto out; | ||
275 | |||
276 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) | ||
277 | ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)), | ||
278 | ipipv6_hdr(skb)); | ||
279 | if (!(x->props.flags & XFRM_STATE_NOECN)) | ||
280 | ipip6_ecn_decapsulate(skb); | ||
281 | |||
282 | skb_reset_network_header(skb); | ||
283 | skb_mac_header_rebuild(skb); | ||
284 | if (skb->mac_len) | ||
285 | eth_hdr(skb)->h_proto = skb->protocol; | ||
286 | |||
287 | err = 0; | ||
288 | |||
289 | out: | ||
290 | return err; | ||
291 | } | ||
292 | |||
293 | static int xfrm6_remove_beet_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
294 | { | ||
295 | struct ipv6hdr *ip6h; | ||
296 | int size = sizeof(struct ipv6hdr); | ||
297 | int err; | ||
298 | |||
299 | err = skb_cow_head(skb, size + skb->mac_len); | ||
300 | if (err) | ||
301 | goto out; | ||
302 | |||
303 | __skb_push(skb, size); | ||
304 | skb_reset_network_header(skb); | ||
305 | skb_mac_header_rebuild(skb); | ||
306 | |||
307 | xfrm6_beet_make_header(skb); | ||
308 | |||
309 | ip6h = ipv6_hdr(skb); | ||
310 | ip6h->payload_len = htons(skb->len - size); | ||
311 | ip6h->daddr = x->sel.daddr.in6; | ||
312 | ip6h->saddr = x->sel.saddr.in6; | ||
313 | err = 0; | ||
314 | out: | ||
315 | return err; | ||
316 | } | ||
317 | |||
318 | /* Remove encapsulation header. | ||
319 | * | ||
320 | * The IP header will be moved over the top of the encapsulation | ||
321 | * header. | ||
322 | * | ||
323 | * On entry, the transport header shall point to where the IP header | ||
324 | * should be and the network header shall be set to where the IP | ||
325 | * header currently is. skb->data shall point to the start of the | ||
326 | * payload. | ||
327 | */ | ||
328 | static int | ||
329 | xfrm_inner_mode_encap_remove(struct xfrm_state *x, | ||
330 | const struct xfrm_mode *inner_mode, | ||
331 | struct sk_buff *skb) | ||
332 | { | ||
333 | switch (inner_mode->encap) { | ||
334 | case XFRM_MODE_BEET: | ||
335 | if (inner_mode->family == AF_INET) | ||
336 | return xfrm4_remove_beet_encap(x, skb); | ||
337 | if (inner_mode->family == AF_INET6) | ||
338 | return xfrm6_remove_beet_encap(x, skb); | ||
339 | break; | ||
340 | case XFRM_MODE_TUNNEL: | ||
341 | if (inner_mode->family == AF_INET) | ||
342 | return xfrm4_remove_tunnel_encap(x, skb); | ||
343 | if (inner_mode->family == AF_INET6) | ||
344 | return xfrm6_remove_tunnel_encap(x, skb); | ||
345 | break; | ||
346 | } | ||
347 | |||
348 | WARN_ON_ONCE(1); | ||
349 | return -EOPNOTSUPP; | ||
350 | } | ||
351 | |||
169 | static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) | 352 | static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) |
170 | { | 353 | { |
171 | struct xfrm_mode *inner_mode = x->inner_mode; | 354 | struct xfrm_mode *inner_mode = x->inner_mode; |
@@ -182,7 +365,7 @@ static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) | |||
182 | } | 365 | } |
183 | 366 | ||
184 | skb->protocol = inner_mode->afinfo->eth_proto; | 367 | skb->protocol = inner_mode->afinfo->eth_proto; |
185 | return inner_mode->input2(x, skb); | 368 | return xfrm_inner_mode_encap_remove(x, inner_mode, skb); |
186 | } | 369 | } |
187 | 370 | ||
188 | /* Remove encapsulation header. | 371 | /* Remove encapsulation header. |