diff options
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r-- | net/ipv6/ip6_output.c | 106 |
1 files changed, 92 insertions, 14 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4fb47a252913..66716911962e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -308,6 +308,56 @@ static int ip6_call_ra_chain(struct sk_buff *skb, int sel) | |||
308 | return 0; | 308 | return 0; |
309 | } | 309 | } |
310 | 310 | ||
311 | static int ip6_forward_proxy_check(struct sk_buff *skb) | ||
312 | { | ||
313 | struct ipv6hdr *hdr = skb->nh.ipv6h; | ||
314 | u8 nexthdr = hdr->nexthdr; | ||
315 | int offset; | ||
316 | |||
317 | if (ipv6_ext_hdr(nexthdr)) { | ||
318 | offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr); | ||
319 | if (offset < 0) | ||
320 | return 0; | ||
321 | } else | ||
322 | offset = sizeof(struct ipv6hdr); | ||
323 | |||
324 | if (nexthdr == IPPROTO_ICMPV6) { | ||
325 | struct icmp6hdr *icmp6; | ||
326 | |||
327 | if (!pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) | ||
328 | return 0; | ||
329 | |||
330 | icmp6 = (struct icmp6hdr *)(skb->nh.raw + offset); | ||
331 | |||
332 | switch (icmp6->icmp6_type) { | ||
333 | case NDISC_ROUTER_SOLICITATION: | ||
334 | case NDISC_ROUTER_ADVERTISEMENT: | ||
335 | case NDISC_NEIGHBOUR_SOLICITATION: | ||
336 | case NDISC_NEIGHBOUR_ADVERTISEMENT: | ||
337 | case NDISC_REDIRECT: | ||
338 | /* For reaction involving unicast neighbor discovery | ||
339 | * message destined to the proxied address, pass it to | ||
340 | * input function. | ||
341 | */ | ||
342 | return 1; | ||
343 | default: | ||
344 | break; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | /* | ||
349 | * The proxying router can't forward traffic sent to a link-local | ||
350 | * address, so signal the sender and discard the packet. This | ||
351 | * behavior is clarified by the MIPv6 specification. | ||
352 | */ | ||
353 | if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) { | ||
354 | dst_link_failure(skb); | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
311 | static inline int ip6_forward_finish(struct sk_buff *skb) | 361 | static inline int ip6_forward_finish(struct sk_buff *skb) |
312 | { | 362 | { |
313 | return dst_output(skb); | 363 | return dst_output(skb); |
@@ -362,6 +412,18 @@ int ip6_forward(struct sk_buff *skb) | |||
362 | return -ETIMEDOUT; | 412 | return -ETIMEDOUT; |
363 | } | 413 | } |
364 | 414 | ||
415 | /* XXX: idev->cnf.proxy_ndp? */ | ||
416 | if (ipv6_devconf.proxy_ndp && | ||
417 | pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { | ||
418 | int proxied = ip6_forward_proxy_check(skb); | ||
419 | if (proxied > 0) | ||
420 | return ip6_input(skb); | ||
421 | else if (proxied < 0) { | ||
422 | IP6_INC_STATS(IPSTATS_MIB_INDISCARDS); | ||
423 | goto drop; | ||
424 | } | ||
425 | } | ||
426 | |||
365 | if (!xfrm6_route_forward(skb)) { | 427 | if (!xfrm6_route_forward(skb)) { |
366 | IP6_INC_STATS(IPSTATS_MIB_INDISCARDS); | 428 | IP6_INC_STATS(IPSTATS_MIB_INDISCARDS); |
367 | goto drop; | 429 | goto drop; |
@@ -475,17 +537,25 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) | |||
475 | switch (**nexthdr) { | 537 | switch (**nexthdr) { |
476 | 538 | ||
477 | case NEXTHDR_HOP: | 539 | case NEXTHDR_HOP: |
540 | break; | ||
478 | case NEXTHDR_ROUTING: | 541 | case NEXTHDR_ROUTING: |
542 | found_rhdr = 1; | ||
543 | break; | ||
479 | case NEXTHDR_DEST: | 544 | case NEXTHDR_DEST: |
480 | if (**nexthdr == NEXTHDR_ROUTING) found_rhdr = 1; | 545 | #ifdef CONFIG_IPV6_MIP6 |
481 | if (**nexthdr == NEXTHDR_DEST && found_rhdr) return offset; | 546 | if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) |
482 | offset += ipv6_optlen(exthdr); | 547 | break; |
483 | *nexthdr = &exthdr->nexthdr; | 548 | #endif |
484 | exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | 549 | if (found_rhdr) |
550 | return offset; | ||
485 | break; | 551 | break; |
486 | default : | 552 | default : |
487 | return offset; | 553 | return offset; |
488 | } | 554 | } |
555 | |||
556 | offset += ipv6_optlen(exthdr); | ||
557 | *nexthdr = &exthdr->nexthdr; | ||
558 | exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | ||
489 | } | 559 | } |
490 | 560 | ||
491 | return offset; | 561 | return offset; |
@@ -726,6 +796,14 @@ fail: | |||
726 | return err; | 796 | return err; |
727 | } | 797 | } |
728 | 798 | ||
799 | static inline int ip6_rt_check(struct rt6key *rt_key, | ||
800 | struct in6_addr *fl_addr, | ||
801 | struct in6_addr *addr_cache) | ||
802 | { | ||
803 | return ((rt_key->plen != 128 || !ipv6_addr_equal(fl_addr, &rt_key->addr)) && | ||
804 | (addr_cache == NULL || !ipv6_addr_equal(fl_addr, addr_cache))); | ||
805 | } | ||
806 | |||
729 | static struct dst_entry *ip6_sk_dst_check(struct sock *sk, | 807 | static struct dst_entry *ip6_sk_dst_check(struct sock *sk, |
730 | struct dst_entry *dst, | 808 | struct dst_entry *dst, |
731 | struct flowi *fl) | 809 | struct flowi *fl) |
@@ -741,8 +819,8 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, | |||
741 | * that we do not support routing by source, TOS, | 819 | * that we do not support routing by source, TOS, |
742 | * and MSG_DONTROUTE --ANK (980726) | 820 | * and MSG_DONTROUTE --ANK (980726) |
743 | * | 821 | * |
744 | * 1. If route was host route, check that | 822 | * 1. ip6_rt_check(): If route was host route, |
745 | * cached destination is current. | 823 | * check that cached destination is current. |
746 | * If it is network route, we still may | 824 | * If it is network route, we still may |
747 | * check its validity using saved pointer | 825 | * check its validity using saved pointer |
748 | * to the last used address: daddr_cache. | 826 | * to the last used address: daddr_cache. |
@@ -753,11 +831,11 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, | |||
753 | * sockets. | 831 | * sockets. |
754 | * 2. oif also should be the same. | 832 | * 2. oif also should be the same. |
755 | */ | 833 | */ |
756 | if (((rt->rt6i_dst.plen != 128 || | 834 | if (ip6_rt_check(&rt->rt6i_dst, &fl->fl6_dst, np->daddr_cache) || |
757 | !ipv6_addr_equal(&fl->fl6_dst, &rt->rt6i_dst.addr)) | 835 | #ifdef CONFIG_IPV6_SUBTREES |
758 | && (np->daddr_cache == NULL || | 836 | ip6_rt_check(&rt->rt6i_src, &fl->fl6_src, np->saddr_cache) || |
759 | !ipv6_addr_equal(&fl->fl6_dst, np->daddr_cache))) | 837 | #endif |
760 | || (fl->oif && fl->oif != dst->dev->ifindex)) { | 838 | (fl->oif && fl->oif != dst->dev->ifindex)) { |
761 | dst_release(dst); | 839 | dst_release(dst); |
762 | dst = NULL; | 840 | dst = NULL; |
763 | } | 841 | } |
@@ -866,7 +944,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, | |||
866 | /* initialize protocol header pointer */ | 944 | /* initialize protocol header pointer */ |
867 | skb->h.raw = skb->data + fragheaderlen; | 945 | skb->h.raw = skb->data + fragheaderlen; |
868 | 946 | ||
869 | skb->ip_summed = CHECKSUM_HW; | 947 | skb->ip_summed = CHECKSUM_PARTIAL; |
870 | skb->csum = 0; | 948 | skb->csum = 0; |
871 | sk->sk_sndmsg_off = 0; | 949 | sk->sk_sndmsg_off = 0; |
872 | } | 950 | } |
@@ -963,7 +1041,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
963 | 1041 | ||
964 | hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); | 1042 | hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); |
965 | 1043 | ||
966 | fragheaderlen = sizeof(struct ipv6hdr) + (opt ? opt->opt_nflen : 0); | 1044 | fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0); |
967 | maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr); | 1045 | maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr); |
968 | 1046 | ||
969 | if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) { | 1047 | if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) { |