diff options
Diffstat (limited to 'net/ipv6/xfrm6_mode_tunnel.c')
-rw-r--r-- | net/ipv6/xfrm6_mode_tunnel.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c new file mode 100644 index 000000000000..8af79be2edca --- /dev/null +++ b/net/ipv6/xfrm6_mode_tunnel.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * xfrm6_mode_tunnel.c - Tunnel mode encapsulation for IPv6. | ||
3 | * | ||
4 | * Copyright (C) 2002 USAGI/WIDE Project | ||
5 | * Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au> | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/skbuff.h> | ||
12 | #include <linux/stringify.h> | ||
13 | #include <net/dsfield.h> | ||
14 | #include <net/dst.h> | ||
15 | #include <net/inet_ecn.h> | ||
16 | #include <net/ipv6.h> | ||
17 | #include <net/xfrm.h> | ||
18 | |||
19 | static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) | ||
20 | { | ||
21 | struct ipv6hdr *outer_iph = skb->nh.ipv6h; | ||
22 | struct ipv6hdr *inner_iph = skb->h.ipv6h; | ||
23 | |||
24 | if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) | ||
25 | IP6_ECN_set_ce(inner_iph); | ||
26 | } | ||
27 | |||
28 | /* Add encapsulation header. | ||
29 | * | ||
30 | * The top IP header will be constructed per RFC 2401. The following fields | ||
31 | * in it shall be filled in by x->type->output: | ||
32 | * payload_len | ||
33 | * | ||
34 | * On exit, skb->h will be set to the start of the encapsulation header to be | ||
35 | * filled in by x->type->output and skb->nh will be set to the nextheader field | ||
36 | * of the extension header directly preceding the encapsulation header, or in | ||
37 | * its absence, that of the top IP header. The value of skb->data will always | ||
38 | * point to the top IP header. | ||
39 | */ | ||
40 | static int xfrm6_tunnel_output(struct sk_buff *skb) | ||
41 | { | ||
42 | struct dst_entry *dst = skb->dst; | ||
43 | struct xfrm_state *x = dst->xfrm; | ||
44 | struct ipv6hdr *iph, *top_iph; | ||
45 | int dsfield; | ||
46 | |||
47 | skb_push(skb, x->props.header_len); | ||
48 | iph = skb->nh.ipv6h; | ||
49 | |||
50 | skb->nh.raw = skb->data; | ||
51 | top_iph = skb->nh.ipv6h; | ||
52 | skb->nh.raw = &top_iph->nexthdr; | ||
53 | skb->h.ipv6h = top_iph + 1; | ||
54 | |||
55 | top_iph->version = 6; | ||
56 | top_iph->priority = iph->priority; | ||
57 | top_iph->flow_lbl[0] = iph->flow_lbl[0]; | ||
58 | top_iph->flow_lbl[1] = iph->flow_lbl[1]; | ||
59 | top_iph->flow_lbl[2] = iph->flow_lbl[2]; | ||
60 | dsfield = ipv6_get_dsfield(top_iph); | ||
61 | dsfield = INET_ECN_encapsulate(dsfield, dsfield); | ||
62 | if (x->props.flags & XFRM_STATE_NOECN) | ||
63 | dsfield &= ~INET_ECN_MASK; | ||
64 | ipv6_change_dsfield(top_iph, 0, dsfield); | ||
65 | top_iph->nexthdr = IPPROTO_IPV6; | ||
66 | top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT); | ||
67 | ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); | ||
68 | ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) | ||
73 | { | ||
74 | int err = -EINVAL; | ||
75 | |||
76 | if (skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPV6) | ||
77 | goto out; | ||
78 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) | ||
79 | goto out; | ||
80 | |||
81 | if (skb_cloned(skb) && | ||
82 | (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) | ||
83 | goto out; | ||
84 | |||
85 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) | ||
86 | ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h); | ||
87 | if (!(x->props.flags & XFRM_STATE_NOECN)) | ||
88 | ipip6_ecn_decapsulate(skb); | ||
89 | skb->mac.raw = memmove(skb->data - skb->mac_len, | ||
90 | skb->mac.raw, skb->mac_len); | ||
91 | skb->nh.raw = skb->data; | ||
92 | err = 0; | ||
93 | |||
94 | out: | ||
95 | return err; | ||
96 | } | ||
97 | |||
98 | static struct xfrm_mode xfrm6_tunnel_mode = { | ||
99 | .input = xfrm6_tunnel_input, | ||
100 | .output = xfrm6_tunnel_output, | ||
101 | .owner = THIS_MODULE, | ||
102 | .encap = XFRM_MODE_TUNNEL, | ||
103 | }; | ||
104 | |||
105 | static int __init xfrm6_tunnel_init(void) | ||
106 | { | ||
107 | return xfrm_register_mode(&xfrm6_tunnel_mode, AF_INET6); | ||
108 | } | ||
109 | |||
110 | static void __exit xfrm6_tunnel_exit(void) | ||
111 | { | ||
112 | int err; | ||
113 | |||
114 | err = xfrm_unregister_mode(&xfrm6_tunnel_mode, AF_INET6); | ||
115 | BUG_ON(err); | ||
116 | } | ||
117 | |||
118 | module_init(xfrm6_tunnel_init); | ||
119 | module_exit(xfrm6_tunnel_exit); | ||
120 | MODULE_LICENSE("GPL"); | ||
121 | MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TUNNEL); | ||