diff options
-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; |