aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ah6.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ah6.c')
-rw-r--r--net/ipv6/ah6.c89
1 files changed, 83 insertions, 6 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 00ffa7bc6c9f..b0d83e8e4252 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -74,6 +74,66 @@ bad:
74 return 0; 74 return 0;
75} 75}
76 76
77#ifdef CONFIG_IPV6_MIP6
78/**
79 * ipv6_rearrange_destopt - rearrange IPv6 destination options header
80 * @iph: IPv6 header
81 * @destopt: destionation options header
82 */
83static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt)
84{
85 u8 *opt = (u8 *)destopt;
86 int len = ipv6_optlen(destopt);
87 int off = 0;
88 int optlen = 0;
89
90 off += 2;
91 len -= 2;
92
93 while (len > 0) {
94
95 switch (opt[off]) {
96
97 case IPV6_TLV_PAD0:
98 optlen = 1;
99 break;
100 default:
101 if (len < 2)
102 goto bad;
103 optlen = opt[off+1]+2;
104 if (len < optlen)
105 goto bad;
106
107 /* Rearrange the source address in @iph and the
108 * addresses in home address option for final source.
109 * See 11.3.2 of RFC 3775 for details.
110 */
111 if (opt[off] == IPV6_TLV_HAO) {
112 struct in6_addr final_addr;
113 struct ipv6_destopt_hao *hao;
114
115 hao = (struct ipv6_destopt_hao *)&opt[off];
116 if (hao->length != sizeof(hao->addr)) {
117 if (net_ratelimit())
118 printk(KERN_WARNING "destopt hao: invalid header length: %u\n", hao->length);
119 goto bad;
120 }
121 ipv6_addr_copy(&final_addr, &hao->addr);
122 ipv6_addr_copy(&hao->addr, &iph->saddr);
123 ipv6_addr_copy(&iph->saddr, &final_addr);
124 }
125 break;
126 }
127
128 off += optlen;
129 len -= optlen;
130 }
131 /* Note: ok if len == 0 */
132bad:
133 return;
134}
135#endif
136
77/** 137/**
78 * ipv6_rearrange_rthdr - rearrange IPv6 routing header 138 * ipv6_rearrange_rthdr - rearrange IPv6 routing header
79 * @iph: IPv6 header 139 * @iph: IPv6 header
@@ -113,7 +173,7 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
113 ipv6_addr_copy(&iph->daddr, &final_addr); 173 ipv6_addr_copy(&iph->daddr, &final_addr);
114} 174}
115 175
116static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) 176static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
117{ 177{
118 union { 178 union {
119 struct ipv6hdr *iph; 179 struct ipv6hdr *iph;
@@ -128,8 +188,12 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
128 188
129 while (exthdr.raw < end) { 189 while (exthdr.raw < end) {
130 switch (nexthdr) { 190 switch (nexthdr) {
131 case NEXTHDR_HOP:
132 case NEXTHDR_DEST: 191 case NEXTHDR_DEST:
192#ifdef CONFIG_IPV6_MIP6
193 if (dir == XFRM_POLICY_OUT)
194 ipv6_rearrange_destopt(iph, exthdr.opth);
195#endif
196 case NEXTHDR_HOP:
133 if (!zero_out_mutable_opts(exthdr.opth)) { 197 if (!zero_out_mutable_opts(exthdr.opth)) {
134 LIMIT_NETDEBUG( 198 LIMIT_NETDEBUG(
135 KERN_WARNING "overrun %sopts\n", 199 KERN_WARNING "overrun %sopts\n",
@@ -164,6 +228,9 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
164 u8 nexthdr; 228 u8 nexthdr;
165 char tmp_base[8]; 229 char tmp_base[8];
166 struct { 230 struct {
231#ifdef CONFIG_IPV6_MIP6
232 struct in6_addr saddr;
233#endif
167 struct in6_addr daddr; 234 struct in6_addr daddr;
168 char hdrs[0]; 235 char hdrs[0];
169 } *tmp_ext; 236 } *tmp_ext;
@@ -188,10 +255,15 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
188 err = -ENOMEM; 255 err = -ENOMEM;
189 goto error; 256 goto error;
190 } 257 }
258#ifdef CONFIG_IPV6_MIP6
259 memcpy(tmp_ext, &top_iph->saddr, extlen);
260#else
191 memcpy(tmp_ext, &top_iph->daddr, extlen); 261 memcpy(tmp_ext, &top_iph->daddr, extlen);
262#endif
192 err = ipv6_clear_mutable_options(top_iph, 263 err = ipv6_clear_mutable_options(top_iph,
193 extlen - sizeof(*tmp_ext) + 264 extlen - sizeof(*tmp_ext) +
194 sizeof(*top_iph)); 265 sizeof(*top_iph),
266 XFRM_POLICY_OUT);
195 if (err) 267 if (err)
196 goto error_free_iph; 268 goto error_free_iph;
197 } 269 }
@@ -222,7 +294,11 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
222 294
223 memcpy(top_iph, tmp_base, sizeof(tmp_base)); 295 memcpy(top_iph, tmp_base, sizeof(tmp_base));
224 if (tmp_ext) { 296 if (tmp_ext) {
297#ifdef CONFIG_IPV6_MIP6
298 memcpy(&top_iph->saddr, tmp_ext, extlen);
299#else
225 memcpy(&top_iph->daddr, tmp_ext, extlen); 300 memcpy(&top_iph->daddr, tmp_ext, extlen);
301#endif
226error_free_iph: 302error_free_iph:
227 kfree(tmp_ext); 303 kfree(tmp_ext);
228 } 304 }
@@ -282,7 +358,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
282 if (!tmp_hdr) 358 if (!tmp_hdr)
283 goto out; 359 goto out;
284 memcpy(tmp_hdr, skb->nh.raw, hdr_len); 360 memcpy(tmp_hdr, skb->nh.raw, hdr_len);
285 if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len)) 361 if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN))
286 goto free_out; 362 goto free_out;
287 skb->nh.ipv6h->priority = 0; 363 skb->nh.ipv6h->priority = 0;
288 skb->nh.ipv6h->flow_lbl[0] = 0; 364 skb->nh.ipv6h->flow_lbl[0] = 0;
@@ -398,7 +474,7 @@ static int ah6_init_state(struct xfrm_state *x)
398 goto error; 474 goto error;
399 475
400 x->props.header_len = XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len); 476 x->props.header_len = XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len);
401 if (x->props.mode) 477 if (x->props.mode == XFRM_MODE_TUNNEL)
402 x->props.header_len += sizeof(struct ipv6hdr); 478 x->props.header_len += sizeof(struct ipv6hdr);
403 x->data = ahp; 479 x->data = ahp;
404 480
@@ -435,7 +511,8 @@ static struct xfrm_type ah6_type =
435 .init_state = ah6_init_state, 511 .init_state = ah6_init_state,
436 .destructor = ah6_destroy, 512 .destructor = ah6_destroy,
437 .input = ah6_input, 513 .input = ah6_input,
438 .output = ah6_output 514 .output = ah6_output,
515 .hdr_offset = xfrm6_find_1stfragopt,
439}; 516};
440 517
441static struct inet6_protocol ah6_protocol = { 518static struct inet6_protocol ah6_protocol = {