aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv6/ah6.c109
-rw-r--r--net/ipv6/ip6_output.c18
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 */
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 if (len == 0)
132 return;
133
134bad:
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
179static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
180#else
116static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) 181static 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
226error_free_iph: 330error_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;