diff options
author | Masahide NAKAMURA <nakam@linux-ipv6.org> | 2006-08-23 22:29:47 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 18:06:56 -0400 |
commit | 27637df92e25dfb45dd71a93a2f4bf9c080fa627 (patch) | |
tree | e4c477a1faff6faf7ae2812a921c06c4116f3731 /net | |
parent | 793832361fe7e9c3fcae2edd1d293c583a0a095c (diff) |
[IPV6] IPSEC: Support sending with Mobile IPv6 extension headers.
Mobile IPv6 defines home address option as an option of destination
options header. It is placed before fragment header then
ip6_find_1stfragopt() is fixed to know about it.
Home address option also carries final source address of the flow,
then outbound AH calculation should take care of it like routing
header case. Based on MIPL2 kernel patch.
Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/ah6.c | 109 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 18 |
2 files changed, 122 insertions, 5 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 6c0aa51319a5..0f2b4e330aa9 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
@@ -74,6 +74,68 @@ 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 | if (len == 0) | ||
132 | return; | ||
133 | |||
134 | bad: | ||
135 | return; | ||
136 | } | ||
137 | #endif | ||
138 | |||
77 | /** | 139 | /** |
78 | * ipv6_rearrange_rthdr - rearrange IPv6 routing header | 140 | * ipv6_rearrange_rthdr - rearrange IPv6 routing header |
79 | * @iph: IPv6 header | 141 | * @iph: IPv6 header |
@@ -113,7 +175,11 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) | |||
113 | ipv6_addr_copy(&iph->daddr, &final_addr); | 175 | ipv6_addr_copy(&iph->daddr, &final_addr); |
114 | } | 176 | } |
115 | 177 | ||
178 | #ifdef CONFIG_IPV6_MIP6 | ||
179 | static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) | ||
180 | #else | ||
116 | static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) | 181 | static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) |
182 | #endif | ||
117 | { | 183 | { |
118 | union { | 184 | union { |
119 | struct ipv6hdr *iph; | 185 | struct ipv6hdr *iph; |
@@ -128,6 +194,28 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) | |||
128 | 194 | ||
129 | while (exthdr.raw < end) { | 195 | while (exthdr.raw < end) { |
130 | switch (nexthdr) { | 196 | switch (nexthdr) { |
197 | #ifdef CONFIG_IPV6_MIP6 | ||
198 | case NEXTHDR_HOP: | ||
199 | if (!zero_out_mutable_opts(exthdr.opth)) { | ||
200 | LIMIT_NETDEBUG( | ||
201 | KERN_WARNING "overrun %sopts\n", | ||
202 | nexthdr == NEXTHDR_HOP ? | ||
203 | "hop" : "dest"); | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | break; | ||
207 | case NEXTHDR_DEST: | ||
208 | if (dir == XFRM_POLICY_OUT) | ||
209 | ipv6_rearrange_destopt(iph, exthdr.opth); | ||
210 | if (!zero_out_mutable_opts(exthdr.opth)) { | ||
211 | LIMIT_NETDEBUG( | ||
212 | KERN_WARNING "overrun %sopts\n", | ||
213 | nexthdr == NEXTHDR_HOP ? | ||
214 | "hop" : "dest"); | ||
215 | return -EINVAL; | ||
216 | } | ||
217 | break; | ||
218 | #else | ||
131 | case NEXTHDR_HOP: | 219 | case NEXTHDR_HOP: |
132 | case NEXTHDR_DEST: | 220 | case NEXTHDR_DEST: |
133 | if (!zero_out_mutable_opts(exthdr.opth)) { | 221 | if (!zero_out_mutable_opts(exthdr.opth)) { |
@@ -138,6 +226,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) | |||
138 | return -EINVAL; | 226 | return -EINVAL; |
139 | } | 227 | } |
140 | break; | 228 | break; |
229 | #endif | ||
141 | 230 | ||
142 | case NEXTHDR_ROUTING: | 231 | case NEXTHDR_ROUTING: |
143 | ipv6_rearrange_rthdr(iph, exthdr.rth); | 232 | ipv6_rearrange_rthdr(iph, exthdr.rth); |
@@ -164,6 +253,9 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
164 | u8 nexthdr; | 253 | u8 nexthdr; |
165 | char tmp_base[8]; | 254 | char tmp_base[8]; |
166 | struct { | 255 | struct { |
256 | #ifdef CONFIG_IPV6_MIP6 | ||
257 | struct in6_addr saddr; | ||
258 | #endif | ||
167 | struct in6_addr daddr; | 259 | struct in6_addr daddr; |
168 | char hdrs[0]; | 260 | char hdrs[0]; |
169 | } *tmp_ext; | 261 | } *tmp_ext; |
@@ -188,10 +280,18 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
188 | err = -ENOMEM; | 280 | err = -ENOMEM; |
189 | goto error; | 281 | goto error; |
190 | } | 282 | } |
283 | #ifdef CONFIG_IPV6_MIP6 | ||
284 | memcpy(tmp_ext, &top_iph->saddr, extlen); | ||
285 | err = ipv6_clear_mutable_options(top_iph, | ||
286 | extlen - sizeof(*tmp_ext) + | ||
287 | sizeof(*top_iph), | ||
288 | XFRM_POLICY_OUT); | ||
289 | #else | ||
191 | memcpy(tmp_ext, &top_iph->daddr, extlen); | 290 | memcpy(tmp_ext, &top_iph->daddr, extlen); |
192 | err = ipv6_clear_mutable_options(top_iph, | 291 | err = ipv6_clear_mutable_options(top_iph, |
193 | extlen - sizeof(*tmp_ext) + | 292 | extlen - sizeof(*tmp_ext) + |
194 | sizeof(*top_iph)); | 293 | sizeof(*top_iph)); |
294 | #endif | ||
195 | if (err) | 295 | if (err) |
196 | goto error_free_iph; | 296 | goto error_free_iph; |
197 | } | 297 | } |
@@ -222,7 +322,11 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
222 | 322 | ||
223 | memcpy(top_iph, tmp_base, sizeof(tmp_base)); | 323 | memcpy(top_iph, tmp_base, sizeof(tmp_base)); |
224 | if (tmp_ext) { | 324 | if (tmp_ext) { |
325 | #ifdef CONFIG_IPV6_MIP6 | ||
326 | memcpy(&top_iph->saddr, tmp_ext, extlen); | ||
327 | #else | ||
225 | memcpy(&top_iph->daddr, tmp_ext, extlen); | 328 | memcpy(&top_iph->daddr, tmp_ext, extlen); |
329 | #endif | ||
226 | error_free_iph: | 330 | error_free_iph: |
227 | kfree(tmp_ext); | 331 | kfree(tmp_ext); |
228 | } | 332 | } |
@@ -282,8 +386,13 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
282 | if (!tmp_hdr) | 386 | if (!tmp_hdr) |
283 | goto out; | 387 | goto out; |
284 | memcpy(tmp_hdr, skb->nh.raw, hdr_len); | 388 | memcpy(tmp_hdr, skb->nh.raw, hdr_len); |
389 | #ifdef CONFIG_IPV6_MIP6 | ||
390 | if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN)) | ||
391 | goto free_out; | ||
392 | #else | ||
285 | if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len)) | 393 | if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len)) |
286 | goto free_out; | 394 | goto free_out; |
395 | #endif | ||
287 | skb->nh.ipv6h->priority = 0; | 396 | skb->nh.ipv6h->priority = 0; |
288 | skb->nh.ipv6h->flow_lbl[0] = 0; | 397 | skb->nh.ipv6h->flow_lbl[0] = 0; |
289 | skb->nh.ipv6h->flow_lbl[1] = 0; | 398 | skb->nh.ipv6h->flow_lbl[1] = 0; |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 258e3e45f5e0..c14ea1ecf379 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -475,17 +475,25 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) | |||
475 | switch (**nexthdr) { | 475 | switch (**nexthdr) { |
476 | 476 | ||
477 | case NEXTHDR_HOP: | 477 | case NEXTHDR_HOP: |
478 | break; | ||
478 | case NEXTHDR_ROUTING: | 479 | case NEXTHDR_ROUTING: |
480 | found_rhdr = 1; | ||
481 | break; | ||
479 | case NEXTHDR_DEST: | 482 | case NEXTHDR_DEST: |
480 | if (**nexthdr == NEXTHDR_ROUTING) found_rhdr = 1; | 483 | #ifdef CONFIG_IPV6_MIP6 |
481 | if (**nexthdr == NEXTHDR_DEST && found_rhdr) return offset; | 484 | if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) |
482 | offset += ipv6_optlen(exthdr); | 485 | break; |
483 | *nexthdr = &exthdr->nexthdr; | 486 | #endif |
484 | exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | 487 | if (found_rhdr) |
488 | return offset; | ||
485 | break; | 489 | break; |
486 | default : | 490 | default : |
487 | return offset; | 491 | return offset; |
488 | } | 492 | } |
493 | |||
494 | offset += ipv6_optlen(exthdr); | ||
495 | *nexthdr = &exthdr->nexthdr; | ||
496 | exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | ||
489 | } | 497 | } |
490 | 498 | ||
491 | return offset; | 499 | return offset; |