diff options
Diffstat (limited to 'net/core/sock.c')
| -rw-r--r-- | net/core/sock.c | 84 | 
1 files changed, 74 insertions, 10 deletions
diff --git a/net/core/sock.c b/net/core/sock.c index 8a146cfcc36..bc131d41968 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -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_seqcount_begin(&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_seqcount_retry(&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);  	}  |