diff options
-rw-r--r-- | include/net/sctp/user.h | 2 | ||||
-rw-r--r-- | net/sctp/associola.c | 4 | ||||
-rw-r--r-- | net/sctp/socket.c | 42 |
3 files changed, 47 insertions, 1 deletions
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index b259fc5798f..1580c04f68b 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -147,6 +147,8 @@ enum sctp_optname { #define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS SCTP_SOCKOPT_CONNECTX, /* CONNECTX requests. */ #define SCTP_SOCKOPT_CONNECTX SCTP_SOCKOPT_CONNECTX + SCTP_SOCKOPT_CONNECTX3, /* CONNECTX requests. (new implementation) */ +#define SCTP_SOCKOPT_CONNECTX3 SCTP_SOCKOPT_CONNECTX3 }; /* diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 39f5166ae7a..525864bf4f0 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1470,6 +1470,10 @@ int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) { int assoc_id; int error = 0; + + /* If the id is already assigned, keep it. */ + if (asoc->assoc_id) + return error; retry: if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) return -ENOMEM; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 5fb3a8c9792..7c3dfd2d948 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1100,6 +1100,15 @@ static int __sctp_connect(struct sock* sk, goto out_free; } + /* In case the user of sctp_connectx() wants an association + * id back, assign one now. + */ + if (assoc_id) { + err = sctp_assoc_set_id(asoc, GFP_KERNEL); + if (err < 0) + goto out_free; + } + err = sctp_primitive_ASSOCIATE(asoc, NULL); if (err < 0) { goto out_free; @@ -1120,7 +1129,7 @@ static int __sctp_connect(struct sock* sk, timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK); err = sctp_wait_for_connect(asoc, &timeo); - if (!err && assoc_id) + if ((err == 0 || err == -EINPROGRESS) && assoc_id) *assoc_id = asoc->assoc_id; /* Don't free association on exit. */ @@ -1264,6 +1273,34 @@ SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk, return assoc_id; } +/* + * New (hopefully final) interface for the API. The option buffer is used + * both for the returned association id and the addresses. + */ +SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len, + char __user *optval, + int __user *optlen) +{ + sctp_assoc_t assoc_id = 0; + int err = 0; + + if (len < sizeof(assoc_id)) + return -EINVAL; + + err = __sctp_setsockopt_connectx(sk, + (struct sockaddr __user *)(optval + sizeof(assoc_id)), + len - sizeof(assoc_id), &assoc_id); + + if (err == 0 || err == -EINPROGRESS) { + if (copy_to_user(optval, &assoc_id, sizeof(assoc_id))) + return -EFAULT; + if (put_user(sizeof(assoc_id), optlen)) + return -EFAULT; + } + + return err; +} + /* API 3.1.4 close() - UDP Style Syntax * Applications use close() to perform graceful shutdown (as described in * Section 10.1 of [SCTP]) on ALL the associations currently represented @@ -5578,6 +5615,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_local_addrs(sk, len, optval, optlen); break; + case SCTP_SOCKOPT_CONNECTX3: + retval = sctp_getsockopt_connectx3(sk, len, optval, optlen); + break; case SCTP_DEFAULT_SEND_PARAM: retval = sctp_getsockopt_default_send_param(sk, len, optval, optlen); |