diff options
Diffstat (limited to 'net/core/sock.c')
| -rw-r--r-- | net/core/sock.c | 219 | 
1 files changed, 158 insertions, 61 deletions
| diff --git a/net/core/sock.c b/net/core/sock.c index b714162213ae..6c4acf1f0220 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -566,7 +566,7 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)  }  EXPORT_SYMBOL(sk_dst_check); -static int sock_setbindtodevice_locked(struct sock *sk, int ifindex) +static int sock_bindtoindex_locked(struct sock *sk, int ifindex)  {  	int ret = -ENOPROTOOPT;  #ifdef CONFIG_NETDEVICES @@ -594,6 +594,20 @@ out:  	return ret;  } +int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk) +{ +	int ret; + +	if (lock_sk) +		lock_sock(sk); +	ret = sock_bindtoindex_locked(sk, ifindex); +	if (lock_sk) +		release_sock(sk); + +	return ret; +} +EXPORT_SYMBOL(sock_bindtoindex); +  static int sock_setbindtodevice(struct sock *sk, char __user *optval,  				int optlen)  { @@ -634,10 +648,7 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,  			goto out;  	} -	lock_sock(sk); -	ret = sock_setbindtodevice_locked(sk, index); -	release_sock(sk); - +	return sock_bindtoindex(sk, index, true);  out:  #endif @@ -712,6 +723,111 @@ bool sk_mc_loop(struct sock *sk)  }  EXPORT_SYMBOL(sk_mc_loop); +void sock_set_reuseaddr(struct sock *sk) +{ +	lock_sock(sk); +	sk->sk_reuse = SK_CAN_REUSE; +	release_sock(sk); +} +EXPORT_SYMBOL(sock_set_reuseaddr); + +void sock_set_reuseport(struct sock *sk) +{ +	lock_sock(sk); +	sk->sk_reuseport = true; +	release_sock(sk); +} +EXPORT_SYMBOL(sock_set_reuseport); + +void sock_no_linger(struct sock *sk) +{ +	lock_sock(sk); +	sk->sk_lingertime = 0; +	sock_set_flag(sk, SOCK_LINGER); +	release_sock(sk); +} +EXPORT_SYMBOL(sock_no_linger); + +void sock_set_priority(struct sock *sk, u32 priority) +{ +	lock_sock(sk); +	sk->sk_priority = priority; +	release_sock(sk); +} +EXPORT_SYMBOL(sock_set_priority); + +void sock_set_sndtimeo(struct sock *sk, s64 secs) +{ +	lock_sock(sk); +	if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) +		sk->sk_sndtimeo = secs * HZ; +	else +		sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; +	release_sock(sk); +} +EXPORT_SYMBOL(sock_set_sndtimeo); + +static void __sock_set_timestamps(struct sock *sk, bool val, bool new, bool ns) +{ +	if (val)  { +		sock_valbool_flag(sk, SOCK_TSTAMP_NEW, new); +		sock_valbool_flag(sk, SOCK_RCVTSTAMPNS, ns); +		sock_set_flag(sk, SOCK_RCVTSTAMP); +		sock_enable_timestamp(sk, SOCK_TIMESTAMP); +	} else { +		sock_reset_flag(sk, SOCK_RCVTSTAMP); +		sock_reset_flag(sk, SOCK_RCVTSTAMPNS); +		sock_reset_flag(sk, SOCK_TSTAMP_NEW); +	} +} + +void sock_enable_timestamps(struct sock *sk) +{ +	lock_sock(sk); +	__sock_set_timestamps(sk, true, false, true); +	release_sock(sk); +} +EXPORT_SYMBOL(sock_enable_timestamps); + +void sock_set_keepalive(struct sock *sk) +{ +	lock_sock(sk); +	if (sk->sk_prot->keepalive) +		sk->sk_prot->keepalive(sk, true); +	sock_valbool_flag(sk, SOCK_KEEPOPEN, true); +	release_sock(sk); +} +EXPORT_SYMBOL(sock_set_keepalive); + +static void __sock_set_rcvbuf(struct sock *sk, int val) +{ +	/* Ensure val * 2 fits into an int, to prevent max_t() from treating it +	 * as a negative value. +	 */ +	val = min_t(int, val, INT_MAX / 2); +	sk->sk_userlocks |= SOCK_RCVBUF_LOCK; + +	/* We double it on the way in to account for "struct sk_buff" etc. +	 * overhead.   Applications assume that the SO_RCVBUF setting they make +	 * will allow that much actual data to be received on that socket. +	 * +	 * Applications are unaware that "struct sk_buff" and other overheads +	 * allocate from the receive buffer during socket buffer allocation. +	 * +	 * And after considering the possible alternatives, returning the value +	 * we actually used in getsockopt is the most desirable behavior. +	 */ +	WRITE_ONCE(sk->sk_rcvbuf, max_t(int, val * 2, SOCK_MIN_RCVBUF)); +} + +void sock_set_rcvbuf(struct sock *sk, int val) +{ +	lock_sock(sk); +	__sock_set_rcvbuf(sk, val); +	release_sock(sk); +} +EXPORT_SYMBOL(sock_set_rcvbuf); +  /*   *	This is meant for all protocols to use and covers goings on   *	at the socket level. Everything here is generic. @@ -808,30 +924,7 @@ set_sndbuf:  		 * play 'guess the biggest size' games. RCVBUF/SNDBUF  		 * are treated in BSD as hints  		 */ -		val = min_t(u32, val, sysctl_rmem_max); -set_rcvbuf: -		/* Ensure val * 2 fits into an int, to prevent max_t() -		 * from treating it as a negative value. -		 */ -		val = min_t(int, val, INT_MAX / 2); -		sk->sk_userlocks |= SOCK_RCVBUF_LOCK; -		/* -		 * We double it on the way in to account for -		 * "struct sk_buff" etc. overhead.   Applications -		 * assume that the SO_RCVBUF setting they make will -		 * allow that much actual data to be received on that -		 * socket. -		 * -		 * Applications are unaware that "struct sk_buff" and -		 * other overheads allocate from the receive buffer -		 * during socket buffer allocation. -		 * -		 * And after considering the possible alternatives, -		 * returning the value we actually used in getsockopt -		 * is the most desirable behavior. -		 */ -		WRITE_ONCE(sk->sk_rcvbuf, -			   max_t(int, val * 2, SOCK_MIN_RCVBUF)); +		__sock_set_rcvbuf(sk, min_t(u32, val, sysctl_rmem_max));  		break;  	case SO_RCVBUFFORCE: @@ -843,9 +936,8 @@ set_rcvbuf:  		/* No negative values (to prevent underflow, as val will be  		 * multiplied by 2).  		 */ -		if (val < 0) -			val = 0; -		goto set_rcvbuf; +		__sock_set_rcvbuf(sk, max(val, 0)); +		break;  	case SO_KEEPALIVE:  		if (sk->sk_prot->keepalive) @@ -903,28 +995,17 @@ set_rcvbuf:  		break;  	case SO_TIMESTAMP_OLD: +		__sock_set_timestamps(sk, valbool, false, false); +		break;  	case SO_TIMESTAMP_NEW: +		__sock_set_timestamps(sk, valbool, true, false); +		break;  	case SO_TIMESTAMPNS_OLD: +		__sock_set_timestamps(sk, valbool, false, true); +		break;  	case SO_TIMESTAMPNS_NEW: -		if (valbool)  { -			if (optname == SO_TIMESTAMP_NEW || optname == SO_TIMESTAMPNS_NEW) -				sock_set_flag(sk, SOCK_TSTAMP_NEW); -			else -				sock_reset_flag(sk, SOCK_TSTAMP_NEW); - -			if (optname == SO_TIMESTAMP_OLD || optname == SO_TIMESTAMP_NEW) -				sock_reset_flag(sk, SOCK_RCVTSTAMPNS); -			else -				sock_set_flag(sk, SOCK_RCVTSTAMPNS); -			sock_set_flag(sk, SOCK_RCVTSTAMP); -			sock_enable_timestamp(sk, SOCK_TIMESTAMP); -		} else { -			sock_reset_flag(sk, SOCK_RCVTSTAMP); -			sock_reset_flag(sk, SOCK_RCVTSTAMPNS); -			sock_reset_flag(sk, SOCK_TSTAMP_NEW); -		} +		__sock_set_timestamps(sk, valbool, true, true);  		break; -  	case SO_TIMESTAMPING_NEW:  		sock_set_flag(sk, SOCK_TSTAMP_NEW);  		/* fall through */ @@ -1152,27 +1233,35 @@ set_rcvbuf:  		break;  	case SO_TXTIME: -		if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { -			ret = -EPERM; -		} else if (optlen != sizeof(struct sock_txtime)) { +		if (optlen != sizeof(struct sock_txtime)) {  			ret = -EINVAL; +			break;  		} else if (copy_from_user(&sk_txtime, optval,  			   sizeof(struct sock_txtime))) {  			ret = -EFAULT; +			break;  		} else if (sk_txtime.flags & ~SOF_TXTIME_FLAGS_MASK) {  			ret = -EINVAL; -		} else { -			sock_valbool_flag(sk, SOCK_TXTIME, true); -			sk->sk_clockid = sk_txtime.clockid; -			sk->sk_txtime_deadline_mode = -				!!(sk_txtime.flags & SOF_TXTIME_DEADLINE_MODE); -			sk->sk_txtime_report_errors = -				!!(sk_txtime.flags & SOF_TXTIME_REPORT_ERRORS); +			break; +		} +		/* CLOCK_MONOTONIC is only used by sch_fq, and this packet +		 * scheduler has enough safe guards. +		 */ +		if (sk_txtime.clockid != CLOCK_MONOTONIC && +		    !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { +			ret = -EPERM; +			break;  		} +		sock_valbool_flag(sk, SOCK_TXTIME, true); +		sk->sk_clockid = sk_txtime.clockid; +		sk->sk_txtime_deadline_mode = +			!!(sk_txtime.flags & SOF_TXTIME_DEADLINE_MODE); +		sk->sk_txtime_report_errors = +			!!(sk_txtime.flags & SOF_TXTIME_REPORT_ERRORS);  		break;  	case SO_BINDTOIFINDEX: -		ret = sock_setbindtodevice_locked(sk, val); +		ret = sock_bindtoindex_locked(sk, val);  		break;  	default: @@ -3625,3 +3714,11 @@ bool sk_busy_loop_end(void *p, unsigned long start_time)  }  EXPORT_SYMBOL(sk_busy_loop_end);  #endif /* CONFIG_NET_RX_BUSY_POLL */ + +int sock_bind_add(struct sock *sk, struct sockaddr *addr, int addr_len) +{ +	if (!sk->sk_prot->bind_add) +		return -EOPNOTSUPP; +	return sk->sk_prot->bind_add(sk, addr, addr_len); +} +EXPORT_SYMBOL(sock_bind_add); | 
