Merge tag 'dm-3.8-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-dm
[firefly-linux-kernel-4.4.55.git] / net / core / sock.c
index 8a146cfcc366fbead42d31d882893a74ec8dcf4e..a692ef49c9bb9b2d996f0538049a135a93c26812 100644 (file)
@@ -505,7 +505,8 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
 }
 EXPORT_SYMBOL(sk_dst_check);
 
-static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
+static int sock_setbindtodevice(struct sock *sk, char __user *optval,
+                               int optlen)
 {
        int ret = -ENOPROTOOPT;
 #ifdef CONFIG_NETDEVICES
@@ -515,7 +516,7 @@ static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
 
        /* Sorry... */
        ret = -EPERM;
-       if (!capable(CAP_NET_RAW))
+       if (!ns_capable(net->user_ns, CAP_NET_RAW))
                goto out;
 
        ret = -EINVAL;
@@ -562,6 +563,59 @@ out:
        return ret;
 }
 
+static int sock_getbindtodevice(struct sock *sk, char __user *optval,
+                               int __user *optlen, int len)
+{
+       int ret = -ENOPROTOOPT;
+#ifdef CONFIG_NETDEVICES
+       struct net *net = sock_net(sk);
+       struct net_device *dev;
+       char devname[IFNAMSIZ];
+       unsigned seq;
+
+       if (sk->sk_bound_dev_if == 0) {
+               len = 0;
+               goto zero;
+       }
+
+       ret = -EINVAL;
+       if (len < IFNAMSIZ)
+               goto out;
+
+retry:
+       seq = read_seqbegin(&devnet_rename_seq);
+       rcu_read_lock();
+       dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
+       ret = -ENODEV;
+       if (!dev) {
+               rcu_read_unlock();
+               goto out;
+       }
+
+       strcpy(devname, dev->name);
+       rcu_read_unlock();
+       if (read_seqretry(&devnet_rename_seq, seq))
+               goto retry;
+
+       len = strlen(devname) + 1;
+
+       ret = -EFAULT;
+       if (copy_to_user(optval, devname, len))
+               goto out;
+
+zero:
+       ret = -EFAULT;
+       if (put_user(len, optlen))
+               goto out;
+
+       ret = 0;
+
+out:
+#endif
+
+       return ret;
+}
+
 static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool)
 {
        if (valbool)
@@ -589,7 +643,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
         */
 
        if (optname == SO_BINDTODEVICE)
-               return sock_bindtodevice(sk, optval, optlen);
+               return sock_setbindtodevice(sk, optval, optlen);
 
        if (optlen < sizeof(int))
                return -EINVAL;
@@ -696,7 +750,8 @@ set_rcvbuf:
                break;
 
        case SO_PRIORITY:
-               if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
+               if ((val >= 0 && val <= 6) ||
+                   ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
                        sk->sk_priority = val;
                else
                        ret = -EPERM;
@@ -813,7 +868,7 @@ set_rcvbuf:
                        clear_bit(SOCK_PASSSEC, &sock->flags);
                break;
        case SO_MARK:
-               if (!capable(CAP_NET_ADMIN))
+               if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
                        ret = -EPERM;
                else
                        sk->sk_mark = val;
@@ -1074,6 +1129,17 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
        case SO_NOFCS:
                v.val = sock_flag(sk, SOCK_NOFCS);
                break;
+
+       case SO_BINDTODEVICE:
+               return sock_getbindtodevice(sk, optval, optlen, len);
+
+       case SO_GET_FILTER:
+               len = sk_get_filter(sk, (struct sock_filter __user *)optval, len);
+               if (len < 0)
+                       return len;
+
+               goto lenout;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -1214,13 +1280,11 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
 
 #ifdef CONFIG_CGROUPS
 #if IS_ENABLED(CONFIG_NET_CLS_CGROUP)
-void sock_update_classid(struct sock *sk)
+void sock_update_classid(struct sock *sk, struct task_struct *task)
 {
        u32 classid;
 
-       rcu_read_lock();  /* doing current task, which cannot vanish. */
-       classid = task_cls_classid(current);
-       rcu_read_unlock();
+       classid = task_cls_classid(task);
        if (classid != sk->sk_classid)
                sk->sk_classid = classid;
 }
@@ -1263,7 +1327,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
                sock_net_set(sk, get_net(net));
                atomic_set(&sk->sk_wmem_alloc, 1);
 
-               sock_update_classid(sk);
+               sock_update_classid(sk, current);
                sock_update_netprioidx(sk, current);
        }