Commit b0e9a2fe authored by Xin Long's avatar Xin Long Committed by David S. Miller

sctp: add support for SCTP_REUSE_PORT sockopt

This feature is actually already supported by sk->sk_reuse which can be
set by socket level opt SO_REUSEADDR. But it's not working exactly as
RFC6458 demands in section 8.1.27, like:

  - This option only supports one-to-one style SCTP sockets
  - This socket option must not be used after calling bind()
    or sctp_bindx().

Besides, SCTP_REUSE_PORT sockopt should be provided for user's programs.
Otherwise, the programs with SCTP_REUSE_PORT from other systems will not
work in linux.

To separate it from the socket level version, this patch adds 'reuse' in
sctp_sock and it works pretty much as sk->sk_reuse, but with some extra
setup limitations that are needed when it is being enabled.

"It should be noted that the behavior of the socket-level socket option
to reuse ports and/or addresses for SCTP sockets is unspecified", so it
leaves SO_REUSEADDR as is for the compatibility.

Note that the name SCTP_REUSE_PORT is somewhat confusing, as its
functionality is nearly identical to SO_REUSEADDR, but with some
extra restrictions. Here it uses 'reuse' in sctp_sock instead of
'reuseport'. As for sk->sk_reuseport support for SCTP, it will be
added in another patch.

Thanks to Neil to make this clear.

v1->v2:
  - add sctp_sk->reuse to separate it from the socket level version.
