Move the SCTP syscalls to netinet with the rest of the SCTP code. The

syscalls themselves are tightly coupled with the network stack and
therefore should not be in the generic socket code.

The following four syscalls have been marked as NOSTD so they can be
dynamically registered in sctp_syscalls_init() function:
  sys_sctp_peeloff
  sys_sctp_generic_sendmsg
  sys_sctp_generic_sendmsg_iov
  sys_sctp_generic_recvmsg

The syscalls are also set up to be dynamically registered when COMPAT32
option is configured.

As a side effect of moving the SCTP syscalls, getsock_cap needs to be
made available outside of the uipc_syscalls.c source file.  A proper
prototype has been added to the sys/socketvar.h header file.

API tests from the SCTP reference implementation have been run to ensure
compatibility. (http://code.google.com/p/sctp-refimpl/source/checkout)

Submitted by:	Steve Kiernan <stevek@juniper.net>
Reviewed by:	tuexen, rrs
Obtained from:	Juniper Networks, Inc.
This commit is contained in:
Marcel Moolenaar 2014-10-09 15:16:52 +00:00
parent 77d68af5df
commit 80b47aefa1
6 changed files with 603 additions and 498 deletions

View File

@ -845,14 +845,14 @@
468 AUE_NULL UNIMPL nosys
469 AUE_NULL UNIMPL __getpath_fromfd
470 AUE_NULL UNIMPL __getpath_fromaddr
471 AUE_NULL NOPROTO { int sctp_peeloff(int sd, uint32_t name); }
472 AUE_NULL NOPROTO { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, \
471 AUE_NULL NOPROTO|NOSTD { int sctp_peeloff(int sd, uint32_t name); }
472 AUE_NULL NOPROTO|NOSTD { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, \
caddr_t to, __socklen_t tolen, \
struct sctp_sndrcvinfo *sinfo, int flags); }
473 AUE_NULL NOPROTO { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, \
473 AUE_NULL NOPROTO|NOSTD { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, \
caddr_t to, __socklen_t tolen, \
struct sctp_sndrcvinfo *sinfo, int flags); }
474 AUE_NULL NOPROTO { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, \
474 AUE_NULL NOPROTO|NOSTD { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, \
struct sockaddr * from, __socklen_t *fromlenaddr, \
struct sctp_sndrcvinfo *sinfo, int *msg_flags); }
#ifdef PAD64_REQUIRED

View File

@ -3429,6 +3429,7 @@ netinet/sctp_output.c optional inet sctp | inet6 sctp
netinet/sctp_pcb.c optional inet sctp | inet6 sctp
netinet/sctp_peeloff.c optional inet sctp | inet6 sctp
netinet/sctp_ss_functions.c optional inet sctp | inet6 sctp
netinet/sctp_syscalls.c optional inet sctp | inet6 sctp
netinet/sctp_sysctl.c optional inet sctp | inet6 sctp
netinet/sctp_timer.c optional inet sctp | inet6 sctp
netinet/sctp_usrreq.c optional inet sctp | inet6 sctp

View File

@ -837,14 +837,14 @@
468 AUE_NULL UNIMPL nosys
469 AUE_NULL UNIMPL __getpath_fromfd
470 AUE_NULL UNIMPL __getpath_fromaddr
471 AUE_NULL STD { int sctp_peeloff(int sd, uint32_t name); }
472 AUE_NULL STD { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, \
471 AUE_NULL NOSTD { int sctp_peeloff(int sd, uint32_t name); }
472 AUE_NULL NOSTD { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, \
caddr_t to, __socklen_t tolen, \
struct sctp_sndrcvinfo *sinfo, int flags); }
473 AUE_NULL STD { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, \
473 AUE_NULL NOSTD { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, \
caddr_t to, __socklen_t tolen, \
struct sctp_sndrcvinfo *sinfo, int flags); }
474 AUE_NULL STD { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, \
474 AUE_NULL NOSTD { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, \
struct sockaddr * from, __socklen_t *fromlenaddr, \
struct sctp_sndrcvinfo *sinfo, int *msg_flags); }
475 AUE_PREAD STD { ssize_t pread(int fd, void *buf, \

View File

@ -38,7 +38,6 @@ __FBSDID("$FreeBSD$");
#include "opt_capsicum.h"
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_sctp.h"
#include "opt_compat.h"
#include "opt_ktrace.h"
@ -95,13 +94,6 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_extern.h>
#include <vm/uma.h>
#if defined(INET) || defined(INET6)
#ifdef SCTP
#include <netinet/sctp.h>
#include <netinet/sctp_peeloff.h>
#endif /* SCTP */
#endif /* INET || INET6 */
/*
* Flags for accept1() and kern_accept4(), in addition to SOCK_CLOEXEC
* and SOCK_NONBLOCK.
@ -198,7 +190,7 @@ SYSCTL_PROC(_kern_ipc, OID_AUTO, sfstat, CTLTYPE_OPAQUE | CTLFLAG_RW,
* capability rights are present.
* A reference on the file entry is held upon returning.
*/
static int
int
getsock_cap(struct filedesc *fdp, int fd, cap_rights_t *rightsp,
struct file **fpp, u_int *fflagp)
{
@ -3206,484 +3198,3 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
return (error);
}
/*
* SCTP syscalls.
* Functionality only compiled in if SCTP is defined in the kernel Makefile,
* otherwise all return EOPNOTSUPP.
* XXX: We should make this loadable one day.
*/
int
sys_sctp_peeloff(td, uap)
struct thread *td;
struct sctp_peeloff_args /* {
int sd;
caddr_t name;
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
struct file *nfp = NULL;
struct socket *head, *so;
cap_rights_t rights;
u_int fflag;
int error, fd;
AUDIT_ARG_FD(uap->sd);
error = fgetsock(td, uap->sd, cap_rights_init(&rights, CAP_PEELOFF),
&head, &fflag);
if (error != 0)
goto done2;
if (head->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto done;
}
error = sctp_can_peel_off(head, (sctp_assoc_t)uap->name);
if (error != 0)
goto done;
/*
* At this point we know we do have a assoc to pull
* we proceed to get the fd setup. This may block
* but that is ok.
*/
error = falloc(td, &nfp, &fd, 0);
if (error != 0)
goto done;
td->td_retval[0] = fd;
CURVNET_SET(head->so_vnet);
so = sonewconn(head, SS_ISCONNECTED);
if (so == NULL) {
error = ENOMEM;
goto noconnection;
}
/*
* Before changing the flags on the socket, we have to bump the
* reference count. Otherwise, if the protocol calls sofree(),
* the socket will be released due to a zero refcount.
*/
SOCK_LOCK(so);
soref(so); /* file descriptor reference */
SOCK_UNLOCK(so);
ACCEPT_LOCK();
TAILQ_REMOVE(&head->so_comp, so, so_list);
head->so_qlen--;
so->so_state |= (head->so_state & SS_NBIO);
so->so_state &= ~SS_NOFDREF;
so->so_qstate &= ~SQ_COMP;
so->so_head = NULL;
ACCEPT_UNLOCK();
finit(nfp, fflag, DTYPE_SOCKET, so, &socketops);
error = sctp_do_peeloff(head, so, (sctp_assoc_t)uap->name);
if (error != 0)
goto noconnection;
if (head->so_sigio != NULL)
fsetown(fgetown(&head->so_sigio), &so->so_sigio);
noconnection:
/*
* close the new descriptor, assuming someone hasn't ripped it
* out from under us.
*/
if (error != 0)
fdclose(td->td_proc->p_fd, nfp, fd, td);
/*
* Release explicitly held references before returning.
*/
CURVNET_RESTORE();
done:
if (nfp != NULL)
fdrop(nfp, td);
fputsock(head);
done2:
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}
int
sys_sctp_generic_sendmsg (td, uap)
struct thread *td;
struct sctp_generic_sendmsg_args /* {
int sd,
caddr_t msg,
int mlen,
caddr_t to,
__socklen_t tolen,
struct sctp_sndrcvinfo *sinfo,
int flags
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
struct socket *so;
struct file *fp = NULL;
struct sockaddr *to = NULL;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
struct uio auio;
struct iovec iov[1];
cap_rights_t rights;
int error = 0, len;
if (uap->sinfo != NULL) {
error = copyin(uap->sinfo, &sinfo, sizeof (sinfo));
if (error != 0)
return (error);
u_sinfo = &sinfo;
}
cap_rights_init(&rights, CAP_SEND);
if (uap->tolen != 0) {
error = getsockaddr(&to, uap->to, uap->tolen);
if (error != 0) {
to = NULL;
goto sctp_bad2;
}
cap_rights_set(&rights, CAP_CONNECT);
}
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd, &rights, &fp, NULL);
if (error != 0)
goto sctp_bad;
#ifdef KTRACE
if (to && (KTRPOINT(td, KTR_STRUCT)))
ktrsockaddr(to);
#endif
iov[0].iov_base = uap->msg;
iov[0].iov_len = uap->mlen;
so = (struct socket *)fp->f_data;
if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto sctp_bad;
}
#ifdef MAC
error = mac_socket_check_send(td->td_ucred, so);
if (error != 0)
goto sctp_bad;
#endif /* MAC */
auio.uio_iov = iov;
auio.uio_iovcnt = 1;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_td = td;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
len = auio.uio_resid = uap->mlen;
CURVNET_SET(so->so_vnet);
error = sctp_lower_sosend(so, to, &auio, (struct mbuf *)NULL,
(struct mbuf *)NULL, uap->flags, u_sinfo, td);
CURVNET_RESTORE();
if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
/* Generation of SIGPIPE can be controlled per socket. */
if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
!(uap->flags & MSG_NOSIGNAL)) {
PROC_LOCK(td->td_proc);
tdsignal(td, SIGPIPE);
PROC_UNLOCK(td->td_proc);
}
}
if (error == 0)
td->td_retval[0] = len - auio.uio_resid;
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = td->td_retval[0];
ktrgenio(uap->sd, UIO_WRITE, ktruio, error);
}
#endif /* KTRACE */
sctp_bad:
if (fp != NULL)
fdrop(fp, td);
sctp_bad2:
free(to, M_SONAME);
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}
int
sys_sctp_generic_sendmsg_iov(td, uap)
struct thread *td;
struct sctp_generic_sendmsg_iov_args /* {
int sd,
struct iovec *iov,
int iovlen,
caddr_t to,
__socklen_t tolen,
struct sctp_sndrcvinfo *sinfo,
int flags
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
struct socket *so;
struct file *fp = NULL;
struct sockaddr *to = NULL;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
struct uio auio;
struct iovec *iov, *tiov;
cap_rights_t rights;
ssize_t len;
int error, i;
if (uap->sinfo != NULL) {
error = copyin(uap->sinfo, &sinfo, sizeof (sinfo));
if (error != 0)
return (error);
u_sinfo = &sinfo;
}
cap_rights_init(&rights, CAP_SEND);
if (uap->tolen != 0) {
error = getsockaddr(&to, uap->to, uap->tolen);
if (error != 0) {
to = NULL;
goto sctp_bad2;
}
cap_rights_set(&rights, CAP_CONNECT);
}
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd, &rights, &fp, NULL);
if (error != 0)
goto sctp_bad1;
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
error = freebsd32_copyiniov((struct iovec32 *)uap->iov,
uap->iovlen, &iov, EMSGSIZE);
else
#endif
error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
if (error != 0)
goto sctp_bad1;
#ifdef KTRACE
if (to && (KTRPOINT(td, KTR_STRUCT)))
ktrsockaddr(to);
#endif
so = (struct socket *)fp->f_data;
if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto sctp_bad;
}
#ifdef MAC
error = mac_socket_check_send(td->td_ucred, so);
if (error != 0)
goto sctp_bad;
#endif /* MAC */
auio.uio_iov = iov;
auio.uio_iovcnt = uap->iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_td = td;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
tiov = iov;
for (i = 0; i <uap->iovlen; i++, tiov++) {
if ((auio.uio_resid += tiov->iov_len) < 0) {
error = EINVAL;
goto sctp_bad;
}
}
len = auio.uio_resid;
CURVNET_SET(so->so_vnet);
error = sctp_lower_sosend(so, to, &auio,
(struct mbuf *)NULL, (struct mbuf *)NULL,
uap->flags, u_sinfo, td);
CURVNET_RESTORE();
if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
/* Generation of SIGPIPE can be controlled per socket */
if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
!(uap->flags & MSG_NOSIGNAL)) {
PROC_LOCK(td->td_proc);
tdsignal(td, SIGPIPE);
PROC_UNLOCK(td->td_proc);
}
}
if (error == 0)
td->td_retval[0] = len - auio.uio_resid;
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = td->td_retval[0];
ktrgenio(uap->sd, UIO_WRITE, ktruio, error);
}
#endif /* KTRACE */
sctp_bad:
free(iov, M_IOV);
sctp_bad1:
if (fp != NULL)
fdrop(fp, td);
sctp_bad2:
free(to, M_SONAME);
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}
int
sys_sctp_generic_recvmsg(td, uap)
struct thread *td;
struct sctp_generic_recvmsg_args /* {
int sd,
struct iovec *iov,
int iovlen,
struct sockaddr *from,
__socklen_t *fromlenaddr,
struct sctp_sndrcvinfo *sinfo,
int *msg_flags
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
uint8_t sockbufstore[256];
struct uio auio;
struct iovec *iov, *tiov;
struct sctp_sndrcvinfo sinfo;
struct socket *so;
struct file *fp = NULL;
struct sockaddr *fromsa;
cap_rights_t rights;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
ssize_t len;
int error, fromlen, i, msg_flags;
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd,
cap_rights_init(&rights, CAP_RECV), &fp, NULL);
if (error != 0)
return (error);
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
error = freebsd32_copyiniov((struct iovec32 *)uap->iov,
uap->iovlen, &iov, EMSGSIZE);
else
#endif
error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
if (error != 0)
goto out1;
so = fp->f_data;
if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto out;
}
#ifdef MAC
error = mac_socket_check_receive(td->td_ucred, so);
if (error != 0)
goto out;
#endif /* MAC */
if (uap->fromlenaddr != NULL) {
error = copyin(uap->fromlenaddr, &fromlen, sizeof (fromlen));
if (error != 0)
goto out;
} else {
fromlen = 0;
}
if (uap->msg_flags) {
error = copyin(uap->msg_flags, &msg_flags, sizeof (int));
if (error != 0)
goto out;
} else {
msg_flags = 0;
}
auio.uio_iov = iov;
auio.uio_iovcnt = uap->iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = td;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
tiov = iov;
for (i = 0; i <uap->iovlen; i++, tiov++) {
if ((auio.uio_resid += tiov->iov_len) < 0) {
error = EINVAL;
goto out;
}
}
len = auio.uio_resid;
fromsa = (struct sockaddr *)sockbufstore;
#ifdef KTRACE
if (KTRPOINT(td, KTR_GENIO))
ktruio = cloneuio(&auio);
#endif /* KTRACE */
memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
CURVNET_SET(so->so_vnet);
error = sctp_sorecvmsg(so, &auio, (struct mbuf **)NULL,
fromsa, fromlen, &msg_flags,
(struct sctp_sndrcvinfo *)&sinfo, 1);
CURVNET_RESTORE();
if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
} else {
if (uap->sinfo)
error = copyout(&sinfo, uap->sinfo, sizeof (sinfo));
}
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = len - auio.uio_resid;
ktrgenio(uap->sd, UIO_READ, ktruio, error);
}
#endif /* KTRACE */
if (error != 0)
goto out;
td->td_retval[0] = len - auio.uio_resid;
if (fromlen && uap->from) {
len = fromlen;
if (len <= 0 || fromsa == 0)
len = 0;
else {
len = MIN(len, fromsa->sa_len);
error = copyout(fromsa, uap->from, (size_t)len);
if (error != 0)
goto out;
}
error = copyout(&len, uap->fromlenaddr, sizeof (socklen_t));
if (error != 0)
goto out;
}
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(fromsa);
#endif
if (uap->msg_flags) {
error = copyout(&msg_flags, uap->msg_flags, sizeof (int));
if (error != 0)
goto out;
}
out:
free(iov, M_IOV);
out1:
if (fp != NULL)
fdrop(fp, td);
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}

