Skip to content

Commit 2cb5c8e

Browse files
nhormandavem330
authored andcommitted
sctp: Add peeloff-flags socket option
Based on a request raised on the sctp devel list, there is a need to augment the sctp_peeloff operation while specifying the O_CLOEXEC and O_NONBLOCK flags (simmilar to the socket syscall). Since modifying the SCTP_SOCKOPT_PEELOFF socket option would break user space ABI for existing programs, this patch creates a new socket option SCTP_SOCKOPT_PEELOFF_FLAGS, which accepts a third flags parameter to allow atomic assignment of the socket descriptor flags. Tested successfully by myself and the requestor Signed-off-by: Neil Horman <[email protected]> CC: Vlad Yasevich <[email protected]> CC: "David S. Miller" <[email protected]> CC: Andreas Steinmetz <[email protected]> CC: Marcelo Ricardo Leitner <[email protected]> Acked-by: Marcelo Ricardo Leitner <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 15324b2 commit 2cb5c8e

File tree

2 files changed

+78
-15
lines changed

2 files changed

+78
-15
lines changed

include/uapi/linux/sctp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ typedef __s32 sctp_assoc_t;
121121
#define SCTP_RESET_STREAMS 119
122122
#define SCTP_RESET_ASSOC 120
123123
#define SCTP_ADD_STREAMS 121
124+
#define SCTP_SOCKOPT_PEELOFF_FLAGS 122
124125

125126
/* PR-SCTP policies */
126127
#define SCTP_PR_SCTP_NONE 0x0000
@@ -978,6 +979,11 @@ typedef struct {
978979
int sd;
979980
} sctp_peeloff_arg_t;
980981

982+
typedef struct {
983+
sctp_peeloff_arg_t p_arg;
984+
unsigned flags;
985+
} sctp_peeloff_flags_arg_t;
986+
981987
/*
982988
* Peer Address Thresholds socket option
983989
*/

net/sctp/socket.c

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4933,11 +4933,47 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
49334933
}
49344934
EXPORT_SYMBOL(sctp_do_peeloff);
49354935

4936+
static int sctp_getsockopt_peeloff_common(struct sock *sk, sctp_peeloff_arg_t *peeloff,
4937+
struct file **newfile, unsigned flags)
4938+
{
4939+
struct socket *newsock;
4940+
int retval;
4941+
4942+
retval = sctp_do_peeloff(sk, peeloff->associd, &newsock);
4943+
if (retval < 0)
4944+
goto out;
4945+
4946+
/* Map the socket to an unused fd that can be returned to the user. */
4947+
retval = get_unused_fd_flags(flags & SOCK_CLOEXEC);
4948+
if (retval < 0) {
4949+
sock_release(newsock);
4950+
goto out;
4951+
}
4952+
4953+
*newfile = sock_alloc_file(newsock, 0, NULL);
4954+
if (IS_ERR(*newfile)) {
4955+
put_unused_fd(retval);
4956+
sock_release(newsock);
4957+
retval = PTR_ERR(*newfile);
4958+
*newfile = NULL;
4959+
return retval;
4960+
}
4961+
4962+
pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
4963+
retval);
4964+
4965+
peeloff->sd = retval;
4966+
4967+
if (flags & SOCK_NONBLOCK)
4968+
(*newfile)->f_flags |= O_NONBLOCK;
4969+
out:
4970+
return retval;
4971+
}
4972+
49364973
static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
49374974
{
49384975
sctp_peeloff_arg_t peeloff;
4939-
struct socket *newsock;
4940-
struct file *newfile;
4976+
struct file *newfile = NULL;
49414977
int retval = 0;
49424978

49434979
if (len < sizeof(sctp_peeloff_arg_t))
@@ -4946,34 +4982,52 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
49464982
if (copy_from_user(&peeloff, optval, len))
49474983
return -EFAULT;
49484984

4949-
retval = sctp_do_peeloff(sk, peeloff.associd, &newsock);
4985+
retval = sctp_getsockopt_peeloff_common(sk, &peeloff, &newfile, 0);
49504986
if (retval < 0)
49514987
goto out;
49524988

4953-
/* Map the socket to an unused fd that can be returned to the user. */
4954-
retval = get_unused_fd_flags(0);
4955-
if (retval < 0) {
4956-
sock_release(newsock);
4957-
goto out;
4989+
/* Return the fd mapped to the new socket. */
4990+
if (put_user(len, optlen)) {
4991+
fput(newfile);
4992+
put_unused_fd(retval);
4993+
return -EFAULT;
49584994
}
49594995

4960-
newfile = sock_alloc_file(newsock, 0, NULL);
4961-
if (IS_ERR(newfile)) {
4996+
if (copy_to_user(optval, &peeloff, len)) {
4997+
fput(newfile);
49624998
put_unused_fd(retval);
4963-
sock_release(newsock);
4964-
return PTR_ERR(newfile);
4999+
return -EFAULT;
49655000
}
5001+
fd_install(retval, newfile);
5002+
out:
5003+
return retval;
5004+
}
49665005

4967-
pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
4968-
retval);
5006+
static int sctp_getsockopt_peeloff_flags(struct sock *sk, int len,
5007+
char __user *optval, int __user *optlen)
5008+
{
5009+
sctp_peeloff_flags_arg_t peeloff;
5010+
struct file *newfile = NULL;
5011+
int retval = 0;
5012+
5013+
if (len < sizeof(sctp_peeloff_flags_arg_t))
5014+
return -EINVAL;
5015+
len = sizeof(sctp_peeloff_flags_arg_t);
5016+
if (copy_from_user(&peeloff, optval, len))
5017+
return -EFAULT;
5018+
5019+
retval = sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg,
5020+
&newfile, peeloff.flags);
5021+
if (retval < 0)
5022+
goto out;
49695023

49705024
/* Return the fd mapped to the new socket. */
49715025
if (put_user(len, optlen)) {
49725026
fput(newfile);
49735027
put_unused_fd(retval);
49745028
return -EFAULT;
49755029
}
4976-
peeloff.sd = retval;
5030+
49775031
if (copy_to_user(optval, &peeloff, len)) {
49785032
fput(newfile);
49795033
put_unused_fd(retval);
@@ -6759,6 +6813,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
67596813
case SCTP_SOCKOPT_PEELOFF:
67606814
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
67616815
break;
6816+
case SCTP_SOCKOPT_PEELOFF_FLAGS:
6817+
retval = sctp_getsockopt_peeloff_flags(sk, len, optval, optlen);
6818+
break;
67626819
case SCTP_PEER_ADDR_PARAMS:
67636820
retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
67646821
optlen);

0 commit comments

Comments
 (0)