diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/af_inet6.c | 107 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 2 | ||||
-rw-r--r-- | net/ipv6/route.c | 54 | ||||
-rw-r--r-- | net/ipv6/sysctl_net_ipv6.c | 2 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 45 |
5 files changed, 193 insertions, 17 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 437b750b98fd..94f74f5b0cbf 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -672,8 +672,7 @@ int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb) | |||
672 | 672 | ||
673 | EXPORT_SYMBOL_GPL(ipv6_opt_accepted); | 673 | EXPORT_SYMBOL_GPL(ipv6_opt_accepted); |
674 | 674 | ||
675 | static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, | 675 | static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) |
676 | int proto) | ||
677 | { | 676 | { |
678 | struct inet6_protocol *ops = NULL; | 677 | struct inet6_protocol *ops = NULL; |
679 | 678 | ||
@@ -704,7 +703,7 @@ static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, | |||
704 | __skb_pull(skb, len); | 703 | __skb_pull(skb, len); |
705 | } | 704 | } |
706 | 705 | ||
707 | return ops; | 706 | return proto; |
708 | } | 707 | } |
709 | 708 | ||
710 | static int ipv6_gso_send_check(struct sk_buff *skb) | 709 | static int ipv6_gso_send_check(struct sk_buff *skb) |
@@ -721,7 +720,9 @@ static int ipv6_gso_send_check(struct sk_buff *skb) | |||
721 | err = -EPROTONOSUPPORT; | 720 | err = -EPROTONOSUPPORT; |
722 | 721 | ||
723 | rcu_read_lock(); | 722 | rcu_read_lock(); |
724 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | 723 | ops = rcu_dereference(inet6_protos[ |
724 | ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); | ||
725 | |||
725 | if (likely(ops && ops->gso_send_check)) { | 726 | if (likely(ops && ops->gso_send_check)) { |
726 | skb_reset_transport_header(skb); | 727 | skb_reset_transport_header(skb); |
727 | err = ops->gso_send_check(skb); | 728 | err = ops->gso_send_check(skb); |
@@ -757,7 +758,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | |||
757 | segs = ERR_PTR(-EPROTONOSUPPORT); | 758 | segs = ERR_PTR(-EPROTONOSUPPORT); |
758 | 759 | ||
759 | rcu_read_lock(); | 760 | rcu_read_lock(); |
760 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | 761 | ops = rcu_dereference(inet6_protos[ |
762 | ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); | ||
763 | |||
761 | if (likely(ops && ops->gso_segment)) { | 764 | if (likely(ops && ops->gso_segment)) { |
762 | skb_reset_transport_header(skb); | 765 | skb_reset_transport_header(skb); |
763 | segs = ops->gso_segment(skb, features); | 766 | segs = ops->gso_segment(skb, features); |
@@ -777,11 +780,105 @@ out: | |||
777 | return segs; | 780 | return segs; |
778 | } | 781 | } |
779 | 782 | ||
783 | struct ipv6_gro_cb { | ||
784 | struct napi_gro_cb napi; | ||
785 | int proto; | ||
786 | }; | ||
787 | |||
788 | #define IPV6_GRO_CB(skb) ((struct ipv6_gro_cb *)(skb)->cb) | ||
789 | |||
790 | static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, | ||
791 | struct sk_buff *skb) | ||
792 | { | ||
793 | struct inet6_protocol *ops; | ||
794 | struct sk_buff **pp = NULL; | ||
795 | struct sk_buff *p; | ||
796 | struct ipv6hdr *iph; | ||
797 | unsigned int nlen; | ||
798 | int flush = 1; | ||
799 | int proto; | ||
800 | |||
801 | if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) | ||
802 | goto out; | ||
803 | |||
804 | iph = ipv6_hdr(skb); | ||
805 | __skb_pull(skb, sizeof(*iph)); | ||
806 | |||
807 | flush += ntohs(iph->payload_len) != skb->len; | ||
808 | |||
809 | rcu_read_lock(); | ||
810 | proto = ipv6_gso_pull_exthdrs(skb, iph->nexthdr); | ||
811 | IPV6_GRO_CB(skb)->proto = proto; | ||
812 | ops = rcu_dereference(inet6_protos[proto]); | ||
813 | if (!ops || !ops->gro_receive) | ||
814 | goto out_unlock; | ||
815 | |||
816 | flush--; | ||
817 | skb_reset_transport_header(skb); | ||
818 | nlen = skb_network_header_len(skb); | ||
819 | |||
820 | for (p = *head; p; p = p->next) { | ||
821 | struct ipv6hdr *iph2; | ||
822 | |||
823 | if (!NAPI_GRO_CB(p)->same_flow) | ||
824 | continue; | ||
825 | |||
826 | iph2 = ipv6_hdr(p); | ||
827 | |||
828 | /* All fields must match except length. */ | ||
829 | if (nlen != skb_network_header_len(p) || | ||
830 | memcmp(iph, iph2, offsetof(struct ipv6hdr, payload_len)) || | ||
831 | memcmp(&iph->nexthdr, &iph2->nexthdr, | ||
832 | nlen - offsetof(struct ipv6hdr, nexthdr))) { | ||
833 | NAPI_GRO_CB(p)->same_flow = 0; | ||
834 | continue; | ||
835 | } | ||
836 | |||
837 | NAPI_GRO_CB(p)->flush |= flush; | ||
838 | } | ||
839 | |||
840 | NAPI_GRO_CB(skb)->flush |= flush; | ||
841 | |||
842 | pp = ops->gro_receive(head, skb); | ||
843 | |||
844 | out_unlock: | ||
845 | rcu_read_unlock(); | ||
846 | |||
847 | out: | ||
848 | NAPI_GRO_CB(skb)->flush |= flush; | ||
849 | |||
850 | return pp; | ||
851 | } | ||
852 | |||
853 | static int ipv6_gro_complete(struct sk_buff *skb) | ||
854 | { | ||
855 | struct inet6_protocol *ops; | ||
856 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
857 | int err = -ENOSYS; | ||
858 | |||
859 | iph->payload_len = htons(skb->len - skb_network_offset(skb) - | ||
860 | sizeof(*iph)); | ||
861 | |||
862 | rcu_read_lock(); | ||
863 | ops = rcu_dereference(inet6_protos[IPV6_GRO_CB(skb)->proto]); | ||
864 | if (WARN_ON(!ops || !ops->gro_complete)) | ||
865 | goto out_unlock; | ||
866 | |||
867 | err = ops->gro_complete(skb); | ||
868 | |||
869 | out_unlock: | ||
870 | rcu_read_unlock(); | ||
871 | |||
872 | return err; | ||
873 | } | ||
874 | |||
780 | static struct packet_type ipv6_packet_type = { | 875 | static struct packet_type ipv6_packet_type = { |
781 | .type = __constant_htons(ETH_P_IPV6), | 876 | .type = __constant_htons(ETH_P_IPV6), |
782 | .func = ipv6_rcv, | 877 | .func = ipv6_rcv, |
783 | .gso_send_check = ipv6_gso_send_check, | 878 | .gso_send_check = ipv6_gso_send_check, |
784 | .gso_segment = ipv6_gso_segment, | 879 | .gso_segment = ipv6_gso_segment, |
880 | .gro_receive = ipv6_gro_receive, | ||
881 | .gro_complete = ipv6_gro_complete, | ||
785 | }; | 882 | }; |
786 | 883 | ||
787 | static int __init ipv6_packet_init(void) | 884 | static int __init ipv6_packet_init(void) |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index eeeaad2e8b5c..40f324655e24 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -404,7 +404,7 @@ sticky_done: | |||
404 | else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL) | 404 | else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL) |
405 | goto e_inval; | 405 | goto e_inval; |
406 | 406 | ||
407 | if (copy_from_user(&pkt, optval, optlen)) { | 407 | if (copy_from_user(&pkt, optval, sizeof(struct in6_pktinfo))) { |
408 | retv = -EFAULT; | 408 | retv = -EFAULT; |
409 | break; | 409 | break; |
410 | } | 410 | } |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 18c486cf4987..c4a59824ac2c 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -627,6 +627,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad | |||
627 | rt = ip6_rt_copy(ort); | 627 | rt = ip6_rt_copy(ort); |
628 | 628 | ||
629 | if (rt) { | 629 | if (rt) { |
630 | struct neighbour *neigh; | ||
631 | int attempts = !in_softirq(); | ||
632 | |||
630 | if (!(rt->rt6i_flags&RTF_GATEWAY)) { | 633 | if (!(rt->rt6i_flags&RTF_GATEWAY)) { |
631 | if (rt->rt6i_dst.plen != 128 && | 634 | if (rt->rt6i_dst.plen != 128 && |
632 | ipv6_addr_equal(&rt->rt6i_dst.addr, daddr)) | 635 | ipv6_addr_equal(&rt->rt6i_dst.addr, daddr)) |
@@ -646,7 +649,35 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad | |||
646 | } | 649 | } |
647 | #endif | 650 | #endif |
648 | 651 | ||
649 | rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); | 652 | retry: |
653 | neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); | ||
654 | if (IS_ERR(neigh)) { | ||
655 | struct net *net = dev_net(rt->rt6i_dev); | ||
656 | int saved_rt_min_interval = | ||
657 | net->ipv6.sysctl.ip6_rt_gc_min_interval; | ||
658 | int saved_rt_elasticity = | ||
659 | net->ipv6.sysctl.ip6_rt_gc_elasticity; | ||
660 | |||
661 | if (attempts-- > 0) { | ||
662 | net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; | ||
663 | net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; | ||
664 | |||
665 | ip6_dst_gc(net->ipv6.ip6_dst_ops); | ||
666 | |||
667 | net->ipv6.sysctl.ip6_rt_gc_elasticity = | ||
668 | saved_rt_elasticity; | ||
669 | net->ipv6.sysctl.ip6_rt_gc_min_interval = | ||
670 | saved_rt_min_interval; | ||
671 | goto retry; | ||
672 | } | ||
673 | |||
674 | if (net_ratelimit()) | ||
675 | printk(KERN_WARNING | ||
676 | "Neighbour table overflow.\n"); | ||
677 | dst_free(&rt->u.dst); | ||
678 | return NULL; | ||
679 | } | ||
680 | rt->rt6i_nexthop = neigh; | ||
650 | 681 | ||
651 | } | 682 | } |
652 | 683 | ||
@@ -945,8 +976,11 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, | |||
945 | dev_hold(dev); | 976 | dev_hold(dev); |
946 | if (neigh) | 977 | if (neigh) |
947 | neigh_hold(neigh); | 978 | neigh_hold(neigh); |
948 | else | 979 | else { |
949 | neigh = ndisc_get_neigh(dev, addr); | 980 | neigh = ndisc_get_neigh(dev, addr); |
981 | if (IS_ERR(neigh)) | ||
982 | neigh = NULL; | ||
983 | } | ||
950 | 984 | ||
951 | rt->rt6i_dev = dev; | 985 | rt->rt6i_dev = dev; |
952 | rt->rt6i_idev = idev; | 986 | rt->rt6i_idev = idev; |
@@ -1887,6 +1921,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, | |||
1887 | { | 1921 | { |
1888 | struct net *net = dev_net(idev->dev); | 1922 | struct net *net = dev_net(idev->dev); |
1889 | struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); | 1923 | struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); |
1924 | struct neighbour *neigh; | ||
1890 | 1925 | ||
1891 | if (rt == NULL) | 1926 | if (rt == NULL) |
1892 | return ERR_PTR(-ENOMEM); | 1927 | return ERR_PTR(-ENOMEM); |
@@ -1909,11 +1944,18 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, | |||
1909 | rt->rt6i_flags |= RTF_ANYCAST; | 1944 | rt->rt6i_flags |= RTF_ANYCAST; |
1910 | else | 1945 | else |
1911 | rt->rt6i_flags |= RTF_LOCAL; | 1946 | rt->rt6i_flags |= RTF_LOCAL; |
1912 | rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); | 1947 | neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); |
1913 | if (rt->rt6i_nexthop == NULL) { | 1948 | if (IS_ERR(neigh)) { |
1914 | dst_free(&rt->u.dst); | 1949 | dst_free(&rt->u.dst); |
1915 | return ERR_PTR(-ENOMEM); | 1950 | |
1951 | /* We are casting this because that is the return | ||
1952 | * value type. But an errno encoded pointer is the | ||
1953 | * same regardless of the underlying pointer type, | ||
1954 | * and that's what we are returning. So this is OK. | ||
1955 | */ | ||
1956 | return (struct rt6_info *) neigh; | ||
1916 | } | 1957 | } |
1958 | rt->rt6i_nexthop = neigh; | ||
1917 | 1959 | ||
1918 | ipv6_addr_copy(&rt->rt6i_dst.addr, addr); | 1960 | ipv6_addr_copy(&rt->rt6i_dst.addr, addr); |
1919 | rt->rt6i_dst.plen = 128; | 1961 | rt->rt6i_dst.plen = 128; |
@@ -2710,7 +2752,7 @@ int __init ip6_route_init(void) | |||
2710 | kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, | 2752 | kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, |
2711 | SLAB_HWCACHE_ALIGN, NULL); | 2753 | SLAB_HWCACHE_ALIGN, NULL); |
2712 | if (!ip6_dst_ops_template.kmem_cachep) | 2754 | if (!ip6_dst_ops_template.kmem_cachep) |
2713 | goto out;; | 2755 | goto out; |
2714 | 2756 | ||
2715 | ret = register_pernet_subsys(&ip6_route_net_ops); | 2757 | ret = register_pernet_subsys(&ip6_route_net_ops); |
2716 | if (ret) | 2758 | if (ret) |
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 9048fe7e7ea7..a031034720b4 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c | |||
@@ -128,7 +128,7 @@ static struct ctl_table_header *ip6_header; | |||
128 | 128 | ||
129 | int ipv6_sysctl_register(void) | 129 | int ipv6_sysctl_register(void) |
130 | { | 130 | { |
131 | int err = -ENOMEM;; | 131 | int err = -ENOMEM; |
132 | 132 | ||
133 | ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_table); | 133 | ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_table); |
134 | if (ip6_header == NULL) | 134 | if (ip6_header == NULL) |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index e8b8337a8310..1297306d729c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -101,7 +101,7 @@ static void tcp_v6_hash(struct sock *sk) | |||
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
104 | static __inline__ __sum16 tcp_v6_check(struct tcphdr *th, int len, | 104 | static __inline__ __sum16 tcp_v6_check(int len, |
105 | struct in6_addr *saddr, | 105 | struct in6_addr *saddr, |
106 | struct in6_addr *daddr, | 106 | struct in6_addr *daddr, |
107 | __wsum base) | 107 | __wsum base) |
@@ -501,7 +501,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req) | |||
501 | if (skb) { | 501 | if (skb) { |
502 | struct tcphdr *th = tcp_hdr(skb); | 502 | struct tcphdr *th = tcp_hdr(skb); |
503 | 503 | ||
504 | th->check = tcp_v6_check(th, skb->len, | 504 | th->check = tcp_v6_check(skb->len, |
505 | &treq->loc_addr, &treq->rmt_addr, | 505 | &treq->loc_addr, &treq->rmt_addr, |
506 | csum_partial(th, skb->len, skb->csum)); | 506 | csum_partial(th, skb->len, skb->csum)); |
507 | 507 | ||
@@ -942,6 +942,41 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb) | |||
942 | return 0; | 942 | return 0; |
943 | } | 943 | } |
944 | 944 | ||
945 | struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb) | ||
946 | { | ||
947 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
948 | |||
949 | switch (skb->ip_summed) { | ||
950 | case CHECKSUM_COMPLETE: | ||
951 | if (!tcp_v6_check(skb->len, &iph->saddr, &iph->daddr, | ||
952 | skb->csum)) { | ||
953 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
954 | break; | ||
955 | } | ||
956 | |||
957 | /* fall through */ | ||
958 | case CHECKSUM_NONE: | ||
959 | NAPI_GRO_CB(skb)->flush = 1; | ||
960 | return NULL; | ||
961 | } | ||
962 | |||
963 | return tcp_gro_receive(head, skb); | ||
964 | } | ||
965 | EXPORT_SYMBOL(tcp6_gro_receive); | ||
966 | |||
967 | int tcp6_gro_complete(struct sk_buff *skb) | ||
968 | { | ||
969 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
970 | struct tcphdr *th = tcp_hdr(skb); | ||
971 | |||
972 | th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb), | ||
973 | &iph->saddr, &iph->daddr, 0); | ||
974 | skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; | ||
975 | |||
976 | return tcp_gro_complete(skb); | ||
977 | } | ||
978 | EXPORT_SYMBOL(tcp6_gro_complete); | ||
979 | |||
945 | static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, | 980 | static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, |
946 | u32 ts, struct tcp_md5sig_key *key, int rst) | 981 | u32 ts, struct tcp_md5sig_key *key, int rst) |
947 | { | 982 | { |
@@ -1429,14 +1464,14 @@ out: | |||
1429 | static __sum16 tcp_v6_checksum_init(struct sk_buff *skb) | 1464 | static __sum16 tcp_v6_checksum_init(struct sk_buff *skb) |
1430 | { | 1465 | { |
1431 | if (skb->ip_summed == CHECKSUM_COMPLETE) { | 1466 | if (skb->ip_summed == CHECKSUM_COMPLETE) { |
1432 | if (!tcp_v6_check(tcp_hdr(skb), skb->len, &ipv6_hdr(skb)->saddr, | 1467 | if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr, |
1433 | &ipv6_hdr(skb)->daddr, skb->csum)) { | 1468 | &ipv6_hdr(skb)->daddr, skb->csum)) { |
1434 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1469 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1435 | return 0; | 1470 | return 0; |
1436 | } | 1471 | } |
1437 | } | 1472 | } |
1438 | 1473 | ||
1439 | skb->csum = ~csum_unfold(tcp_v6_check(tcp_hdr(skb), skb->len, | 1474 | skb->csum = ~csum_unfold(tcp_v6_check(skb->len, |
1440 | &ipv6_hdr(skb)->saddr, | 1475 | &ipv6_hdr(skb)->saddr, |
1441 | &ipv6_hdr(skb)->daddr, 0)); | 1476 | &ipv6_hdr(skb)->daddr, 0)); |
1442 | 1477 | ||
@@ -2062,6 +2097,8 @@ static struct inet6_protocol tcpv6_protocol = { | |||
2062 | .err_handler = tcp_v6_err, | 2097 | .err_handler = tcp_v6_err, |
2063 | .gso_send_check = tcp_v6_gso_send_check, | 2098 | .gso_send_check = tcp_v6_gso_send_check, |
2064 | .gso_segment = tcp_tso_segment, | 2099 | .gso_segment = tcp_tso_segment, |
2100 | .gro_receive = tcp6_gro_receive, | ||
2101 | .gro_complete = tcp6_gro_complete, | ||
2065 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, | 2102 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
2066 | }; | 2103 | }; |
2067 | 2104 | ||