diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2006-07-30 23:19:33 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-08-02 16:38:14 -0400 |
commit | 497c615abad7ee81994dd592194535aea2aad617 (patch) | |
tree | 86bb23e49071ea1bc867232d576b2d4ece31eb4d | |
parent | 679e898a4742d4a4a47430b67fd68a789a73dcfd (diff) |
[IPV6]: Audit all ip6_dst_lookup/ip6_dst_store calls
The current users of ip6_dst_lookup can be divided into two classes:
1) The caller holds no locks and is in user-context (UDP).
2) The caller does not want to lookup the dst cache at all.
The second class covers everyone except UDP because most people do
the cache lookup directly before calling ip6_dst_lookup. This patch
adds ip6_sk_dst_lookup for the first class.
Similarly ip6_dst_store users can be divded into those that need to
take the socket dst lock and those that don't. This patch adds
__ip6_dst_store for those (everyone except UDP/datagram) that don't
need an extra lock.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ip6_route.h | 12 | ||||
-rw-r--r-- | include/net/ipv6.h | 3 | ||||
-rw-r--r-- | net/dccp/ipv6.c | 4 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 2 | ||||
-rw-r--r-- | net/ipv6/inet6_connection_sock.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 120 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 4 | ||||
-rw-r--r-- | net/ipv6/udp.c | 2 |
8 files changed, 100 insertions, 49 deletions
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index ab29dafb1a6a..96b0e66406ec 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h | |||
@@ -139,16 +139,22 @@ extern rwlock_t rt6_lock; | |||
139 | /* | 139 | /* |
140 | * Store a destination cache entry in a socket | 140 | * Store a destination cache entry in a socket |
141 | */ | 141 | */ |
142 | static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, | 142 | static inline void __ip6_dst_store(struct sock *sk, struct dst_entry *dst, |
143 | struct in6_addr *daddr) | 143 | struct in6_addr *daddr) |
144 | { | 144 | { |
145 | struct ipv6_pinfo *np = inet6_sk(sk); | 145 | struct ipv6_pinfo *np = inet6_sk(sk); |
146 | struct rt6_info *rt = (struct rt6_info *) dst; | 146 | struct rt6_info *rt = (struct rt6_info *) dst; |
147 | 147 | ||
148 | write_lock(&sk->sk_dst_lock); | ||
149 | sk_setup_caps(sk, dst); | 148 | sk_setup_caps(sk, dst); |
150 | np->daddr_cache = daddr; | 149 | np->daddr_cache = daddr; |
151 | np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; | 150 | np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; |
151 | } | ||
152 | |||
153 | static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, | ||
154 | struct in6_addr *daddr) | ||
155 | { | ||
156 | write_lock(&sk->sk_dst_lock); | ||
157 | __ip6_dst_store(sk, dst, daddr); | ||
152 | write_unlock(&sk->sk_dst_lock); | 158 | write_unlock(&sk->sk_dst_lock); |
153 | } | 159 | } |
154 | 160 | ||
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index a8fdf7970b37..ece7e8a84ffd 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -468,6 +468,9 @@ extern void ip6_flush_pending_frames(struct sock *sk); | |||
468 | extern int ip6_dst_lookup(struct sock *sk, | 468 | extern int ip6_dst_lookup(struct sock *sk, |
469 | struct dst_entry **dst, | 469 | struct dst_entry **dst, |
470 | struct flowi *fl); | 470 | struct flowi *fl); |
471 | extern int ip6_sk_dst_lookup(struct sock *sk, | ||
472 | struct dst_entry **dst, | ||
473 | struct flowi *fl); | ||
471 | 474 | ||
472 | /* | 475 | /* |
473 | * skb processing functions | 476 | * skb processing functions |
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 9f3d4d7cd0bf..610c722ac27f 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -230,7 +230,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
230 | ipv6_addr_copy(&np->saddr, saddr); | 230 | ipv6_addr_copy(&np->saddr, saddr); |
231 | inet->rcv_saddr = LOOPBACK4_IPV6; | 231 | inet->rcv_saddr = LOOPBACK4_IPV6; |
232 | 232 | ||
233 | ip6_dst_store(sk, dst, NULL); | 233 | __ip6_dst_store(sk, dst, NULL); |
234 | 234 | ||
235 | icsk->icsk_ext_hdr_len = 0; | 235 | icsk->icsk_ext_hdr_len = 0; |
236 | if (np->opt != NULL) | 236 | if (np->opt != NULL) |
@@ -863,7 +863,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | |||
863 | * comment in that function for the gory details. -acme | 863 | * comment in that function for the gory details. -acme |
864 | */ | 864 | */ |
865 | 865 | ||
866 | ip6_dst_store(newsk, dst, NULL); | 866 | __ip6_dst_store(newsk, dst, NULL); |
867 | newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM | | 867 | newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM | |
868 | NETIF_F_TSO); | 868 | NETIF_F_TSO); |
869 | newdp6 = (struct dccp6_sock *)newsk; | 869 | newdp6 = (struct dccp6_sock *)newsk; |
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 5a0ba58b86cc..ac85e9c532c2 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -658,7 +658,7 @@ int inet6_sk_rebuild_header(struct sock *sk) | |||
658 | return err; | 658 | return err; |
659 | } | 659 | } |
660 | 660 | ||
661 | ip6_dst_store(sk, dst, NULL); | 661 | __ip6_dst_store(sk, dst, NULL); |
662 | } | 662 | } |
663 | 663 | ||
664 | return 0; | 664 | return 0; |
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 5c950cc79d80..bf491077b822 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c | |||
@@ -185,7 +185,7 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok) | |||
185 | return err; | 185 | return err; |
186 | } | 186 | } |
187 | 187 | ||
188 | ip6_dst_store(sk, dst, NULL); | 188 | __ip6_dst_store(sk, dst, NULL); |
189 | } | 189 | } |
190 | 190 | ||
191 | skb->dst = dst_clone(dst); | 191 | skb->dst = dst_clone(dst); |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 3bc74ce78800..5e74a37695f7 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -723,48 +723,51 @@ fail: | |||
723 | return err; | 723 | return err; |
724 | } | 724 | } |
725 | 725 | ||
726 | int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) | 726 | static struct dst_entry *ip6_sk_dst_check(struct sock *sk, |
727 | struct dst_entry *dst, | ||
728 | struct flowi *fl) | ||
727 | { | 729 | { |
728 | int err = 0; | 730 | struct ipv6_pinfo *np = inet6_sk(sk); |
731 | struct rt6_info *rt = (struct rt6_info *)dst; | ||
729 | 732 | ||
730 | *dst = NULL; | 733 | if (!dst) |
731 | if (sk) { | 734 | goto out; |
732 | struct ipv6_pinfo *np = inet6_sk(sk); | 735 | |
733 | 736 | /* Yes, checking route validity in not connected | |
734 | *dst = sk_dst_check(sk, np->dst_cookie); | 737 | * case is not very simple. Take into account, |
735 | if (*dst) { | 738 | * that we do not support routing by source, TOS, |
736 | struct rt6_info *rt = (struct rt6_info*)*dst; | 739 | * and MSG_DONTROUTE --ANK (980726) |
737 | 740 | * | |
738 | /* Yes, checking route validity in not connected | 741 | * 1. If route was host route, check that |
739 | * case is not very simple. Take into account, | 742 | * cached destination is current. |
740 | * that we do not support routing by source, TOS, | 743 | * If it is network route, we still may |
741 | * and MSG_DONTROUTE --ANK (980726) | 744 | * check its validity using saved pointer |
742 | * | 745 | * to the last used address: daddr_cache. |
743 | * 1. If route was host route, check that | 746 | * We do not want to save whole address now, |
744 | * cached destination is current. | 747 | * (because main consumer of this service |
745 | * If it is network route, we still may | 748 | * is tcp, which has not this problem), |
746 | * check its validity using saved pointer | 749 | * so that the last trick works only on connected |
747 | * to the last used address: daddr_cache. | 750 | * sockets. |
748 | * We do not want to save whole address now, | 751 | * 2. oif also should be the same. |
749 | * (because main consumer of this service | 752 | */ |
750 | * is tcp, which has not this problem), | 753 | if (((rt->rt6i_dst.plen != 128 || |
751 | * so that the last trick works only on connected | 754 | !ipv6_addr_equal(&fl->fl6_dst, &rt->rt6i_dst.addr)) |
752 | * sockets. | 755 | && (np->daddr_cache == NULL || |
753 | * 2. oif also should be the same. | 756 | !ipv6_addr_equal(&fl->fl6_dst, np->daddr_cache))) |
754 | */ | 757 | || (fl->oif && fl->oif != dst->dev->ifindex)) { |
755 | if (((rt->rt6i_dst.plen != 128 || | 758 | dst_release(dst); |
756 | !ipv6_addr_equal(&fl->fl6_dst, | 759 | dst = NULL; |
757 | &rt->rt6i_dst.addr)) | ||
758 | && (np->daddr_cache == NULL || | ||
759 | !ipv6_addr_equal(&fl->fl6_dst, | ||
760 | np->daddr_cache))) | ||
761 | || (fl->oif && fl->oif != (*dst)->dev->ifindex)) { | ||
762 | dst_release(*dst); | ||
763 | *dst = NULL; | ||
764 | } | ||
765 | } | ||
766 | } | 760 | } |
767 | 761 | ||
762 | out: | ||
763 | return dst; | ||
764 | } | ||
765 | |||
766 | static int ip6_dst_lookup_tail(struct sock *sk, | ||
767 | struct dst_entry **dst, struct flowi *fl) | ||
768 | { | ||
769 | int err; | ||
770 | |||
768 | if (*dst == NULL) | 771 | if (*dst == NULL) |
769 | *dst = ip6_route_output(sk, fl); | 772 | *dst = ip6_route_output(sk, fl); |
770 | 773 | ||
@@ -773,7 +776,6 @@ int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) | |||
773 | 776 | ||
774 | if (ipv6_addr_any(&fl->fl6_src)) { | 777 | if (ipv6_addr_any(&fl->fl6_src)) { |
775 | err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src); | 778 | err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src); |
776 | |||
777 | if (err) | 779 | if (err) |
778 | goto out_err_release; | 780 | goto out_err_release; |
779 | } | 781 | } |
@@ -786,8 +788,48 @@ out_err_release: | |||
786 | return err; | 788 | return err; |
787 | } | 789 | } |
788 | 790 | ||
791 | /** | ||
792 | * ip6_dst_lookup - perform route lookup on flow | ||
793 | * @sk: socket which provides route info | ||
794 | * @dst: pointer to dst_entry * for result | ||
795 | * @fl: flow to lookup | ||
796 | * | ||
797 | * This function performs a route lookup on the given flow. | ||
798 | * | ||
799 | * It returns zero on success, or a standard errno code on error. | ||
800 | */ | ||
801 | int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) | ||
802 | { | ||
803 | *dst = NULL; | ||
804 | return ip6_dst_lookup_tail(sk, dst, fl); | ||
805 | } | ||
789 | EXPORT_SYMBOL_GPL(ip6_dst_lookup); | 806 | EXPORT_SYMBOL_GPL(ip6_dst_lookup); |
790 | 807 | ||
808 | /** | ||
809 | * ip6_sk_dst_lookup - perform socket cached route lookup on flow | ||
810 | * @sk: socket which provides the dst cache and route info | ||
811 | * @dst: pointer to dst_entry * for result | ||
812 | * @fl: flow to lookup | ||
813 | * | ||
814 | * This function performs a route lookup on the given flow with the | ||
815 | * possibility of using the cached route in the socket if it is valid. | ||
816 | * It will take the socket dst lock when operating on the dst cache. | ||
817 | * As a result, this function can only be used in process context. | ||
818 | * | ||
819 | * It returns zero on success, or a standard errno code on error. | ||
820 | */ | ||
821 | int ip6_sk_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) | ||
822 | { | ||
823 | *dst = NULL; | ||
824 | if (sk) { | ||
825 | *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); | ||
826 | *dst = ip6_sk_dst_check(sk, *dst, fl); | ||
827 | } | ||
828 | |||
829 | return ip6_dst_lookup_tail(sk, dst, fl); | ||
830 | } | ||
831 | EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup); | ||
832 | |||
791 | static inline int ip6_ufo_append_data(struct sock *sk, | 833 | static inline int ip6_ufo_append_data(struct sock *sk, |
792 | int getfrag(void *from, char *to, int offset, int len, | 834 | int getfrag(void *from, char *to, int offset, int len, |
793 | int odd, struct sk_buff *skb), | 835 | int odd, struct sk_buff *skb), |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 923989d0520d..b76fd7fba5f5 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -270,7 +270,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
270 | inet->rcv_saddr = LOOPBACK4_IPV6; | 270 | inet->rcv_saddr = LOOPBACK4_IPV6; |
271 | 271 | ||
272 | sk->sk_gso_type = SKB_GSO_TCPV6; | 272 | sk->sk_gso_type = SKB_GSO_TCPV6; |
273 | ip6_dst_store(sk, dst, NULL); | 273 | __ip6_dst_store(sk, dst, NULL); |
274 | 274 | ||
275 | icsk->icsk_ext_hdr_len = 0; | 275 | icsk->icsk_ext_hdr_len = 0; |
276 | if (np->opt) | 276 | if (np->opt) |
@@ -947,7 +947,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
947 | */ | 947 | */ |
948 | 948 | ||
949 | sk->sk_gso_type = SKB_GSO_TCPV6; | 949 | sk->sk_gso_type = SKB_GSO_TCPV6; |
950 | ip6_dst_store(newsk, dst, NULL); | 950 | __ip6_dst_store(newsk, dst, NULL); |
951 | 951 | ||
952 | newtcp6sk = (struct tcp6_sock *)newsk; | 952 | newtcp6sk = (struct tcp6_sock *)newsk; |
953 | inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; | 953 | inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index ccc57f434cd3..3d54f246411e 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -782,7 +782,7 @@ do_udp_sendmsg: | |||
782 | connected = 0; | 782 | connected = 0; |
783 | } | 783 | } |
784 | 784 | ||
785 | err = ip6_dst_lookup(sk, &dst, fl); | 785 | err = ip6_sk_dst_lookup(sk, &dst, fl); |
786 | if (err) | 786 | if (err) |
787 | goto out; | 787 | goto out; |
788 | if (final_p) | 788 | if (final_p) |