aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
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