]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
sctp: add support for SCTP_REUSE_PORT sockopt
authorXin Long <lucien.xin@gmail.com>
Thu, 28 Jun 2018 07:31:00 +0000 (15:31 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 29 Jun 2018 13:20:55 +0000 (22:20 +0900)
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: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/structs.h
include/uapi/linux/sctp.h
net/sctp/socket.c

index e0f962d273862cb957fa3fda479d5bc52e923582..701a51736fa53477a2f9b62afeea7319b7d80623 100644 (file)
@@ -220,6 +220,7 @@ struct sctp_sock {
        __u32 adaptation_ind;
        __u32 pd_point;
        __u16   nodelay:1,
+               reuse:1,
                disable_fragments:1,
                v4mapped:1,
                frag_interleave:1,
index b64d583bf053bef4826a4571589a7c3d76632659..c02986a284db33a915142925bde7420e7e0f1142 100644 (file)
@@ -100,6 +100,7 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_RECVNXTINFO       33
 #define SCTP_DEFAULT_SNDINFO   34
 #define SCTP_AUTH_DEACTIVATE_KEY       35
+#define SCTP_REUSE_PORT                36
 
 /* Internal Socket Options. Some of the sctp library functions are
  * implemented using these socket options.
index 0e91e83eea5a778a43159daf785cd831308a5334..bf11f9cacb634fbd7c22de520ab4b2ef90cc6e2a 100644 (file)
@@ -4170,6 +4170,28 @@ out:
        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()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -4364,6 +4386,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
                retval = sctp_setsockopt_interleaving_supported(sk, optval,
                                                                optlen);
                break;
+       case SCTP_REUSE_PORT:
+               retval = sctp_setsockopt_reuse_port(sk, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -7197,6 +7222,26 @@ out:
        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,
                           char __user *optval, int __user *optlen)
 {
@@ -7392,6 +7437,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
                retval = sctp_getsockopt_interleaving_supported(sk, len, optval,
                                                                optlen);
                break;
+       case SCTP_REUSE_PORT:
+               retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -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)
 {
+       bool reuse = (sk->sk_reuse || sctp_sk(sk)->reuse);
        struct sctp_bind_hashbucket *head; /* hash list */
        struct sctp_bind_bucket *pp;
        unsigned short snum;
@@ -7501,13 +7550,11 @@ pp_found:
                 * used by other socket (pp->owner not empty); that other
                 * socket is going to be sk2.
                 */
-               int reuse = sk->sk_reuse;
                struct sock *sk2;
 
                pr_debug("%s: found a possible match\n", __func__);
 
-               if (pp->fastreuse && sk->sk_reuse &&
-                       sk->sk_state != SCTP_SS_LISTENING)
+               if (pp->fastreuse && reuse && sk->sk_state != SCTP_SS_LISTENING)
                        goto success;
 
                /* Run through the list of sockets bound to the port
@@ -7525,7 +7572,7 @@ pp_found:
                        ep2 = sctp_sk(sk2)->ep;
 
                        if (sk == sk2 ||
-                           (reuse && sk2->sk_reuse &&
+                           (reuse && (sk2->sk_reuse || sctp_sk(sk2)->reuse) &&
                             sk2->sk_state != SCTP_SS_LISTENING))
                                continue;
 
@@ -7549,12 +7596,12 @@ pp_not_found:
         * SO_REUSEADDR on this socket -sk-).
         */
        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;
                else
                        pp->fastreuse = 0;
        } else if (pp->fastreuse &&
-               (!sk->sk_reuse || sk->sk_state == SCTP_SS_LISTENING))
+                  (!reuse || sk->sk_state == SCTP_SS_LISTENING))
                pp->fastreuse = 0;
 
        /* 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)
                err = 0;
                sctp_unhash_endpoint(ep);
                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;
                goto out;
        }
@@ -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_rx = sk->sk_no_check_rx;
        newsk->sk_reuse = sk->sk_reuse;
+       sctp_sk(newsk)->reuse = sp->reuse;
 
        newsk->sk_shutdown = sk->sk_shutdown;
        newsk->sk_destruct = sctp_destruct_sock;