diff options
Diffstat (limited to 'net/ipv6/ah6.c')
-rw-r--r-- | net/ipv6/ah6.c | 89 |
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 | */ | ||
83 | static 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 */ | ||
132 | bad: | ||
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 | ||
116 | static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) | 176 | static 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 | ||
226 | error_free_iph: | 302 | error_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 | ||
441 | static struct inet6_protocol ah6_protocol = { | 518 | static struct inet6_protocol ah6_protocol = { |