588
sys/netinet/sctp_syscalls.c Normal file
View File

@ -0,0 +1,588 @@
/*-
* Copyright (c) 1982, 1986, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_capsicum.h"
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_sctp.h"
#include "opt_compat.h"
#include "opt_ktrace.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/capsicum.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysproto.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
#include <sys/event.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <sys/jail.h>
#include <sys/mount.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/sf_buf.h>
#include <sys/sysent.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/signalvar.h>
#include <sys/syscall.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <sys/vnode.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#ifdef COMPAT_FREEBSD32
#include <compat/freebsd32/freebsd32_util.h>
#endif
#include <net/vnet.h>
#include <security/audit/audit.h>
#include <security/mac/mac_framework.h>
#include <netinet/sctp.h>
#include <netinet/sctp_peeloff.h>
static struct syscall_helper_data sctp_syscalls[] = {
SYSCALL_INIT_HELPER(sctp_peeloff),
SYSCALL_INIT_HELPER(sctp_generic_sendmsg),
SYSCALL_INIT_HELPER(sctp_generic_sendmsg_iov),
SYSCALL_INIT_HELPER(sctp_generic_recvmsg),
SYSCALL_INIT_LAST
};
static void
sctp_syscalls_init(void *unused __unused)
{
int error;
error = syscall_helper_register(sctp_syscalls);
KASSERT((error == 0),
("%s: syscall_helper_register failed for sctp syscalls", __func__));
#ifdef COMPAT_FREEBSD32
error = syscall32_helper_register(sctp_syscalls);
KASSERT((error == 0),
("%s: syscall32_helper_register failed for sctp syscalls",
__func__));
#endif
}
SYSINIT(sctp_syscalls, SI_SUB_SYSCALLS, SI_ORDER_ANY, sctp_syscalls_init, NULL);
/*
* SCTP syscalls.
* Functionality only compiled in if SCTP is defined in the kernel Makefile,
* otherwise all return EOPNOTSUPP.
* XXX: We should make this loadable one day.
*/
int
sys_sctp_peeloff(td, uap)
struct thread *td;
struct sctp_peeloff_args /* {
int sd;
caddr_t name;
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
struct file *nfp = NULL;
struct socket *head, *so;
cap_rights_t rights;
u_int fflag;
int error, fd;
AUDIT_ARG_FD(uap->sd);
error = fgetsock(td, uap->sd, cap_rights_init(&rights, CAP_PEELOFF),
&head, &fflag);
if (error != 0)
goto done2;
if (head->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto done;
}
error = sctp_can_peel_off(head, (sctp_assoc_t)uap->name);
if (error != 0)
goto done;
/*
* At this point we know we do have a assoc to pull
* we proceed to get the fd setup. This may block
* but that is ok.
*/
error = falloc(td, &nfp, &fd, 0);
if (error != 0)
goto done;
td->td_retval[0] = fd;
CURVNET_SET(head->so_vnet);
so = sonewconn(head, SS_ISCONNECTED);
if (so == NULL) {
error = ENOMEM;
goto noconnection;
}
/*
* Before changing the flags on the socket, we have to bump the
* reference count. Otherwise, if the protocol calls sofree(),
* the socket will be released due to a zero refcount.
*/
SOCK_LOCK(so);
soref(so); /* file descriptor reference */
SOCK_UNLOCK(so);
ACCEPT_LOCK();
TAILQ_REMOVE(&head->so_comp, so, so_list);
head->so_qlen--;
so->so_state |= (head->so_state & SS_NBIO);
so->so_state &= ~SS_NOFDREF;
so->so_qstate &= ~SQ_COMP;
so->so_head = NULL;
ACCEPT_UNLOCK();
finit(nfp, fflag, DTYPE_SOCKET, so, &socketops);
error = sctp_do_peeloff(head, so, (sctp_assoc_t)uap->name);
if (error != 0)
goto noconnection;
if (head->so_sigio != NULL)
fsetown(fgetown(&head->so_sigio), &so->so_sigio);
noconnection:
/*
* close the new descriptor, assuming someone hasn't ripped it
* out from under us.
*/
if (error != 0)
fdclose(td->td_proc->p_fd, nfp, fd, td);
/*
* Release explicitly held references before returning.
*/
CURVNET_RESTORE();
done:
if (nfp != NULL)
fdrop(nfp, td);
fputsock(head);
done2:
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}
int
sys_sctp_generic_sendmsg (td, uap)
struct thread *td;
struct sctp_generic_sendmsg_args /* {
int sd,
caddr_t msg,
int mlen,
caddr_t to,
__socklen_t tolen,
struct sctp_sndrcvinfo *sinfo,
int flags
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
struct socket *so;
struct file *fp = NULL;
struct sockaddr *to = NULL;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
struct uio auio;
struct iovec iov[1];
cap_rights_t rights;
int error = 0, len;
if (uap->sinfo != NULL) {
error = copyin(uap->sinfo, &sinfo, sizeof (sinfo));
if (error != 0)
return (error);
u_sinfo = &sinfo;
}
cap_rights_init(&rights, CAP_SEND);
if (uap->tolen != 0) {
error = getsockaddr(&to, uap->to, uap->tolen);
if (error != 0) {
to = NULL;
goto sctp_bad2;
}
cap_rights_set(&rights, CAP_CONNECT);
}
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd, &rights, &fp, NULL);
if (error != 0)
goto sctp_bad;
#ifdef KTRACE
if (to && (KTRPOINT(td, KTR_STRUCT)))
ktrsockaddr(to);
#endif
iov[0].iov_base = uap->msg;
iov[0].iov_len = uap->mlen;
so = (struct socket *)fp->f_data;
if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto sctp_bad;
}
#ifdef MAC
error = mac_socket_check_send(td->td_ucred, so);
if (error != 0)
goto sctp_bad;
#endif /* MAC */
auio.uio_iov = iov;
auio.uio_iovcnt = 1;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_td = td;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
len = auio.uio_resid = uap->mlen;
CURVNET_SET(so->so_vnet);
error = sctp_lower_sosend(so, to, &auio, (struct mbuf *)NULL,
(struct mbuf *)NULL, uap->flags, u_sinfo, td);
CURVNET_RESTORE();
if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
/* Generation of SIGPIPE can be controlled per socket. */
if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
!(uap->flags & MSG_NOSIGNAL)) {
PROC_LOCK(td->td_proc);
tdsignal(td, SIGPIPE);
PROC_UNLOCK(td->td_proc);
}
}
if (error == 0)
td->td_retval[0] = len - auio.uio_resid;
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = td->td_retval[0];
ktrgenio(uap->sd, UIO_WRITE, ktruio, error);
}
#endif /* KTRACE */
sctp_bad:
if (fp != NULL)
fdrop(fp, td);
sctp_bad2:
free(to, M_SONAME);
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}
int
sys_sctp_generic_sendmsg_iov(td, uap)
struct thread *td;
struct sctp_generic_sendmsg_iov_args /* {
int sd,
struct iovec *iov,
int iovlen,
caddr_t to,
__socklen_t tolen,
struct sctp_sndrcvinfo *sinfo,
int flags
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
struct socket *so;
struct file *fp = NULL;
struct sockaddr *to = NULL;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
struct uio auio;
struct iovec *iov, *tiov;
cap_rights_t rights;
ssize_t len;
int error, i;
if (uap->sinfo != NULL) {
error = copyin(uap->sinfo, &sinfo, sizeof (sinfo));
if (error != 0)
return (error);
u_sinfo = &sinfo;
}
cap_rights_init(&rights, CAP_SEND);
if (uap->tolen != 0) {
error = getsockaddr(&to, uap->to, uap->tolen);
if (error != 0) {
to = NULL;
goto sctp_bad2;
}
cap_rights_set(&rights, CAP_CONNECT);
}
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd, &rights, &fp, NULL);
if (error != 0)
goto sctp_bad1;
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
error = freebsd32_copyiniov((struct iovec32 *)uap->iov,
uap->iovlen, &iov, EMSGSIZE);
else
#endif
error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
if (error != 0)
goto sctp_bad1;
#ifdef KTRACE
if (to && (KTRPOINT(td, KTR_STRUCT)))
ktrsockaddr(to);
#endif
so = (struct socket *)fp->f_data;
if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto sctp_bad;
}
#ifdef MAC
error = mac_socket_check_send(td->td_ucred, so);
if (error != 0)
goto sctp_bad;
#endif /* MAC */
auio.uio_iov = iov;
auio.uio_iovcnt = uap->iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_td = td;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
tiov = iov;
for (i = 0; i <uap->iovlen; i++, tiov++) {
if ((auio.uio_resid += tiov->iov_len) < 0) {
error = EINVAL;
goto sctp_bad;
}
}
len = auio.uio_resid;
CURVNET_SET(so->so_vnet);
error = sctp_lower_sosend(so, to, &auio,
(struct mbuf *)NULL, (struct mbuf *)NULL,
uap->flags, u_sinfo, td);
CURVNET_RESTORE();
if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
/* Generation of SIGPIPE can be controlled per socket */
if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
!(uap->flags & MSG_NOSIGNAL)) {
PROC_LOCK(td->td_proc);
tdsignal(td, SIGPIPE);
PROC_UNLOCK(td->td_proc);
}
}
if (error == 0)
td->td_retval[0] = len - auio.uio_resid;
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = td->td_retval[0];
ktrgenio(uap->sd, UIO_WRITE, ktruio, error);
}
#endif /* KTRACE */
sctp_bad:
free(iov, M_IOV);
sctp_bad1:
if (fp != NULL)
fdrop(fp, td);
sctp_bad2:
free(to, M_SONAME);
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}
int
sys_sctp_generic_recvmsg(td, uap)
struct thread *td;
struct sctp_generic_recvmsg_args /* {
int sd,
struct iovec *iov,
int iovlen,
struct sockaddr *from,
__socklen_t *fromlenaddr,
struct sctp_sndrcvinfo *sinfo,
int *msg_flags
} */ *uap;
{
#if (defined(INET) || defined(INET6)) && defined(SCTP)
uint8_t sockbufstore[256];
struct uio auio;
struct iovec *iov, *tiov;
struct sctp_sndrcvinfo sinfo;
struct socket *so;
struct file *fp = NULL;
struct sockaddr *fromsa;
cap_rights_t rights;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
ssize_t len;
int error, fromlen, i, msg_flags;
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd,
cap_rights_init(&rights, CAP_RECV), &fp, NULL);
if (error != 0)
return (error);
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
error = freebsd32_copyiniov((struct iovec32 *)uap->iov,
uap->iovlen, &iov, EMSGSIZE);
else
#endif
error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
if (error != 0)
goto out1;
so = fp->f_data;
if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
error = EOPNOTSUPP;
goto out;
}
#ifdef MAC
error = mac_socket_check_receive(td->td_ucred, so);
if (error != 0)
goto out;
#endif /* MAC */
if (uap->fromlenaddr != NULL) {
error = copyin(uap->fromlenaddr, &fromlen, sizeof (fromlen));
if (error != 0)
goto out;
} else {
fromlen = 0;
}
if (uap->msg_flags) {
error = copyin(uap->msg_flags, &msg_flags, sizeof (int));
if (error != 0)
goto out;
} else {
msg_flags = 0;
}
auio.uio_iov = iov;
auio.uio_iovcnt = uap->iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = td;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
tiov = iov;
for (i = 0; i <uap->iovlen; i++, tiov++) {
if ((auio.uio_resid += tiov->iov_len) < 0) {
error = EINVAL;
goto out;
}
}
len = auio.uio_resid;
fromsa = (struct sockaddr *)sockbufstore;
#ifdef KTRACE
if (KTRPOINT(td, KTR_GENIO))
ktruio = cloneuio(&auio);
#endif /* KTRACE */
memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
CURVNET_SET(so->so_vnet);
error = sctp_sorecvmsg(so, &auio, (struct mbuf **)NULL,
fromsa, fromlen, &msg_flags,
(struct sctp_sndrcvinfo *)&sinfo, 1);
CURVNET_RESTORE();
if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
} else {
if (uap->sinfo)
error = copyout(&sinfo, uap->sinfo, sizeof (sinfo));
}
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = len - auio.uio_resid;
ktrgenio(uap->sd, UIO_READ, ktruio, error);
}
#endif /* KTRACE */
if (error != 0)
goto out;
td->td_retval[0] = len - auio.uio_resid;
if (fromlen && uap->from) {
len = fromlen;
if (len <= 0 || fromsa == 0)
len = 0;
else {
len = MIN(len, fromsa->sa_len);
error = copyout(fromsa, uap->from, (size_t)len);
if (error != 0)
goto out;
}
error = copyout(&len, uap->fromlenaddr, sizeof (socklen_t));
if (error != 0)
goto out;
}
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(fromsa);
#endif
if (uap->msg_flags) {
error = copyout(&msg_flags, uap->msg_flags, sizeof (int));
if (error != 0)
goto out;
}
out:
free(iov, M_IOV);
out1:
if (fp != NULL)
fdrop(fp, td);
return (error);
#else /* SCTP */
return (EOPNOTSUPP);
#endif /* SCTP */
}

View File

@ -43,6 +43,7 @@
#include <sys/sockbuf.h>
#include <sys/sockstate.h>
#ifdef _KERNEL
#include <sys/caprights.h>
#include <sys/sockopt.h>
#endif
@ -318,6 +319,8 @@ extern int maxsockets;
extern u_long sb_max;
extern so_gen_t so_gencnt;
struct file;
struct filedesc;
struct mbuf;
struct sockaddr;
struct ucred;
@ -336,6 +339,8 @@ struct uio;
*/
int sockargs(struct mbuf **mp, caddr_t buf, int buflen, int type);
int getsockaddr(struct sockaddr **namp, caddr_t uaddr, size_t len);
int getsock_cap(struct filedesc *fdp, int fd, cap_rights_t *rightsp,
struct file **fpp, u_int *fflagp);
void soabort(struct socket *so);
int soaccept(struct socket *so, struct sockaddr **nam);
int socheckuid(struct socket *so, uid_t uid);