diff options
Diffstat (limited to 'net/core/sock.c')
| -rw-r--r-- | net/core/sock.c | 110 | 
1 files changed, 97 insertions, 13 deletions
diff --git a/net/core/sock.c b/net/core/sock.c index 8a146cfcc36..b261a797774 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -186,8 +186,10 @@ void mem_cgroup_sockets_destroy(struct mem_cgroup *memcg)  static struct lock_class_key af_family_keys[AF_MAX];  static struct lock_class_key af_family_slock_keys[AF_MAX]; +#if defined(CONFIG_MEMCG_KMEM)  struct static_key memcg_socket_limit_enabled;  EXPORT_SYMBOL(memcg_socket_limit_enabled); +#endif  /*   * Make lock validator output more readable. (we pre-construct these @@ -505,7 +507,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 +518,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 +565,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 +645,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; @@ -611,6 +667,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname,  	case SO_REUSEADDR:  		sk->sk_reuse = (valbool ? SK_CAN_REUSE : SK_NO_REUSE);  		break; +	case SO_REUSEPORT: +		sk->sk_reuseport = valbool; +		break;  	case SO_TYPE:  	case SO_PROTOCOL:  	case SO_DOMAIN: @@ -696,7 +755,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; @@ -806,6 +866,13 @@ set_rcvbuf:  		ret = sk_detach_filter(sk);  		break; +	case SO_LOCK_FILTER: +		if (sock_flag(sk, SOCK_FILTER_LOCKED) && !valbool) +			ret = -EPERM; +		else +			sock_valbool_flag(sk, SOCK_FILTER_LOCKED, valbool); +		break; +  	case SO_PASSSEC:  		if (valbool)  			set_bit(SOCK_PASSSEC, &sock->flags); @@ -813,7 +880,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; @@ -910,6 +977,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,  		v.val = sk->sk_reuse;  		break; +	case SO_REUSEPORT: +		v.val = sk->sk_reuseport; +		break; +  	case SO_KEEPALIVE:  		v.val = sock_flag(sk, SOCK_KEEPOPEN);  		break; @@ -1074,6 +1145,21 @@ 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; + +	case SO_LOCK_FILTER: +		v.val = sock_flag(sk, SOCK_FILTER_LOCKED); +		break; +  	default:  		return -ENOPROTOOPT;  	} @@ -1214,13 +1300,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 +1347,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);  	} @@ -2148,7 +2232,7 @@ EXPORT_SYMBOL(sk_reset_timer);  void sk_stop_timer(struct sock *sk, struct timer_list* timer)  { -	if (timer_pending(timer) && del_timer(timer)) +	if (del_timer(timer))  		__sock_put(sk);  }  EXPORT_SYMBOL(sk_stop_timer); @@ -2754,7 +2838,7 @@ static const struct file_operations proto_seq_fops = {  static __net_init int proto_init_net(struct net *net)  { -	if (!proc_net_fops_create(net, "protocols", S_IRUGO, &proto_seq_fops)) +	if (!proc_create("protocols", S_IRUGO, net->proc_net, &proto_seq_fops))  		return -ENOMEM;  	return 0; @@ -2762,7 +2846,7 @@ static __net_init int proto_init_net(struct net *net)  static __net_exit void proto_exit_net(struct net *net)  { -	proc_net_remove(net, "protocols"); +	remove_proc_entry("protocols", net->proc_net);  }  |