diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 31 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 35 |
2 files changed, 52 insertions, 14 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 5cd99271d3a6..5171709199f4 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -536,12 +536,25 @@ out: | |||
536 | * Socket option code for IP. This is the end of the line after any | 536 | * Socket option code for IP. This is the end of the line after any |
537 | * TCP,UDP etc options on an IP socket. | 537 | * TCP,UDP etc options on an IP socket. |
538 | */ | 538 | */ |
539 | static bool setsockopt_needs_rtnl(int optname) | ||
540 | { | ||
541 | switch (optname) { | ||
542 | case IP_ADD_MEMBERSHIP: | ||
543 | case IP_ADD_SOURCE_MEMBERSHIP: | ||
544 | case IP_DROP_MEMBERSHIP: | ||
545 | case MCAST_JOIN_GROUP: | ||
546 | case MCAST_LEAVE_GROUP: | ||
547 | return true; | ||
548 | } | ||
549 | return false; | ||
550 | } | ||
539 | 551 | ||
540 | static int do_ip_setsockopt(struct sock *sk, int level, | 552 | static int do_ip_setsockopt(struct sock *sk, int level, |
541 | int optname, char __user *optval, unsigned int optlen) | 553 | int optname, char __user *optval, unsigned int optlen) |
542 | { | 554 | { |
543 | struct inet_sock *inet = inet_sk(sk); | 555 | struct inet_sock *inet = inet_sk(sk); |
544 | int val = 0, err; | 556 | int val = 0, err; |
557 | bool needs_rtnl = setsockopt_needs_rtnl(optname); | ||
545 | 558 | ||
546 | switch (optname) { | 559 | switch (optname) { |
547 | case IP_PKTINFO: | 560 | case IP_PKTINFO: |
@@ -584,6 +597,8 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
584 | return ip_mroute_setsockopt(sk, optname, optval, optlen); | 597 | return ip_mroute_setsockopt(sk, optname, optval, optlen); |
585 | 598 | ||
586 | err = 0; | 599 | err = 0; |
600 | if (needs_rtnl) | ||
601 | rtnl_lock(); | ||
587 | lock_sock(sk); | 602 | lock_sock(sk); |
588 | 603 | ||
589 | switch (optname) { | 604 | switch (optname) { |
@@ -846,9 +861,9 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
846 | } | 861 | } |
847 | 862 | ||
848 | if (optname == IP_ADD_MEMBERSHIP) | 863 | if (optname == IP_ADD_MEMBERSHIP) |
849 | err = ip_mc_join_group(sk, &mreq); | 864 | err = __ip_mc_join_group(sk, &mreq); |
850 | else | 865 | else |
851 | err = ip_mc_leave_group(sk, &mreq); | 866 | err = __ip_mc_leave_group(sk, &mreq); |
852 | break; | 867 | break; |
853 | } | 868 | } |
854 | case IP_MSFILTER: | 869 | case IP_MSFILTER: |
@@ -913,7 +928,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
913 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; | 928 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; |
914 | mreq.imr_address.s_addr = mreqs.imr_interface; | 929 | mreq.imr_address.s_addr = mreqs.imr_interface; |
915 | mreq.imr_ifindex = 0; | 930 | mreq.imr_ifindex = 0; |
916 | err = ip_mc_join_group(sk, &mreq); | 931 | err = __ip_mc_join_group(sk, &mreq); |
917 | if (err && err != -EADDRINUSE) | 932 | if (err && err != -EADDRINUSE) |
918 | break; | 933 | break; |
919 | omode = MCAST_INCLUDE; | 934 | omode = MCAST_INCLUDE; |
@@ -945,9 +960,9 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
945 | mreq.imr_ifindex = greq.gr_interface; | 960 | mreq.imr_ifindex = greq.gr_interface; |
946 | 961 | ||
947 | if (optname == MCAST_JOIN_GROUP) | 962 | if (optname == MCAST_JOIN_GROUP) |
948 | err = ip_mc_join_group(sk, &mreq); | 963 | err = __ip_mc_join_group(sk, &mreq); |
949 | else | 964 | else |
950 | err = ip_mc_leave_group(sk, &mreq); | 965 | err = __ip_mc_leave_group(sk, &mreq); |
951 | break; | 966 | break; |
952 | } | 967 | } |
953 | case MCAST_JOIN_SOURCE_GROUP: | 968 | case MCAST_JOIN_SOURCE_GROUP: |
@@ -990,7 +1005,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
990 | mreq.imr_multiaddr = psin->sin_addr; | 1005 | mreq.imr_multiaddr = psin->sin_addr; |
991 | mreq.imr_address.s_addr = 0; | 1006 | mreq.imr_address.s_addr = 0; |
992 | mreq.imr_ifindex = greqs.gsr_interface; | 1007 | mreq.imr_ifindex = greqs.gsr_interface; |
993 | err = ip_mc_join_group(sk, &mreq); | 1008 | err = __ip_mc_join_group(sk, &mreq); |
994 | if (err && err != -EADDRINUSE) | 1009 | if (err && err != -EADDRINUSE) |
995 | break; | 1010 | break; |
996 | greqs.gsr_interface = mreq.imr_ifindex; | 1011 | greqs.gsr_interface = mreq.imr_ifindex; |
@@ -1118,10 +1133,14 @@ mc_msf_out: | |||
1118 | break; | 1133 | break; |
1119 | } | 1134 | } |
1120 | release_sock(sk); | 1135 | release_sock(sk); |
1136 | if (needs_rtnl) | ||
1137 | rtnl_unlock(); | ||
1121 | return err; | 1138 | return err; |
1122 | 1139 | ||
1123 | e_inval: | 1140 | e_inval: |
1124 | release_sock(sk); | 1141 | release_sock(sk); |
1142 | if (needs_rtnl) | ||
1143 | rtnl_unlock(); | ||
1125 | return -EINVAL; | 1144 | return -EINVAL; |
1126 | } | 1145 | } |
1127 | 1146 | ||
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 | ||
120 | static 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 | |||
120 | static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | 132 | static 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 | ||
843 | e_inval: | 860 | e_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 | ||