Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <nhorman@tuxdriver.com>
CC: Vlad Yasevich <vyasevich@gmail.com>
CC: "David S. Miller" <davem@davemloft.net>
CC: Andreas Steinmetz <ast@domdv.de>
CC: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Neil Horman and committed by
David S. Miller
2cb5c8e3 15324b25

+84 -21
+6
include/uapi/linux/sctp.h
··· 121 121 #define SCTP_RESET_STREAMS 119 122 122 #define SCTP_RESET_ASSOC 120 123 123 #define SCTP_ADD_STREAMS 121 124 + #define SCTP_SOCKOPT_PEELOFF_FLAGS 122 124 125 125 126 /* PR-SCTP policies */ 126 127 #define SCTP_PR_SCTP_NONE 0x0000 ··· 978 977 sctp_assoc_t associd; 979 978 int sd; 980 979 } sctp_peeloff_arg_t; 980 + 981 + typedef struct { 982 + sctp_peeloff_arg_t p_arg; 983 + unsigned flags; 984 + } sctp_peeloff_flags_arg_t; 981 985 982 986 /* 983 987 * Peer Address Thresholds socket option
+78 -21
net/sctp/socket.c
··· 4933 4933 } 4934 4934 EXPORT_SYMBOL(sctp_do_peeloff); 4935 4935 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 + 4936 4973 static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen) 4937 4974 { 4938 4975 sctp_peeloff_arg_t peeloff; 4939 - struct socket *newsock; 4940 - struct file *newfile; 4976 + struct file *newfile = NULL; 4941 4977 int retval = 0; 4942 4978 4943 4979 if (len < sizeof(sctp_peeloff_arg_t)) ··· 4982 4946 if (copy_from_user(&peeloff, optval, len)) 4983 4947 return -EFAULT; 4984 4948 4985 - retval = sctp_do_peeloff(sk, peeloff.associd, &newsock); 4949 + retval = sctp_getsockopt_peeloff_common(sk, &peeloff, &newfile, 0); 4986 4950 if (retval < 0) 4987 4951 goto out; 4988 - 4989 - /* Map the socket to an unused fd that can be returned to the user. */ 4990 - retval = get_unused_fd_flags(0); 4991 - if (retval < 0) { 4992 - sock_release(newsock); 4993 - goto out; 4994 - } 4995 - 4996 - newfile = sock_alloc_file(newsock, 0, NULL); 4997 - if (IS_ERR(newfile)) { 4998 - put_unused_fd(retval); 4999 - sock_release(newsock); 5000 - return PTR_ERR(newfile); 5001 - } 5002 - 5003 - pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk, 5004 - retval); 5005 4952 5006 4953 /* Return the fd mapped to the new socket. */ 5007 4954 if (put_user(len, optlen)) { ··· 4992 4973 put_unused_fd(retval); 4993 4974 return -EFAULT; 4994 4975 } 4995 - peeloff.sd = retval; 4976 + 4977 + if (copy_to_user(optval, &peeloff, len)) { 4978 + fput(newfile); 4979 + put_unused_fd(retval); 4980 + return -EFAULT; 4981 + } 4982 + fd_install(retval, newfile); 4983 + out: 4984 + return retval; 4985 + } 4986 + 4987 + static int sctp_getsockopt_peeloff_flags(struct sock *sk, int len, 4988 + char __user *optval, int __user *optlen) 4989 + { 4990 + sctp_peeloff_flags_arg_t peeloff; 4991 + struct file *newfile = NULL; 4992 + int retval = 0; 4993 + 4994 + if (len < sizeof(sctp_peeloff_flags_arg_t)) 4995 + return -EINVAL; 4996 + len = sizeof(sctp_peeloff_flags_arg_t); 4997 + if (copy_from_user(&peeloff, optval, len)) 4998 + return -EFAULT; 4999 + 5000 + retval = sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg, 5001 + &newfile, peeloff.flags); 5002 + if (retval < 0) 5003 + goto out; 5004 + 5005 + /* Return the fd mapped to the new socket. */ 5006 + if (put_user(len, optlen)) { 5007 + fput(newfile); 5008 + put_unused_fd(retval); 5009 + return -EFAULT; 5010 + } 5011 + 4996 5012 if (copy_to_user(optval, &peeloff, len)) { 4997 5013 fput(newfile); 4998 5014 put_unused_fd(retval); ··· 6812 6758 break; 6813 6759 case SCTP_SOCKOPT_PEELOFF: 6814 6760 retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); 6761 + break; 6762 + case SCTP_SOCKOPT_PEELOFF_FLAGS: 6763 + retval = sctp_getsockopt_peeloff_flags(sk, len, optval, optlen); 6815 6764 break; 6816 6765 case SCTP_PEER_ADDR_PARAMS: 6817 6766 retval = sctp_getsockopt_peer_addr_params(sk, len, optval,