v2->v3:
  - improve changelog according to Marcelo's suggestion.
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 23c94d63
...@@ -220,6 +220,7 @@ struct sctp_sock { ...@@ -220,6 +220,7 @@ struct sctp_sock {
__u32 adaptation_ind; __u32 adaptation_ind;
__u32 pd_point; __u32 pd_point;
__u16 nodelay:1, __u16 nodelay:1,
reuse:1,
disable_fragments:1, disable_fragments:1,
v4mapped:1, v4mapped:1,
frag_interleave:1, frag_interleave:1,
......
...@@ -100,6 +100,7 @@ typedef __s32 sctp_assoc_t; ...@@ -100,6 +100,7 @@ typedef __s32 sctp_assoc_t;
#define SCTP_RECVNXTINFO 33 #define SCTP_RECVNXTINFO 33
#define SCTP_DEFAULT_SNDINFO 34 #define SCTP_DEFAULT_SNDINFO 34
#define SCTP_AUTH_DEACTIVATE_KEY 35 #define SCTP_AUTH_DEACTIVATE_KEY 35
#define SCTP_REUSE_PORT 36
/* Internal Socket Options. Some of the sctp library functions are /* Internal Socket Options. Some of the sctp library functions are
* implemented using these socket options. * implemented using these socket options.
......
...@@ -4170,6 +4170,28 @@ static int sctp_setsockopt_interleaving_supported(struct sock *sk, ...@@ -4170,6 +4170,28 @@ static int sctp_setsockopt_interleaving_supported(struct sock *sk,
return retval; return retval;
} }
static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval,
unsigned int optlen)
{
int val;
if (!sctp_style(sk, TCP))
return -EOPNOTSUPP;
if (sctp_sk(sk)->ep->base.bind_addr.port)
return -EFAULT;
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int __user *)optval))
return -EFAULT;
sctp_sk(sk)->reuse = !!val;
return 0;
}
/* API 6.2 setsockopt(), getsockopt() /* API 6.2 setsockopt(), getsockopt()
* *
* Applications use setsockopt() and getsockopt() to set or retrieve * Applications use setsockopt() and getsockopt() to set or retrieve
...@@ -4364,6 +4386,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -4364,6 +4386,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
retval = sctp_setsockopt_interleaving_supported(sk, optval, retval = sctp_setsockopt_interleaving_supported(sk, optval,
optlen); optlen);
break; break;
case SCTP_REUSE_PORT:
retval = sctp_setsockopt_reuse_port(sk, optval, optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
...@@ -7197,6 +7222,26 @@ static int sctp_getsockopt_interleaving_supported(struct sock *sk, int len, ...@@ -7197,6 +7222,26 @@ static int sctp_getsockopt_interleaving_supported(struct sock *sk, int len,
return retval; return retval;
} }
static int sctp_getsockopt_reuse_port(struct sock *sk, int len,
char __user *optval,
int __user *optlen)
{
int val;
if (len < sizeof(int))
return -EINVAL;
len = sizeof(int);
val = sctp_sk(sk)->reuse;
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
static int sctp_getsockopt(struct sock *sk, int level, int optname, static int sctp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen) char __user *optval, int __user *optlen)
{ {
...@@ -7392,6 +7437,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, ...@@ -7392,6 +7437,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_interleaving_supported(sk, len, optval, retval = sctp_getsockopt_interleaving_supported(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_REUSE_PORT:
retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
...@@ -7429,6 +7477,7 @@ static struct sctp_bind_bucket *sctp_bucket_create( ...@@ -7429,6 +7477,7 @@ static struct sctp_bind_bucket *sctp_bucket_create(
static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
{ {
bool reuse = (sk->sk_reuse || sctp_sk(sk)->reuse);
struct sctp_bind_hashbucket *head; /* hash list */ struct sctp_bind_hashbucket *head; /* hash list */
struct sctp_bind_bucket *pp; struct sctp_bind_bucket *pp;
unsigned short snum; unsigned short snum;
...@@ -7501,13 +7550,11 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -7501,13 +7550,11 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
* used by other socket (pp->owner not empty); that other * used by other socket (pp->owner not empty); that other
* socket is going to be sk2. * socket is going to be sk2.
*/ */
int reuse = sk->sk_reuse;
struct sock *sk2; struct sock *sk2;
pr_debug("%s: found a possible match\n", __func__); pr_debug("%s: found a possible match\n", __func__);
if (pp->fastreuse && sk->sk_reuse && if (pp->fastreuse && reuse && sk->sk_state != SCTP_SS_LISTENING)
sk->sk_state != SCTP_SS_LISTENING)
goto success; goto success;
/* Run through the list of sockets bound to the port /* Run through the list of sockets bound to the port
...@@ -7525,7 +7572,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -7525,7 +7572,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
ep2 = sctp_sk(sk2)->ep; ep2 = sctp_sk(sk2)->ep;
if (sk == sk2 || if (sk == sk2 ||
(reuse && sk2->sk_reuse && (reuse && (sk2->sk_reuse || sctp_sk(sk2)->reuse) &&
sk2->sk_state != SCTP_SS_LISTENING)) sk2->sk_state != SCTP_SS_LISTENING))
continue; continue;
...@@ -7549,12 +7596,12 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -7549,12 +7596,12 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
* SO_REUSEADDR on this socket -sk-). * SO_REUSEADDR on this socket -sk-).
*/ */
if (hlist_empty(&pp->owner)) { if (hlist_empty(&pp->owner)) {
if (sk->sk_reuse && sk->sk_state != SCTP_SS_LISTENING) if (reuse && sk->sk_state != SCTP_SS_LISTENING)
pp->fastreuse = 1; pp->fastreuse = 1;
else else
pp->fastreuse = 0; pp->fastreuse = 0;
} else if (pp->fastreuse && } else if (pp->fastreuse &&
(!sk->sk_reuse || sk->sk_state == SCTP_SS_LISTENING)) (!reuse || sk->sk_state == SCTP_SS_LISTENING))
pp->fastreuse = 0; pp->fastreuse = 0;
/* We are set, so fill up all the data in the hash table /* We are set, so fill up all the data in the hash table
...@@ -7685,7 +7732,7 @@ int sctp_inet_listen(struct socket *sock, int backlog) ...@@ -7685,7 +7732,7 @@ int sctp_inet_listen(struct socket *sock, int backlog)
err = 0; err = 0;
sctp_unhash_endpoint(ep); sctp_unhash_endpoint(ep);
sk->sk_state = SCTP_SS_CLOSED; sk->sk_state = SCTP_SS_CLOSED;
if (sk->sk_reuse) if (sk->sk_reuse || sctp_sk(sk)->reuse)
sctp_sk(sk)->bind_hash->fastreuse = 1; sctp_sk(sk)->bind_hash->fastreuse = 1;
goto out; goto out;
} }
...@@ -8550,6 +8597,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, ...@@ -8550,6 +8597,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
newsk->sk_no_check_tx = sk->sk_no_check_tx; newsk->sk_no_check_tx = sk->sk_no_check_tx;
newsk->sk_no_check_rx = sk->sk_no_check_rx; newsk->sk_no_check_rx = sk->sk_no_check_rx;
newsk->sk_reuse = sk->sk_reuse; newsk->sk_reuse = sk->sk_reuse;
sctp_sk(newsk)->reuse = sp->reuse;
newsk->sk_shutdown = sk->sk_shutdown; newsk->sk_shutdown = sk->sk_shutdown;
newsk->sk_destruct = sctp_destruct_sock; newsk->sk_destruct = sctp_destruct_sock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment