Merge branch 'sched-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / net / ipv6 / ipv6_sockglue.c
index 8d766d9100cba408525faf5818b7b0c6b6bc543c..63e6956917c9cf20ee74968de3a7f03c1b48c849 100644 (file)
@@ -85,7 +85,7 @@ int ip6_ra_control(struct sock *sk, int sel)
                        return 0;
                }
        }
-       if (new_ra == NULL) {
+       if (!new_ra) {
                write_unlock_bh(&ip6_ra_lock);
                return -ENOBUFS;
        }
@@ -117,6 +117,25 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
        return opt;
 }
 
+static bool setsockopt_needs_rtnl(int optname)
+{
+       switch (optname) {
+       case IPV6_ADD_MEMBERSHIP:
+       case IPV6_DROP_MEMBERSHIP:
+       case IPV6_JOIN_ANYCAST:
+       case IPV6_LEAVE_ANYCAST:
+       case MCAST_JOIN_GROUP:
+       case MCAST_LEAVE_GROUP:
+       case MCAST_JOIN_SOURCE_GROUP:
+       case MCAST_LEAVE_SOURCE_GROUP:
+       case MCAST_BLOCK_SOURCE:
+       case MCAST_UNBLOCK_SOURCE:
+       case MCAST_MSFILTER:
+               return true;
+       }
+       return false;
+}
+
 static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, unsigned int optlen)
 {
@@ -124,8 +143,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
        struct net *net = sock_net(sk);
        int val, valbool;
        int retv = -ENOPROTOOPT;
+       bool needs_rtnl = setsockopt_needs_rtnl(optname);
 
-       if (optval == NULL)
+       if (!optval)
                val = 0;
        else {
                if (optlen >= sizeof(int)) {
@@ -140,6 +160,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
        if (ip6_mroute_opt(optname))
                return ip6_mroute_setsockopt(sk, optname, optval, optlen);
 
+       if (needs_rtnl)
+               rtnl_lock();
        lock_sock(sk);
 
        switch (optname) {
@@ -370,7 +392,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                 */
                if (optlen == 0)
                        optval = NULL;
-               else if (optval == NULL)
+               else if (!optval)
                        goto e_inval;
                else if (optlen < sizeof(struct ipv6_opt_hdr) ||
                         optlen & 0x7 || optlen > 8 * 255)
@@ -421,7 +443,7 @@ sticky_done:
 
                if (optlen == 0)
                        goto e_inval;
-               else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL)
+               else if (optlen < sizeof(struct in6_pktinfo) || !optval)
                        goto e_inval;
 
                if (copy_from_user(&pkt, optval, sizeof(struct in6_pktinfo))) {
@@ -460,7 +482,7 @@ sticky_done:
 
                opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
                retv = -ENOBUFS;
-               if (opt == NULL)
+               if (!opt)
                        break;
 
                memset(opt, 0, sizeof(*opt));
@@ -624,10 +646,10 @@ done:
                psin6 = (struct sockaddr_in6 *)&greq.gr_group;
                if (optname == MCAST_JOIN_GROUP)
                        retv = ipv6_sock_mc_join(sk, greq.gr_interface,
-                               &psin6->sin6_addr);
+                                                &psin6->sin6_addr);
                else
                        retv = ipv6_sock_mc_drop(sk, greq.gr_interface,
-                               &psin6->sin6_addr);
+                                                &psin6->sin6_addr);
                break;
        }
        case MCAST_JOIN_SOURCE_GROUP:
@@ -660,7 +682,7 @@ done:
 
                        psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
                        retv = ipv6_sock_mc_join(sk, greqs.gsr_interface,
-                               &psin6->sin6_addr);
+                                                &psin6->sin6_addr);
                        /* prior join w/ different source is ok */
                        if (retv && retv != -EADDRINUSE)
                                break;
@@ -837,11 +859,15 @@ pref_skip_coa:
        }
 
        release_sock(sk);
+       if (needs_rtnl)
+               rtnl_unlock();
 
        return retv;
 
 e_inval:
        release_sock(sk);
+       if (needs_rtnl)
+               rtnl_unlock();
        return -EINVAL;
 }