aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>2015-03-18 13:50:42 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-18 22:05:09 -0400
commitbaf606d9c9b12517e47e0d1370e8aa9f7323f210 (patch)
tree77bcd9602f772e50f509f8f28e0156aa7acdbf4c /net/ipv6
parentfdf9ef8999c0758bf622899e049f2be45ab1f4da (diff)
ipv4,ipv6: grab rtnl before locking the socket
There are some setsockopt operations in ipv4 and ipv6 that are grabbing rtnl after having grabbed the socket lock. Yet this makes it impossible to do operations that have to lock the socket when already within a rtnl protected scope, like ndo dev_open and dev_stop. We normally take coarse grained locks first but setsockopt inverted that. So this patch invert the lock logic for these operations and makes setsockopt grab rtnl if it will be needed prior to grabbing socket lock. Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ipv6_sockglue.c35
1 files changed, 27 insertions, 8 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 8d766d9100cb..f2b731df8d77 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -117,6 +117,18 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
117 return opt; 117 return opt;
118} 118}
119 119
120static bool setsockopt_needs_rtnl(int optname)
121{
122 switch (optname) {
123 case IPV6_ADD_MEMBERSHIP:
124 case IPV6_DROP_MEMBERSHIP:
125 case MCAST_JOIN_GROUP:
126 case MCAST_LEAVE_GROUP:
127 return true;
128 }
129 return false;
130}
131
120static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, 132static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
121 char __user *optval, unsigned int optlen) 133 char __user *optval, unsigned int optlen)
122{ 134{
@@ -124,6 +136,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
124 struct net *net = sock_net(sk); 136 struct net *net = sock_net(sk);
125 int val, valbool; 137 int val, valbool;
126 int retv = -ENOPROTOOPT; 138 int retv = -ENOPROTOOPT;
139 bool needs_rtnl = setsockopt_needs_rtnl(optname);
127 140
128 if (optval == NULL) 141 if (optval == NULL)
129 val = 0; 142 val = 0;
@@ -140,6 +153,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
140 if (ip6_mroute_opt(optname)) 153 if (ip6_mroute_opt(optname))
141 return ip6_mroute_setsockopt(sk, optname, optval, optlen); 154 return ip6_mroute_setsockopt(sk, optname, optval, optlen);
142 155
156 if (needs_rtnl)
157 rtnl_lock();
143 lock_sock(sk); 158 lock_sock(sk);
144 159
145 switch (optname) { 160 switch (optname) {
@@ -582,9 +597,9 @@ done:
582 break; 597 break;
583 598
584 if (optname == IPV6_ADD_MEMBERSHIP) 599 if (optname == IPV6_ADD_MEMBERSHIP)
585 retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); 600 retv = __ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
586 else 601 else
587 retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); 602 retv = __ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
588 break; 603 break;
589 } 604 }
590 case IPV6_JOIN_ANYCAST: 605 case IPV6_JOIN_ANYCAST:
@@ -623,11 +638,11 @@ done:
623 } 638 }
624 psin6 = (struct sockaddr_in6 *)&greq.gr_group; 639 psin6 = (struct sockaddr_in6 *)&greq.gr_group;
625 if (optname == MCAST_JOIN_GROUP) 640 if (optname == MCAST_JOIN_GROUP)
626 retv = ipv6_sock_mc_join(sk, greq.gr_interface, 641 retv = __ipv6_sock_mc_join(sk, greq.gr_interface,
627 &psin6->sin6_addr); 642 &psin6->sin6_addr);
628 else 643 else
629 retv = ipv6_sock_mc_drop(sk, greq.gr_interface, 644 retv = __ipv6_sock_mc_drop(sk, greq.gr_interface,
630 &psin6->sin6_addr); 645 &psin6->sin6_addr);
631 break; 646 break;
632 } 647 }
633 case MCAST_JOIN_SOURCE_GROUP: 648 case MCAST_JOIN_SOURCE_GROUP:
@@ -659,8 +674,8 @@ done:
659 struct sockaddr_in6 *psin6; 674 struct sockaddr_in6 *psin6;
660 675
661 psin6 = (struct sockaddr_in6 *)&greqs.gsr_group; 676 psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
662 retv = ipv6_sock_mc_join(sk, greqs.gsr_interface, 677 retv = __ipv6_sock_mc_join(sk, greqs.gsr_interface,
663 &psin6->sin6_addr); 678 &psin6->sin6_addr);
664 /* prior join w/ different source is ok */ 679 /* prior join w/ different source is ok */
665 if (retv && retv != -EADDRINUSE) 680 if (retv && retv != -EADDRINUSE)
666 break; 681 break;
@@ -837,11 +852,15 @@ pref_skip_coa:
837 } 852 }
838 853
839 release_sock(sk); 854 release_sock(sk);
855 if (needs_rtnl)
856 rtnl_unlock();
840 857
841 return retv; 858 return retv;
842 859
843e_inval: 860e_inval:
844 release_sock(sk); 861 release_sock(sk);
862 if (needs_rtnl)
863 rtnl_unlock();
845 return -EINVAL; 864 return -EINVAL;
846} 865}
847 866