aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2006-07-30 23:19:33 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-08-02 16:38:14 -0400
commit497c615abad7ee81994dd592194535aea2aad617 (patch)
tree86bb23e49071ea1bc867232d576b2d4ece31eb4d
parent679e898a4742d4a4a47430b67fd68a789a73dcfd (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.h12
-rw-r--r--include/net/ipv6.h3
-rw-r--r--net/dccp/ipv6.c4
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/inet6_connection_sock.c2
-rw-r--r--net/ipv6/ip6_output.c120
-rw-r--r--net/ipv6/tcp_ipv6.c4
-rw-r--r--net/ipv6/udp.c2
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 */
142static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, 142static 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
153static 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);
468extern int ip6_dst_lookup(struct sock *sk, 468extern 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);
471extern 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
726int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) 726static 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
762out:
763 return dst;
764}
765
766static 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 */
801int 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}
789EXPORT_SYMBOL_GPL(ip6_dst_lookup); 806EXPORT_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 */
821int 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}
831EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup);
832
791static inline int ip6_ufo_append_data(struct sock *sk, 833static 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)