diff options
Diffstat (limited to 'net/ipv6/exthdrs.c')
-rw-r--r-- | net/ipv6/exthdrs.c | 233 |
1 files changed, 213 insertions, 20 deletions
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 86dac106873b..88c96b10684c 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c | |||
@@ -43,9 +43,54 @@ | |||
43 | #include <net/ndisc.h> | 43 | #include <net/ndisc.h> |
44 | #include <net/ip6_route.h> | 44 | #include <net/ip6_route.h> |
45 | #include <net/addrconf.h> | 45 | #include <net/addrconf.h> |
46 | #ifdef CONFIG_IPV6_MIP6 | ||
47 | #include <net/xfrm.h> | ||
48 | #endif | ||
46 | 49 | ||
47 | #include <asm/uaccess.h> | 50 | #include <asm/uaccess.h> |
48 | 51 | ||
52 | int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) | ||
53 | { | ||
54 | int packet_len = skb->tail - skb->nh.raw; | ||
55 | struct ipv6_opt_hdr *hdr; | ||
56 | int len; | ||
57 | |||
58 | if (offset + 2 > packet_len) | ||
59 | goto bad; | ||
60 | hdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | ||
61 | len = ((hdr->hdrlen + 1) << 3); | ||
62 | |||
63 | if (offset + len > packet_len) | ||
64 | goto bad; | ||
65 | |||
66 | offset += 2; | ||
67 | len -= 2; | ||
68 | |||
69 | while (len > 0) { | ||
70 | int opttype = skb->nh.raw[offset]; | ||
71 | int optlen; | ||
72 | |||
73 | if (opttype == type) | ||
74 | return offset; | ||
75 | |||
76 | switch (opttype) { | ||
77 | case IPV6_TLV_PAD0: | ||
78 | optlen = 1; | ||
79 | break; | ||
80 | default: | ||
81 | optlen = skb->nh.raw[offset + 1] + 2; | ||
82 | if (optlen > len) | ||
83 | goto bad; | ||
84 | break; | ||
85 | } | ||
86 | offset += optlen; | ||
87 | len -= optlen; | ||
88 | } | ||
89 | /* not_found */ | ||
90 | bad: | ||
91 | return -1; | ||
92 | } | ||
93 | |||
49 | /* | 94 | /* |
50 | * Parsing tlv encoded headers. | 95 | * Parsing tlv encoded headers. |
51 | * | 96 | * |
@@ -56,7 +101,7 @@ | |||
56 | 101 | ||
57 | struct tlvtype_proc { | 102 | struct tlvtype_proc { |
58 | int type; | 103 | int type; |
59 | int (*func)(struct sk_buff *skb, int offset); | 104 | int (*func)(struct sk_buff **skbp, int offset); |
60 | }; | 105 | }; |
61 | 106 | ||
62 | /********************* | 107 | /********************* |
@@ -65,8 +110,10 @@ struct tlvtype_proc { | |||
65 | 110 | ||
66 | /* An unknown option is detected, decide what to do */ | 111 | /* An unknown option is detected, decide what to do */ |
67 | 112 | ||
68 | static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff) | 113 | static int ip6_tlvopt_unknown(struct sk_buff **skbp, int optoff) |
69 | { | 114 | { |
115 | struct sk_buff *skb = *skbp; | ||
116 | |||
70 | switch ((skb->nh.raw[optoff] & 0xC0) >> 6) { | 117 | switch ((skb->nh.raw[optoff] & 0xC0) >> 6) { |
71 | case 0: /* ignore */ | 118 | case 0: /* ignore */ |
72 | return 1; | 119 | return 1; |
@@ -91,8 +138,9 @@ static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff) | |||
91 | 138 | ||
92 | /* Parse tlv encoded option header (hop-by-hop or destination) */ | 139 | /* Parse tlv encoded option header (hop-by-hop or destination) */ |
93 | 140 | ||
94 | static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb) | 141 | static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff **skbp) |
95 | { | 142 | { |
143 | struct sk_buff *skb = *skbp; | ||
96 | struct tlvtype_proc *curr; | 144 | struct tlvtype_proc *curr; |
97 | int off = skb->h.raw - skb->nh.raw; | 145 | int off = skb->h.raw - skb->nh.raw; |
98 | int len = ((skb->h.raw[1]+1)<<3); | 146 | int len = ((skb->h.raw[1]+1)<<3); |
@@ -122,13 +170,13 @@ static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb) | |||
122 | /* type specific length/alignment | 170 | /* type specific length/alignment |
123 | checks will be performed in the | 171 | checks will be performed in the |
124 | func(). */ | 172 | func(). */ |
125 | if (curr->func(skb, off) == 0) | 173 | if (curr->func(skbp, off) == 0) |
126 | return 0; | 174 | return 0; |
127 | break; | 175 | break; |
128 | } | 176 | } |
129 | } | 177 | } |
130 | if (curr->type < 0) { | 178 | if (curr->type < 0) { |
131 | if (ip6_tlvopt_unknown(skb, off) == 0) | 179 | if (ip6_tlvopt_unknown(skbp, off) == 0) |
132 | return 0; | 180 | return 0; |
133 | } | 181 | } |
134 | break; | 182 | break; |
@@ -147,8 +195,85 @@ bad: | |||
147 | Destination options header. | 195 | Destination options header. |
148 | *****************************/ | 196 | *****************************/ |
149 | 197 | ||
198 | #ifdef CONFIG_IPV6_MIP6 | ||
199 | static int ipv6_dest_hao(struct sk_buff **skbp, int optoff) | ||
200 | { | ||
201 | struct sk_buff *skb = *skbp; | ||
202 | struct ipv6_destopt_hao *hao; | ||
203 | struct inet6_skb_parm *opt = IP6CB(skb); | ||
204 | struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->nh.raw; | ||
205 | struct in6_addr tmp_addr; | ||
206 | int ret; | ||
207 | |||
208 | if (opt->dsthao) { | ||
209 | LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n"); | ||
210 | goto discard; | ||
211 | } | ||
212 | opt->dsthao = opt->dst1; | ||
213 | opt->dst1 = 0; | ||
214 | |||
215 | hao = (struct ipv6_destopt_hao *)(skb->nh.raw + optoff); | ||
216 | |||
217 | if (hao->length != 16) { | ||
218 | LIMIT_NETDEBUG( | ||
219 | KERN_DEBUG "hao invalid option length = %d\n", hao->length); | ||
220 | goto discard; | ||
221 | } | ||
222 | |||
223 | if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) { | ||
224 | LIMIT_NETDEBUG( | ||
225 | KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr)); | ||
226 | goto discard; | ||
227 | } | ||
228 | |||
229 | ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr, | ||
230 | (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS); | ||
231 | if (unlikely(ret < 0)) | ||
232 | goto discard; | ||
233 | |||
234 | if (skb_cloned(skb)) { | ||
235 | struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); | ||
236 | struct inet6_skb_parm *opt2; | ||
237 | |||
238 | if (skb2 == NULL) | ||
239 | goto discard; | ||
240 | |||
241 | opt2 = IP6CB(skb2); | ||
242 | memcpy(opt2, opt, sizeof(*opt2)); | ||
243 | |||
244 | kfree_skb(skb); | ||
245 | |||
246 | /* update all variable using below by copied skbuff */ | ||
247 | *skbp = skb = skb2; | ||
248 | hao = (struct ipv6_destopt_hao *)(skb2->nh.raw + optoff); | ||
249 | ipv6h = (struct ipv6hdr *)skb2->nh.raw; | ||
250 | } | ||
251 | |||
252 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
253 | skb->ip_summed = CHECKSUM_NONE; | ||
254 | |||
255 | ipv6_addr_copy(&tmp_addr, &ipv6h->saddr); | ||
256 | ipv6_addr_copy(&ipv6h->saddr, &hao->addr); | ||
257 | ipv6_addr_copy(&hao->addr, &tmp_addr); | ||
258 | |||
259 | if (skb->tstamp.off_sec == 0) | ||
260 | __net_timestamp(skb); | ||
261 | |||
262 | return 1; | ||
263 | |||
264 | discard: | ||
265 | kfree_skb(skb); | ||
266 | return 0; | ||
267 | } | ||
268 | #endif | ||
269 | |||
150 | static struct tlvtype_proc tlvprocdestopt_lst[] = { | 270 | static struct tlvtype_proc tlvprocdestopt_lst[] = { |
151 | /* No destination options are defined now */ | 271 | #ifdef CONFIG_IPV6_MIP6 |
272 | { | ||
273 | .type = IPV6_TLV_HAO, | ||
274 | .func = ipv6_dest_hao, | ||
275 | }, | ||
276 | #endif | ||
152 | {-1, NULL} | 277 | {-1, NULL} |
153 | }; | 278 | }; |
154 | 279 | ||
@@ -156,6 +281,9 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp) | |||
156 | { | 281 | { |
157 | struct sk_buff *skb = *skbp; | 282 | struct sk_buff *skb = *skbp; |
158 | struct inet6_skb_parm *opt = IP6CB(skb); | 283 | struct inet6_skb_parm *opt = IP6CB(skb); |
284 | #ifdef CONFIG_IPV6_MIP6 | ||
285 | __u16 dstbuf; | ||
286 | #endif | ||
159 | 287 | ||
160 | if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || | 288 | if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || |
161 | !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { | 289 | !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { |
@@ -166,10 +294,19 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp) | |||
166 | 294 | ||
167 | opt->lastopt = skb->h.raw - skb->nh.raw; | 295 | opt->lastopt = skb->h.raw - skb->nh.raw; |
168 | opt->dst1 = skb->h.raw - skb->nh.raw; | 296 | opt->dst1 = skb->h.raw - skb->nh.raw; |
297 | #ifdef CONFIG_IPV6_MIP6 | ||
298 | dstbuf = opt->dst1; | ||
299 | #endif | ||
169 | 300 | ||
170 | if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { | 301 | if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) { |
302 | skb = *skbp; | ||
171 | skb->h.raw += ((skb->h.raw[1]+1)<<3); | 303 | skb->h.raw += ((skb->h.raw[1]+1)<<3); |
304 | opt = IP6CB(skb); | ||
305 | #ifdef CONFIG_IPV6_MIP6 | ||
306 | opt->nhoff = dstbuf; | ||
307 | #else | ||
172 | opt->nhoff = opt->dst1; | 308 | opt->nhoff = opt->dst1; |
309 | #endif | ||
173 | return 1; | 310 | return 1; |
174 | } | 311 | } |
175 | 312 | ||
@@ -219,7 +356,7 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp) | |||
219 | { | 356 | { |
220 | struct sk_buff *skb = *skbp; | 357 | struct sk_buff *skb = *skbp; |
221 | struct inet6_skb_parm *opt = IP6CB(skb); | 358 | struct inet6_skb_parm *opt = IP6CB(skb); |
222 | struct in6_addr *addr; | 359 | struct in6_addr *addr = NULL; |
223 | struct in6_addr daddr; | 360 | struct in6_addr daddr; |
224 | int n, i; | 361 | int n, i; |
225 | 362 | ||
@@ -244,6 +381,23 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp) | |||
244 | 381 | ||
245 | looped_back: | 382 | looped_back: |
246 | if (hdr->segments_left == 0) { | 383 | if (hdr->segments_left == 0) { |
384 | switch (hdr->type) { | ||
385 | #ifdef CONFIG_IPV6_MIP6 | ||
386 | case IPV6_SRCRT_TYPE_2: | ||
387 | /* Silently discard type 2 header unless it was | ||
388 | * processed by own | ||
389 | */ | ||
390 | if (!addr) { | ||
391 | IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); | ||
392 | kfree_skb(skb); | ||
393 | return -1; | ||
394 | } | ||
395 | break; | ||
396 | #endif | ||
397 | default: | ||
398 | break; | ||
399 | } | ||
400 | |||
247 | opt->lastopt = skb->h.raw - skb->nh.raw; | 401 | opt->lastopt = skb->h.raw - skb->nh.raw; |
248 | opt->srcrt = skb->h.raw - skb->nh.raw; | 402 | opt->srcrt = skb->h.raw - skb->nh.raw; |
249 | skb->h.raw += (hdr->hdrlen + 1) << 3; | 403 | skb->h.raw += (hdr->hdrlen + 1) << 3; |
@@ -253,17 +407,29 @@ looped_back: | |||
253 | return 1; | 407 | return 1; |
254 | } | 408 | } |
255 | 409 | ||
256 | if (hdr->type != IPV6_SRCRT_TYPE_0) { | 410 | switch (hdr->type) { |
411 | case IPV6_SRCRT_TYPE_0: | ||
412 | if (hdr->hdrlen & 0x01) { | ||
413 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); | ||
414 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw); | ||
415 | return -1; | ||
416 | } | ||
417 | break; | ||
418 | #ifdef CONFIG_IPV6_MIP6 | ||
419 | case IPV6_SRCRT_TYPE_2: | ||
420 | /* Silently discard invalid RTH type 2 */ | ||
421 | if (hdr->hdrlen != 2 || hdr->segments_left != 1) { | ||
422 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); | ||
423 | kfree_skb(skb); | ||
424 | return -1; | ||
425 | } | ||
426 | break; | ||
427 | #endif | ||
428 | default: | ||
257 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); | 429 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); |
258 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); | 430 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); |
259 | return -1; | 431 | return -1; |
260 | } | 432 | } |
261 | |||
262 | if (hdr->hdrlen & 0x01) { | ||
263 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); | ||
264 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw); | ||
265 | return -1; | ||
266 | } | ||
267 | 433 | ||
268 | /* | 434 | /* |
269 | * This is the routing header forwarding algorithm from | 435 | * This is the routing header forwarding algorithm from |
@@ -294,7 +460,7 @@ looped_back: | |||
294 | hdr = (struct ipv6_rt_hdr *) skb2->h.raw; | 460 | hdr = (struct ipv6_rt_hdr *) skb2->h.raw; |
295 | } | 461 | } |
296 | 462 | ||
297 | if (skb->ip_summed == CHECKSUM_HW) | 463 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
298 | skb->ip_summed = CHECKSUM_NONE; | 464 | skb->ip_summed = CHECKSUM_NONE; |
299 | 465 | ||
300 | i = n - --hdr->segments_left; | 466 | i = n - --hdr->segments_left; |
@@ -303,6 +469,27 @@ looped_back: | |||
303 | addr = rthdr->addr; | 469 | addr = rthdr->addr; |
304 | addr += i - 1; | 470 | addr += i - 1; |
305 | 471 | ||
472 | switch (hdr->type) { | ||
473 | #ifdef CONFIG_IPV6_MIP6 | ||
474 | case IPV6_SRCRT_TYPE_2: | ||
475 | if (xfrm6_input_addr(skb, (xfrm_address_t *)addr, | ||
476 | (xfrm_address_t *)&skb->nh.ipv6h->saddr, | ||
477 | IPPROTO_ROUTING) < 0) { | ||
478 | IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); | ||
479 | kfree_skb(skb); | ||
480 | return -1; | ||
481 | } | ||
482 | if (!ipv6_chk_home_addr(addr)) { | ||
483 | IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); | ||
484 | kfree_skb(skb); | ||
485 | return -1; | ||
486 | } | ||
487 | break; | ||
488 | #endif | ||
489 | default: | ||
490 | break; | ||
491 | } | ||
492 | |||
306 | if (ipv6_addr_is_multicast(addr)) { | 493 | if (ipv6_addr_is_multicast(addr)) { |
307 | IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); | 494 | IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); |
308 | kfree_skb(skb); | 495 | kfree_skb(skb); |
@@ -421,8 +608,10 @@ EXPORT_SYMBOL_GPL(ipv6_invert_rthdr); | |||
421 | 608 | ||
422 | /* Router Alert as of RFC 2711 */ | 609 | /* Router Alert as of RFC 2711 */ |
423 | 610 | ||
424 | static int ipv6_hop_ra(struct sk_buff *skb, int optoff) | 611 | static int ipv6_hop_ra(struct sk_buff **skbp, int optoff) |
425 | { | 612 | { |
613 | struct sk_buff *skb = *skbp; | ||
614 | |||
426 | if (skb->nh.raw[optoff+1] == 2) { | 615 | if (skb->nh.raw[optoff+1] == 2) { |
427 | IP6CB(skb)->ra = optoff; | 616 | IP6CB(skb)->ra = optoff; |
428 | return 1; | 617 | return 1; |
@@ -435,8 +624,9 @@ static int ipv6_hop_ra(struct sk_buff *skb, int optoff) | |||
435 | 624 | ||
436 | /* Jumbo payload */ | 625 | /* Jumbo payload */ |
437 | 626 | ||
438 | static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff) | 627 | static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff) |
439 | { | 628 | { |
629 | struct sk_buff *skb = *skbp; | ||
440 | u32 pkt_len; | 630 | u32 pkt_len; |
441 | 631 | ||
442 | if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) { | 632 | if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) { |
@@ -485,8 +675,9 @@ static struct tlvtype_proc tlvprochopopt_lst[] = { | |||
485 | { -1, } | 675 | { -1, } |
486 | }; | 676 | }; |
487 | 677 | ||
488 | int ipv6_parse_hopopts(struct sk_buff *skb) | 678 | int ipv6_parse_hopopts(struct sk_buff **skbp) |
489 | { | 679 | { |
680 | struct sk_buff *skb = *skbp; | ||
490 | struct inet6_skb_parm *opt = IP6CB(skb); | 681 | struct inet6_skb_parm *opt = IP6CB(skb); |
491 | 682 | ||
492 | /* | 683 | /* |
@@ -502,8 +693,10 @@ int ipv6_parse_hopopts(struct sk_buff *skb) | |||
502 | } | 693 | } |
503 | 694 | ||
504 | opt->hop = sizeof(struct ipv6hdr); | 695 | opt->hop = sizeof(struct ipv6hdr); |
505 | if (ip6_parse_tlv(tlvprochopopt_lst, skb)) { | 696 | if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) { |
697 | skb = *skbp; | ||
506 | skb->h.raw += (skb->h.raw[1]+1)<<3; | 698 | skb->h.raw += (skb->h.raw[1]+1)<<3; |
699 | opt = IP6CB(skb); | ||
507 | opt->nhoff = sizeof(struct ipv6hdr); | 700 | opt->nhoff = sizeof(struct ipv6hdr); |
508 | return 1; | 701 | return 1; |
509 | } | 702 | } |