mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 04:31:06 +00:00
f4538e24b6
There are problems with global shared state and no API stability guarantees, and we can't rely on distros shipping the fixes we need. Both firefox and Chrome bundle their own copies too. Imported from https://github.com/sctplab/usrsctp, commit 547d3b46c64876c0336b9eef297fda58dbe1adaf Date: Thu Jul 23 21:49:32 2020 +0200 Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/870 Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1465>
3590 lines
88 KiB
C
3590 lines
88 KiB
C
/*-
|
|
* Copyright (c) 1982, 1986, 1988, 1990, 1993
|
|
* The Regents of the University of California.
|
|
* Copyright (c) 2004 The FreeBSD Foundation
|
|
* Copyright (c) 2004-2008 Robert N. M. Watson
|
|
* Copyright (c) 2009-2010 Brad Penoff
|
|
* Copyright (c) 2009-2010 Humaira Kamal
|
|
* Copyright (c) 2011-2012 Irene Ruengeler
|
|
* Copyright (c) 2011-2012 Michael Tuexen
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <netinet/sctp_os.h>
|
|
#include <netinet/sctp_pcb.h>
|
|
#include <netinet/sctputil.h>
|
|
#include <netinet/sctp_var.h>
|
|
#include <netinet/sctp_sysctl.h>
|
|
#include <netinet/sctp_input.h>
|
|
#include <netinet/sctp_peeloff.h>
|
|
#include <netinet/sctp_callout.h>
|
|
#include <netinet/sctp_crc32.h>
|
|
#ifdef INET6
|
|
#include <netinet6/sctp6_var.h>
|
|
#endif
|
|
#if defined(__FreeBSD__)
|
|
#include <sys/param.h>
|
|
#endif
|
|
#if defined(__linux__)
|
|
#define __FAVOR_BSD /* (on Ubuntu at least) enables UDP header field names like BSD in RFC 768 */
|
|
#endif
|
|
#if !defined(_WIN32)
|
|
#if defined INET || defined INET6
|
|
#include <netinet/udp.h>
|
|
#endif
|
|
#include <arpa/inet.h>
|
|
#else
|
|
#include <user_socketvar.h>
|
|
#endif
|
|
userland_mutex_t accept_mtx;
|
|
userland_cond_t accept_cond;
|
|
#ifdef _WIN32
|
|
#include <time.h>
|
|
#include <sys/timeb.h>
|
|
#endif
|
|
|
|
MALLOC_DEFINE(M_PCB, "sctp_pcb", "sctp pcb");
|
|
MALLOC_DEFINE(M_SONAME, "sctp_soname", "sctp soname");
|
|
#define MAXLEN_MBUF_CHAIN 32
|
|
|
|
/* Prototypes */
|
|
extern int sctp_sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
|
struct mbuf *top, struct mbuf *control, int flags,
|
|
/* proc is a dummy in __Userspace__ and will not be passed to sctp_lower_sosend */
|
|
struct proc *p);
|
|
|
|
extern int sctp_attach(struct socket *so, int proto, uint32_t vrf_id);
|
|
extern int sctpconn_attach(struct socket *so, int proto, uint32_t vrf_id);
|
|
|
|
static void init_sync(void) {
|
|
#if defined(_WIN32)
|
|
#if defined(INET) || defined(INET6)
|
|
WSADATA wsaData;
|
|
|
|
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
|
|
SCTP_PRINTF("WSAStartup failed\n");
|
|
exit (-1);
|
|
}
|
|
#endif
|
|
InitializeConditionVariable(&accept_cond);
|
|
InitializeCriticalSection(&accept_mtx);
|
|
#else
|
|
pthread_mutexattr_t mutex_attr;
|
|
|
|
pthread_mutexattr_init(&mutex_attr);
|
|
#ifdef INVARIANTS
|
|
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
|
|
#endif
|
|
pthread_mutex_init(&accept_mtx, &mutex_attr);
|
|
pthread_mutexattr_destroy(&mutex_attr);
|
|
pthread_cond_init(&accept_cond, NULL);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
usrsctp_init(uint16_t port,
|
|
int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
|
|
void (*debug_printf)(const char *format, ...))
|
|
{
|
|
init_sync();
|
|
sctp_init(port, conn_output, debug_printf, 1);
|
|
}
|
|
|
|
|
|
void
|
|
usrsctp_init_nothreads(uint16_t port,
|
|
int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
|
|
void (*debug_printf)(const char *format, ...))
|
|
{
|
|
init_sync();
|
|
sctp_init(port, conn_output, debug_printf, 0);
|
|
}
|
|
|
|
|
|
/* Taken from usr/src/sys/kern/uipc_sockbuf.c and modified for __Userspace__*/
|
|
/*
|
|
* Socantsendmore indicates that no more data will be sent on the socket; it
|
|
* would normally be applied to a socket when the user informs the system
|
|
* that no more data is to be sent, by the protocol code (in case
|
|
* PRU_SHUTDOWN). Socantrcvmore indicates that no more data will be
|
|
* received, and will normally be applied to the socket by a protocol when it
|
|
* detects that the peer will send no more data. Data queued for reading in
|
|
* the socket may yet be read.
|
|
*/
|
|
|
|
void socantrcvmore_locked(struct socket *so)
|
|
{
|
|
SOCKBUF_LOCK_ASSERT(&so->so_rcv);
|
|
so->so_rcv.sb_state |= SBS_CANTRCVMORE;
|
|
sorwakeup_locked(so);
|
|
}
|
|
|
|
void socantrcvmore(struct socket *so)
|
|
{
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
|
socantrcvmore_locked(so);
|
|
}
|
|
|
|
void
|
|
socantsendmore_locked(struct socket *so)
|
|
{
|
|
SOCKBUF_LOCK_ASSERT(&so->so_snd);
|
|
so->so_snd.sb_state |= SBS_CANTSENDMORE;
|
|
sowwakeup_locked(so);
|
|
}
|
|
|
|
void
|
|
socantsendmore(struct socket *so)
|
|
{
|
|
SOCKBUF_LOCK(&so->so_snd);
|
|
socantsendmore_locked(so);
|
|
}
|
|
|
|
|
|
|
|
/* Taken from usr/src/sys/kern/uipc_sockbuf.c and called within sctp_lower_sosend.
|
|
*/
|
|
int
|
|
sbwait(struct sockbuf *sb)
|
|
{
|
|
SOCKBUF_LOCK_ASSERT(sb);
|
|
|
|
sb->sb_flags |= SB_WAIT;
|
|
#if defined(_WIN32)
|
|
if (SleepConditionVariableCS(&(sb->sb_cond), &(sb->sb_mtx), INFINITE))
|
|
return (0);
|
|
else
|
|
return (-1);
|
|
#else
|
|
return (pthread_cond_wait(&(sb->sb_cond), &(sb->sb_mtx)));
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
static struct socket *
|
|
soalloc(void)
|
|
{
|
|
struct socket *so;
|
|
|
|
/*
|
|
* soalloc() sets of socket layer state for a socket,
|
|
* called only by socreate() and sonewconn().
|
|
*
|
|
* sodealloc() tears down socket layer state for a socket,
|
|
* called only by sofree() and sonewconn().
|
|
* __Userspace__ TODO : Make sure so is properly deallocated
|
|
* when tearing down the connection.
|
|
*/
|
|
|
|
so = (struct socket *)malloc(sizeof(struct socket));
|
|
|
|
if (so == NULL) {
|
|
return (NULL);
|
|
}
|
|
memset(so, 0, sizeof(struct socket));
|
|
|
|
/* __Userspace__ Initializing the socket locks here */
|
|
SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd");
|
|
SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv");
|
|
SOCKBUF_COND_INIT(&so->so_snd);
|
|
SOCKBUF_COND_INIT(&so->so_rcv);
|
|
SOCK_COND_INIT(so); /* timeo_cond */
|
|
|
|
/* __Userspace__ Any ref counting required here? Will we have any use for aiojobq?
|
|
What about gencnt and numopensockets?*/
|
|
TAILQ_INIT(&so->so_aiojobq);
|
|
return (so);
|
|
}
|
|
|
|
static void
|
|
sodealloc(struct socket *so)
|
|
{
|
|
|
|
KASSERT(so->so_count == 0, ("sodealloc(): so_count %d", so->so_count));
|
|
KASSERT(so->so_pcb == NULL, ("sodealloc(): so_pcb != NULL"));
|
|
|
|
SOCKBUF_COND_DESTROY(&so->so_snd);
|
|
SOCKBUF_COND_DESTROY(&so->so_rcv);
|
|
|
|
SOCK_COND_DESTROY(so);
|
|
|
|
SOCKBUF_LOCK_DESTROY(&so->so_snd);
|
|
SOCKBUF_LOCK_DESTROY(&so->so_rcv);
|
|
|
|
free(so);
|
|
}
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
void
|
|
sofree(struct socket *so)
|
|
{
|
|
struct socket *head;
|
|
|
|
ACCEPT_LOCK_ASSERT();
|
|
SOCK_LOCK_ASSERT(so);
|
|
/* SS_NOFDREF unset in accept call. this condition seems irrelevent
|
|
* for __Userspace__...
|
|
*/
|
|
if (so->so_count != 0 ||
|
|
(so->so_state & SS_PROTOREF) || (so->so_qstate & SQ_COMP)) {
|
|
SOCK_UNLOCK(so);
|
|
ACCEPT_UNLOCK();
|
|
return;
|
|
}
|
|
head = so->so_head;
|
|
if (head != NULL) {
|
|
KASSERT((so->so_qstate & SQ_COMP) != 0 ||
|
|
(so->so_qstate & SQ_INCOMP) != 0,
|
|
("sofree: so_head != NULL, but neither SQ_COMP nor "
|
|
"SQ_INCOMP"));
|
|
KASSERT((so->so_qstate & SQ_COMP) == 0 ||
|
|
(so->so_qstate & SQ_INCOMP) == 0,
|
|
("sofree: so->so_qstate is SQ_COMP and also SQ_INCOMP"));
|
|
TAILQ_REMOVE(&head->so_incomp, so, so_list);
|
|
head->so_incqlen--;
|
|
so->so_qstate &= ~SQ_INCOMP;
|
|
so->so_head = NULL;
|
|
}
|
|
KASSERT((so->so_qstate & SQ_COMP) == 0 &&
|
|
(so->so_qstate & SQ_INCOMP) == 0,
|
|
("sofree: so_head == NULL, but still SQ_COMP(%d) or SQ_INCOMP(%d)",
|
|
so->so_qstate & SQ_COMP, so->so_qstate & SQ_INCOMP));
|
|
if (so->so_options & SCTP_SO_ACCEPTCONN) {
|
|
KASSERT((TAILQ_EMPTY(&so->so_comp)), ("sofree: so_comp populated"));
|
|
KASSERT((TAILQ_EMPTY(&so->so_incomp)), ("sofree: so_comp populated"));
|
|
}
|
|
SOCK_UNLOCK(so);
|
|
ACCEPT_UNLOCK();
|
|
sctp_close(so); /* was... sctp_detach(so); */
|
|
/*
|
|
* From this point on, we assume that no other references to this
|
|
* socket exist anywhere else in the stack. Therefore, no locks need
|
|
* to be acquired or held.
|
|
*
|
|
* We used to do a lot of socket buffer and socket locking here, as
|
|
* well as invoke sorflush() and perform wakeups. The direct call to
|
|
* dom_dispose() and sbrelease_internal() are an inlining of what was
|
|
* necessary from sorflush().
|
|
*
|
|
* Notice that the socket buffer and kqueue state are torn down
|
|
* before calling pru_detach. This means that protocols shold not
|
|
* assume they can perform socket wakeups, etc, in their detach code.
|
|
*/
|
|
sodealloc(so);
|
|
}
|
|
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c */
|
|
void
|
|
soabort(struct socket *so)
|
|
{
|
|
#if defined(INET6)
|
|
struct sctp_inpcb *inp;
|
|
#endif
|
|
|
|
#if defined(INET6)
|
|
inp = (struct sctp_inpcb *)so->so_pcb;
|
|
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
|
|
sctp6_abort(so);
|
|
} else {
|
|
#if defined(INET)
|
|
sctp_abort(so);
|
|
#endif
|
|
}
|
|
#elif defined(INET)
|
|
sctp_abort(so);
|
|
#endif
|
|
ACCEPT_LOCK();
|
|
SOCK_LOCK(so);
|
|
sofree(so);
|
|
}
|
|
|
|
|
|
/* Taken from usr/src/sys/kern/uipc_socket.c and called within sctp_connect (sctp_usrreq.c).
|
|
* We use sctp_connect for send_one_init_real in ms1.
|
|
*/
|
|
void
|
|
soisconnecting(struct socket *so)
|
|
{
|
|
|
|
SOCK_LOCK(so);
|
|
so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING);
|
|
so->so_state |= SS_ISCONNECTING;
|
|
SOCK_UNLOCK(so);
|
|
}
|
|
|
|
/* Taken from usr/src/sys/kern/uipc_socket.c and called within sctp_disconnect (sctp_usrreq.c).
|
|
* TODO Do we use sctp_disconnect?
|
|
*/
|
|
void
|
|
soisdisconnecting(struct socket *so)
|
|
{
|
|
|
|
/*
|
|
* Note: This code assumes that SOCK_LOCK(so) and
|
|
* SOCKBUF_LOCK(&so->so_rcv) are the same.
|
|
*/
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
|
so->so_state &= ~SS_ISCONNECTING;
|
|
so->so_state |= SS_ISDISCONNECTING;
|
|
so->so_rcv.sb_state |= SBS_CANTRCVMORE;
|
|
sorwakeup_locked(so);
|
|
SOCKBUF_LOCK(&so->so_snd);
|
|
so->so_snd.sb_state |= SBS_CANTSENDMORE;
|
|
sowwakeup_locked(so);
|
|
wakeup("dummy",so);
|
|
/* requires 2 args but this was in orig */
|
|
/* wakeup(&so->so_timeo); */
|
|
}
|
|
|
|
|
|
/* Taken from sys/kern/kern_synch.c and
|
|
modified for __Userspace__
|
|
*/
|
|
|
|
/*
|
|
* Make all threads sleeping on the specified identifier runnable.
|
|
* Associating wakeup with so_timeo identifier and timeo_cond
|
|
* condition variable. TODO. If we use iterator thread then we need to
|
|
* modify wakeup so it can distinguish between iterator identifier and
|
|
* timeo identifier.
|
|
*/
|
|
void
|
|
wakeup(void *ident, struct socket *so)
|
|
{
|
|
SOCK_LOCK(so);
|
|
#if defined(_WIN32)
|
|
WakeAllConditionVariable(&(so)->timeo_cond);
|
|
#else
|
|
pthread_cond_broadcast(&(so)->timeo_cond);
|
|
#endif
|
|
SOCK_UNLOCK(so);
|
|
}
|
|
|
|
|
|
/*
|
|
* Make a thread sleeping on the specified identifier runnable.
|
|
* May wake more than one thread if a target thread is currently
|
|
* swapped out.
|
|
*/
|
|
void
|
|
wakeup_one(void *ident)
|
|
{
|
|
/* __Userspace__ Check: We are using accept_cond for wakeup_one.
|
|
It seems that wakeup_one is only called within
|
|
soisconnected() and sonewconn() with ident &head->so_timeo
|
|
head is so->so_head, which is back pointer to listen socket
|
|
This seems to indicate that the use of accept_cond is correct
|
|
since socket where accepts occur is so_head in all
|
|
subsidiary sockets.
|
|
*/
|
|
ACCEPT_LOCK();
|
|
#if defined(_WIN32)
|
|
WakeAllConditionVariable(&accept_cond);
|
|
#else
|
|
pthread_cond_broadcast(&accept_cond);
|
|
#endif
|
|
ACCEPT_UNLOCK();
|
|
}
|
|
|
|
|
|
/* Called within sctp_process_cookie_[existing/new] */
|
|
void
|
|
soisconnected(struct socket *so)
|
|
{
|
|
struct socket *head;
|
|
|
|
ACCEPT_LOCK();
|
|
SOCK_LOCK(so);
|
|
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
|
|
so->so_state |= SS_ISCONNECTED;
|
|
head = so->so_head;
|
|
if (head != NULL && (so->so_qstate & SQ_INCOMP)) {
|
|
SOCK_UNLOCK(so);
|
|
TAILQ_REMOVE(&head->so_incomp, so, so_list);
|
|
head->so_incqlen--;
|
|
so->so_qstate &= ~SQ_INCOMP;
|
|
TAILQ_INSERT_TAIL(&head->so_comp, so, so_list);
|
|
head->so_qlen++;
|
|
so->so_qstate |= SQ_COMP;
|
|
ACCEPT_UNLOCK();
|
|
sorwakeup(head);
|
|
wakeup_one(&head->so_timeo);
|
|
return;
|
|
}
|
|
SOCK_UNLOCK(so);
|
|
ACCEPT_UNLOCK();
|
|
wakeup(&so->so_timeo, so);
|
|
sorwakeup(so);
|
|
sowwakeup(so);
|
|
|
|
}
|
|
|
|
/* called within sctp_handle_cookie_echo */
|
|
|
|
struct socket *
|
|
sonewconn(struct socket *head, int connstatus)
|
|
{
|
|
struct socket *so;
|
|
int over;
|
|
|
|
ACCEPT_LOCK();
|
|
over = (head->so_qlen > 3 * head->so_qlimit / 2);
|
|
ACCEPT_UNLOCK();
|
|
#ifdef REGRESSION
|
|
if (regression_sonewconn_earlytest && over)
|
|
#else
|
|
if (over)
|
|
#endif
|
|
return (NULL);
|
|
so = soalloc();
|
|
if (so == NULL)
|
|
return (NULL);
|
|
so->so_head = head;
|
|
so->so_type = head->so_type;
|
|
so->so_options = head->so_options &~ SCTP_SO_ACCEPTCONN;
|
|
so->so_linger = head->so_linger;
|
|
so->so_state = head->so_state | SS_NOFDREF;
|
|
so->so_dom = head->so_dom;
|
|
#ifdef MAC
|
|
SOCK_LOCK(head);
|
|
mac_create_socket_from_socket(head, so);
|
|
SOCK_UNLOCK(head);
|
|
#endif
|
|
if (soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat)) {
|
|
sodealloc(so);
|
|
return (NULL);
|
|
}
|
|
switch (head->so_dom) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
if (sctp_attach(so, IPPROTO_SCTP, SCTP_DEFAULT_VRFID)) {
|
|
sodealloc(so);
|
|
return (NULL);
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
if (sctp6_attach(so, IPPROTO_SCTP, SCTP_DEFAULT_VRFID)) {
|
|
sodealloc(so);
|
|
return (NULL);
|
|
}
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
if (sctpconn_attach(so, IPPROTO_SCTP, SCTP_DEFAULT_VRFID)) {
|
|
sodealloc(so);
|
|
return (NULL);
|
|
}
|
|
break;
|
|
default:
|
|
sodealloc(so);
|
|
return (NULL);
|
|
break;
|
|
}
|
|
so->so_rcv.sb_lowat = head->so_rcv.sb_lowat;
|
|
so->so_snd.sb_lowat = head->so_snd.sb_lowat;
|
|
so->so_rcv.sb_timeo = head->so_rcv.sb_timeo;
|
|
so->so_snd.sb_timeo = head->so_snd.sb_timeo;
|
|
so->so_rcv.sb_flags |= head->so_rcv.sb_flags & SB_AUTOSIZE;
|
|
so->so_snd.sb_flags |= head->so_snd.sb_flags & SB_AUTOSIZE;
|
|
so->so_state |= connstatus;
|
|
ACCEPT_LOCK();
|
|
if (connstatus) {
|
|
TAILQ_INSERT_TAIL(&head->so_comp, so, so_list);
|
|
so->so_qstate |= SQ_COMP;
|
|
head->so_qlen++;
|
|
} else {
|
|
/*
|
|
* Keep removing sockets from the head until there's room for
|
|
* us to insert on the tail. In pre-locking revisions, this
|
|
* was a simple if (), but as we could be racing with other
|
|
* threads and soabort() requires dropping locks, we must
|
|
* loop waiting for the condition to be true.
|
|
*/
|
|
while (head->so_incqlen > head->so_qlimit) {
|
|
struct socket *sp;
|
|
sp = TAILQ_FIRST(&head->so_incomp);
|
|
TAILQ_REMOVE(&head->so_incomp, sp, so_list);
|
|
head->so_incqlen--;
|
|
sp->so_qstate &= ~SQ_INCOMP;
|
|
sp->so_head = NULL;
|
|
ACCEPT_UNLOCK();
|
|
soabort(sp);
|
|
ACCEPT_LOCK();
|
|
}
|
|
TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list);
|
|
so->so_qstate |= SQ_INCOMP;
|
|
head->so_incqlen++;
|
|
}
|
|
ACCEPT_UNLOCK();
|
|
if (connstatus) {
|
|
sorwakeup(head);
|
|
wakeup_one(&head->so_timeo);
|
|
}
|
|
return (so);
|
|
|
|
}
|
|
|
|
/*
|
|
Source: /src/sys/gnu/fs/xfs/FreeBSD/xfs_ioctl.c
|
|
*/
|
|
static __inline__ int
|
|
copy_to_user(void *dst, void *src, size_t len) {
|
|
memcpy(dst, src, len);
|
|
return 0;
|
|
}
|
|
|
|
static __inline__ int
|
|
copy_from_user(void *dst, void *src, size_t len) {
|
|
memcpy(dst, src, len);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
References:
|
|
src/sys/dev/lmc/if_lmc.h:
|
|
src/sys/powerpc/powerpc/copyinout.c
|
|
src/sys/sys/systm.h
|
|
*/
|
|
# define copyin(u, k, len) copy_from_user(k, u, len)
|
|
|
|
/* References:
|
|
src/sys/powerpc/powerpc/copyinout.c
|
|
src/sys/sys/systm.h
|
|
*/
|
|
# define copyout(k, u, len) copy_to_user(u, k, len)
|
|
|
|
|
|
/* copyiniov definition copied/modified from src/sys/kern/kern_subr.c */
|
|
int
|
|
copyiniov(struct iovec *iovp, u_int iovcnt, struct iovec **iov, int error)
|
|
{
|
|
u_int iovlen;
|
|
|
|
*iov = NULL;
|
|
if (iovcnt > UIO_MAXIOV)
|
|
return (error);
|
|
iovlen = iovcnt * sizeof (struct iovec);
|
|
*iov = malloc(iovlen); /*, M_IOV, M_WAITOK); */
|
|
error = copyin(iovp, *iov, iovlen);
|
|
if (error) {
|
|
free(*iov); /*, M_IOV); */
|
|
*iov = NULL;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/* (__Userspace__) version of uiomove */
|
|
int
|
|
uiomove(void *cp, int n, struct uio *uio)
|
|
{
|
|
struct iovec *iov;
|
|
size_t cnt;
|
|
int error = 0;
|
|
|
|
if ((uio->uio_rw != UIO_READ) &&
|
|
(uio->uio_rw != UIO_WRITE)) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
while (n > 0 && uio->uio_resid) {
|
|
iov = uio->uio_iov;
|
|
cnt = iov->iov_len;
|
|
if (cnt == 0) {
|
|
uio->uio_iov++;
|
|
uio->uio_iovcnt--;
|
|
continue;
|
|
}
|
|
if (cnt > (size_t)n)
|
|
cnt = n;
|
|
|
|
switch (uio->uio_segflg) {
|
|
|
|
case UIO_USERSPACE:
|
|
if (uio->uio_rw == UIO_READ)
|
|
error = copyout(cp, iov->iov_base, cnt);
|
|
else
|
|
error = copyin(iov->iov_base, cp, cnt);
|
|
if (error)
|
|
goto out;
|
|
break;
|
|
|
|
case UIO_SYSSPACE:
|
|
if (uio->uio_rw == UIO_READ)
|
|
memcpy(iov->iov_base, cp, cnt);
|
|
else
|
|
memcpy(cp, iov->iov_base, cnt);
|
|
break;
|
|
}
|
|
iov->iov_base = (char *)iov->iov_base + cnt;
|
|
iov->iov_len -= cnt;
|
|
uio->uio_resid -= cnt;
|
|
uio->uio_offset += (off_t)cnt;
|
|
cp = (char *)cp + cnt;
|
|
n -= (int)cnt;
|
|
}
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
|
|
/* Source: src/sys/kern/uipc_syscalls.c */
|
|
int
|
|
getsockaddr(struct sockaddr **namp, caddr_t uaddr, size_t len)
|
|
{
|
|
struct sockaddr *sa;
|
|
int error;
|
|
|
|
if (len > SOCK_MAXADDRLEN)
|
|
return (ENAMETOOLONG);
|
|
if (len < offsetof(struct sockaddr, sa_data))
|
|
return (EINVAL);
|
|
MALLOC(sa, struct sockaddr *, len, M_SONAME, M_WAITOK);
|
|
error = copyin(uaddr, sa, len);
|
|
if (error) {
|
|
FREE(sa, M_SONAME);
|
|
} else {
|
|
#ifdef HAVE_SA_LEN
|
|
sa->sa_len = len;
|
|
#endif
|
|
*namp = sa;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
usrsctp_getsockopt(struct socket *so, int level, int option_name,
|
|
void *option_value, socklen_t *option_len);
|
|
|
|
sctp_assoc_t
|
|
usrsctp_getassocid(struct socket *sock, struct sockaddr *sa)
|
|
{
|
|
struct sctp_paddrinfo sp;
|
|
socklen_t siz;
|
|
#ifndef HAVE_SA_LEN
|
|
size_t sa_len;
|
|
#endif
|
|
|
|
/* First get the assoc id */
|
|
siz = sizeof(sp);
|
|
memset(&sp, 0, sizeof(sp));
|
|
#ifdef HAVE_SA_LEN
|
|
memcpy((caddr_t)&sp.spinfo_address, sa, sa->sa_len);
|
|
#else
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
sa_len = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
sa_len = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
sa_len = sizeof(struct sockaddr_conn);
|
|
break;
|
|
default:
|
|
sa_len = 0;
|
|
break;
|
|
}
|
|
memcpy((caddr_t)&sp.spinfo_address, sa, sa_len);
|
|
#endif
|
|
if (usrsctp_getsockopt(sock, IPPROTO_SCTP, SCTP_GET_PEER_ADDR_INFO, &sp, &siz) != 0) {
|
|
/* We depend on the fact that 0 can never be returned */
|
|
return ((sctp_assoc_t) 0);
|
|
}
|
|
return (sp.spinfo_assoc_id);
|
|
}
|
|
|
|
|
|
/* Taken from /src/lib/libc/net/sctp_sys_calls.c
|
|
* and modified for __Userspace__
|
|
* calling sctp_generic_sendmsg from this function
|
|
*/
|
|
ssize_t
|
|
userspace_sctp_sendmsg(struct socket *so,
|
|
const void *data,
|
|
size_t len,
|
|
struct sockaddr *to,
|
|
socklen_t tolen,
|
|
uint32_t ppid,
|
|
uint32_t flags,
|
|
uint16_t stream_no,
|
|
uint32_t timetolive,
|
|
uint32_t context)
|
|
{
|
|
struct sctp_sndrcvinfo sndrcvinfo, *sinfo = &sndrcvinfo;
|
|
struct uio auio;
|
|
struct iovec iov[1];
|
|
|
|
memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
|
|
sinfo->sinfo_ppid = ppid;
|
|
sinfo->sinfo_flags = flags;
|
|
sinfo->sinfo_stream = stream_no;
|
|
sinfo->sinfo_timetolive = timetolive;
|
|
sinfo->sinfo_context = context;
|
|
sinfo->sinfo_assoc_id = 0;
|
|
|
|
|
|
/* Perform error checks on destination (to) */
|
|
if (tolen > SOCK_MAXADDRLEN) {
|
|
errno = ENAMETOOLONG;
|
|
return (-1);
|
|
}
|
|
if ((tolen > 0) &&
|
|
((to == NULL) || (tolen < (socklen_t)sizeof(struct sockaddr)))) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
if (data == NULL) {
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
/* Adding the following as part of defensive programming, in case the application
|
|
does not do it when preparing the destination address.*/
|
|
#ifdef HAVE_SA_LEN
|
|
if (to != NULL) {
|
|
to->sa_len = tolen;
|
|
}
|
|
#endif
|
|
|
|
iov[0].iov_base = (caddr_t)data;
|
|
iov[0].iov_len = len;
|
|
|
|
auio.uio_iov = iov;
|
|
auio.uio_iovcnt = 1;
|
|
auio.uio_segflg = UIO_USERSPACE;
|
|
auio.uio_rw = UIO_WRITE;
|
|
auio.uio_offset = 0; /* XXX */
|
|
auio.uio_resid = len;
|
|
errno = sctp_lower_sosend(so, to, &auio, NULL, NULL, 0, sinfo);
|
|
if (errno == 0) {
|
|
return (len - auio.uio_resid);
|
|
} else {
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
|
|
ssize_t
|
|
usrsctp_sendv(struct socket *so,
|
|
const void *data,
|
|
size_t len,
|
|
struct sockaddr *to,
|
|
int addrcnt,
|
|
void *info,
|
|
socklen_t infolen,
|
|
unsigned int infotype,
|
|
int flags)
|
|
{
|
|
struct sctp_sndrcvinfo sinfo;
|
|
struct uio auio;
|
|
struct iovec iov[1];
|
|
int use_sinfo;
|
|
sctp_assoc_t *assoc_id;
|
|
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
if (data == NULL) {
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
|
|
assoc_id = NULL;
|
|
use_sinfo = 0;
|
|
switch (infotype) {
|
|
case SCTP_SENDV_NOINFO:
|
|
if ((infolen != 0) || (info != NULL)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
break;
|
|
case SCTP_SENDV_SNDINFO:
|
|
if ((info == NULL) || (infolen != sizeof(struct sctp_sndinfo))) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
sinfo.sinfo_stream = ((struct sctp_sndinfo *)info)->snd_sid;
|
|
sinfo.sinfo_flags = ((struct sctp_sndinfo *)info)->snd_flags;
|
|
sinfo.sinfo_ppid = ((struct sctp_sndinfo *)info)->snd_ppid;
|
|
sinfo.sinfo_context = ((struct sctp_sndinfo *)info)->snd_context;
|
|
sinfo.sinfo_assoc_id = ((struct sctp_sndinfo *)info)->snd_assoc_id;
|
|
assoc_id = &(((struct sctp_sndinfo *)info)->snd_assoc_id);
|
|
use_sinfo = 1;
|
|
break;
|
|
case SCTP_SENDV_PRINFO:
|
|
if ((info == NULL) || (infolen != sizeof(struct sctp_prinfo))) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
sinfo.sinfo_stream = 0;
|
|
sinfo.sinfo_flags = PR_SCTP_POLICY(((struct sctp_prinfo *)info)->pr_policy);
|
|
sinfo.sinfo_timetolive = ((struct sctp_prinfo *)info)->pr_value;
|
|
use_sinfo = 1;
|
|
break;
|
|
case SCTP_SENDV_AUTHINFO:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
case SCTP_SENDV_SPA:
|
|
if ((info == NULL) || (infolen != sizeof(struct sctp_sendv_spa))) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
if (((struct sctp_sendv_spa *)info)->sendv_flags & SCTP_SEND_SNDINFO_VALID) {
|
|
sinfo.sinfo_stream = ((struct sctp_sendv_spa *)info)->sendv_sndinfo.snd_sid;
|
|
sinfo.sinfo_flags = ((struct sctp_sendv_spa *)info)->sendv_sndinfo.snd_flags;
|
|
sinfo.sinfo_ppid = ((struct sctp_sendv_spa *)info)->sendv_sndinfo.snd_ppid;
|
|
sinfo.sinfo_context = ((struct sctp_sendv_spa *)info)->sendv_sndinfo.snd_context;
|
|
sinfo.sinfo_assoc_id = ((struct sctp_sendv_spa *)info)->sendv_sndinfo.snd_assoc_id;
|
|
assoc_id = &(((struct sctp_sendv_spa *)info)->sendv_sndinfo.snd_assoc_id);
|
|
} else {
|
|
sinfo.sinfo_flags = 0;
|
|
sinfo.sinfo_stream = 0;
|
|
}
|
|
if (((struct sctp_sendv_spa *)info)->sendv_flags & SCTP_SEND_PRINFO_VALID) {
|
|
sinfo.sinfo_flags |= PR_SCTP_POLICY(((struct sctp_sendv_spa *)info)->sendv_prinfo.pr_policy);
|
|
sinfo.sinfo_timetolive = ((struct sctp_sendv_spa *)info)->sendv_prinfo.pr_value;
|
|
}
|
|
if (((struct sctp_sendv_spa *)info)->sendv_flags & SCTP_SEND_AUTHINFO_VALID) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
use_sinfo = 1;
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
/* Perform error checks on destination (to) */
|
|
if (addrcnt > 1) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
iov[0].iov_base = (caddr_t)data;
|
|
iov[0].iov_len = len;
|
|
|
|
auio.uio_iov = iov;
|
|
auio.uio_iovcnt = 1;
|
|
auio.uio_segflg = UIO_USERSPACE;
|
|
auio.uio_rw = UIO_WRITE;
|
|
auio.uio_offset = 0; /* XXX */
|
|
auio.uio_resid = len;
|
|
errno = sctp_lower_sosend(so, to, &auio, NULL, NULL, flags, use_sinfo ? &sinfo : NULL);
|
|
if (errno == 0) {
|
|
if ((to != NULL) && (assoc_id != NULL)) {
|
|
*assoc_id = usrsctp_getassocid(so, to);
|
|
}
|
|
return (len - auio.uio_resid);
|
|
} else {
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
|
|
ssize_t
|
|
userspace_sctp_sendmbuf(struct socket *so,
|
|
struct mbuf* mbufdata,
|
|
size_t len,
|
|
struct sockaddr *to,
|
|
socklen_t tolen,
|
|
uint32_t ppid,
|
|
uint32_t flags,
|
|
uint16_t stream_no,
|
|
uint32_t timetolive,
|
|
uint32_t context)
|
|
{
|
|
|
|
struct sctp_sndrcvinfo sndrcvinfo, *sinfo = &sndrcvinfo;
|
|
/* struct uio auio;
|
|
struct iovec iov[1]; */
|
|
int error = 0;
|
|
int uflags = 0;
|
|
ssize_t retval;
|
|
|
|
sinfo->sinfo_ppid = ppid;
|
|
sinfo->sinfo_flags = flags;
|
|
sinfo->sinfo_stream = stream_no;
|
|
sinfo->sinfo_timetolive = timetolive;
|
|
sinfo->sinfo_context = context;
|
|
sinfo->sinfo_assoc_id = 0;
|
|
|
|
/* Perform error checks on destination (to) */
|
|
if (tolen > SOCK_MAXADDRLEN){
|
|
error = (ENAMETOOLONG);
|
|
goto sendmsg_return;
|
|
}
|
|
if (tolen < (socklen_t)offsetof(struct sockaddr, sa_data)){
|
|
error = (EINVAL);
|
|
goto sendmsg_return;
|
|
}
|
|
/* Adding the following as part of defensive programming, in case the application
|
|
does not do it when preparing the destination address.*/
|
|
#ifdef HAVE_SA_LEN
|
|
to->sa_len = tolen;
|
|
#endif
|
|
|
|
error = sctp_lower_sosend(so, to, NULL/*uio*/,
|
|
(struct mbuf *)mbufdata, (struct mbuf *)NULL,
|
|
uflags, sinfo);
|
|
sendmsg_return:
|
|
/* TODO: Needs a condition for non-blocking when error is EWOULDBLOCK */
|
|
if (0 == error)
|
|
retval = len;
|
|
else if (error == EWOULDBLOCK) {
|
|
errno = EWOULDBLOCK;
|
|
retval = -1;
|
|
} else {
|
|
SCTP_PRINTF("%s: error = %d\n", __func__, error);
|
|
errno = error;
|
|
retval = -1;
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
|
|
/* taken from usr.lib/sctp_sys_calls.c and needed here */
|
|
#define SCTP_SMALL_IOVEC_SIZE 2
|
|
|
|
/* Taken from /src/lib/libc/net/sctp_sys_calls.c
|
|
* and modified for __Userspace__
|
|
* calling sctp_generic_recvmsg from this function
|
|
*/
|
|
ssize_t
|
|
userspace_sctp_recvmsg(struct socket *so,
|
|
void *dbuf,
|
|
size_t len,
|
|
struct sockaddr *from,
|
|
socklen_t *fromlenp,
|
|
struct sctp_sndrcvinfo *sinfo,
|
|
int *msg_flags)
|
|
{
|
|
struct uio auio;
|
|
struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
|
|
struct iovec *tiov;
|
|
int iovlen = 1;
|
|
int error = 0;
|
|
ssize_t ulen;
|
|
int i;
|
|
socklen_t fromlen;
|
|
|
|
iov[0].iov_base = dbuf;
|
|
iov[0].iov_len = len;
|
|
|
|
auio.uio_iov = iov;
|
|
auio.uio_iovcnt = iovlen;
|
|
auio.uio_segflg = UIO_USERSPACE;
|
|
auio.uio_rw = UIO_READ;
|
|
auio.uio_offset = 0; /* XXX */
|
|
auio.uio_resid = 0;
|
|
tiov = iov;
|
|
for (i = 0; i <iovlen; i++, tiov++) {
|
|
if ((auio.uio_resid += tiov->iov_len) < 0) {
|
|
error = EINVAL;
|
|
SCTP_PRINTF("%s: error = %d\n", __func__, error);
|
|
return (-1);
|
|
}
|
|
}
|
|
ulen = auio.uio_resid;
|
|
if (fromlenp != NULL) {
|
|
fromlen = *fromlenp;
|
|
} else {
|
|
fromlen = 0;
|
|
}
|
|
error = sctp_sorecvmsg(so, &auio, (struct mbuf **)NULL,
|
|
from, fromlen, msg_flags,
|
|
(struct sctp_sndrcvinfo *)sinfo, 1);
|
|
|
|
if (error) {
|
|
if ((auio.uio_resid != ulen) &&
|
|
(error == EINTR ||
|
|
#if !defined(__NetBSD__)
|
|
error == ERESTART ||
|
|
#endif
|
|
error == EWOULDBLOCK)) {
|
|
error = 0;
|
|
}
|
|
}
|
|
if ((fromlenp != NULL) && (fromlen > 0) && (from != NULL)) {
|
|
switch (from->sa_family) {
|
|
#if defined(INET)
|
|
case AF_INET:
|
|
*fromlenp = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
#if defined(INET6)
|
|
case AF_INET6:
|
|
*fromlenp = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
*fromlenp = sizeof(struct sockaddr_conn);
|
|
break;
|
|
default:
|
|
*fromlenp = 0;
|
|
break;
|
|
}
|
|
if (*fromlenp > fromlen) {
|
|
*fromlenp = fromlen;
|
|
}
|
|
}
|
|
if (error == 0) {
|
|
/* ready return value */
|
|
return (ulen - auio.uio_resid);
|
|
} else {
|
|
SCTP_PRINTF("%s: error = %d\n", __func__, error);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
usrsctp_recvv(struct socket *so,
|
|
void *dbuf,
|
|
size_t len,
|
|
struct sockaddr *from,
|
|
socklen_t *fromlenp,
|
|
void *info,
|
|
socklen_t *infolen,
|
|
unsigned int *infotype,
|
|
int *msg_flags)
|
|
{
|
|
struct uio auio;
|
|
struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
|
|
struct iovec *tiov;
|
|
int iovlen = 1;
|
|
ssize_t ulen;
|
|
int i;
|
|
socklen_t fromlen;
|
|
struct sctp_rcvinfo *rcv;
|
|
struct sctp_recvv_rn *rn;
|
|
struct sctp_extrcvinfo seinfo;
|
|
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
iov[0].iov_base = dbuf;
|
|
iov[0].iov_len = len;
|
|
|
|
auio.uio_iov = iov;
|
|
auio.uio_iovcnt = iovlen;
|
|
auio.uio_segflg = UIO_USERSPACE;
|
|
auio.uio_rw = UIO_READ;
|
|
auio.uio_offset = 0; /* XXX */
|
|
auio.uio_resid = 0;
|
|
tiov = iov;
|
|
for (i = 0; i <iovlen; i++, tiov++) {
|
|
if ((auio.uio_resid += tiov->iov_len) < 0) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
}
|
|
ulen = auio.uio_resid;
|
|
if (fromlenp != NULL) {
|
|
fromlen = *fromlenp;
|
|
} else {
|
|
fromlen = 0;
|
|
}
|
|
errno = sctp_sorecvmsg(so, &auio, (struct mbuf **)NULL,
|
|
from, fromlen, msg_flags,
|
|
(struct sctp_sndrcvinfo *)&seinfo, 1);
|
|
if (errno) {
|
|
if ((auio.uio_resid != ulen) &&
|
|
(errno == EINTR ||
|
|
#if !defined(__NetBSD__)
|
|
errno == ERESTART ||
|
|
#endif
|
|
errno == EWOULDBLOCK)) {
|
|
errno = 0;
|
|
}
|
|
}
|
|
if (errno != 0) {
|
|
goto out;
|
|
}
|
|
if ((*msg_flags & MSG_NOTIFICATION) == 0) {
|
|
struct sctp_inpcb *inp;
|
|
|
|
inp = (struct sctp_inpcb *)so->so_pcb;
|
|
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVNXTINFO) &&
|
|
sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVRCVINFO) &&
|
|
*infolen >= (socklen_t)sizeof(struct sctp_recvv_rn) &&
|
|
seinfo.sreinfo_next_flags & SCTP_NEXT_MSG_AVAIL) {
|
|
rn = (struct sctp_recvv_rn *)info;
|
|
rn->recvv_rcvinfo.rcv_sid = seinfo.sinfo_stream;
|
|
rn->recvv_rcvinfo.rcv_ssn = seinfo.sinfo_ssn;
|
|
rn->recvv_rcvinfo.rcv_flags = seinfo.sinfo_flags;
|
|
rn->recvv_rcvinfo.rcv_ppid = seinfo.sinfo_ppid;
|
|
rn->recvv_rcvinfo.rcv_context = seinfo.sinfo_context;
|
|
rn->recvv_rcvinfo.rcv_tsn = seinfo.sinfo_tsn;
|
|
rn->recvv_rcvinfo.rcv_cumtsn = seinfo.sinfo_cumtsn;
|
|
rn->recvv_rcvinfo.rcv_assoc_id = seinfo.sinfo_assoc_id;
|
|
rn->recvv_nxtinfo.nxt_sid = seinfo.sreinfo_next_stream;
|
|
rn->recvv_nxtinfo.nxt_flags = 0;
|
|
if (seinfo.sreinfo_next_flags & SCTP_NEXT_MSG_IS_UNORDERED) {
|
|
rn->recvv_nxtinfo.nxt_flags |= SCTP_UNORDERED;
|
|
}
|
|
if (seinfo.sreinfo_next_flags & SCTP_NEXT_MSG_IS_NOTIFICATION) {
|
|
rn->recvv_nxtinfo.nxt_flags |= SCTP_NOTIFICATION;
|
|
}
|
|
if (seinfo.sreinfo_next_flags & SCTP_NEXT_MSG_ISCOMPLETE) {
|
|
rn->recvv_nxtinfo.nxt_flags |= SCTP_COMPLETE;
|
|
}
|
|
rn->recvv_nxtinfo.nxt_ppid = seinfo.sreinfo_next_ppid;
|
|
rn->recvv_nxtinfo.nxt_length = seinfo.sreinfo_next_length;
|
|
rn->recvv_nxtinfo.nxt_assoc_id = seinfo.sreinfo_next_aid;
|
|
*infolen = (socklen_t)sizeof(struct sctp_recvv_rn);
|
|
*infotype = SCTP_RECVV_RN;
|
|
} else if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVRCVINFO) &&
|
|
*infolen >= (socklen_t)sizeof(struct sctp_rcvinfo)) {
|
|
rcv = (struct sctp_rcvinfo *)info;
|
|
rcv->rcv_sid = seinfo.sinfo_stream;
|
|
rcv->rcv_ssn = seinfo.sinfo_ssn;
|
|
rcv->rcv_flags = seinfo.sinfo_flags;
|
|
rcv->rcv_ppid = seinfo.sinfo_ppid;
|
|
rcv->rcv_context = seinfo.sinfo_context;
|
|
rcv->rcv_tsn = seinfo.sinfo_tsn;
|
|
rcv->rcv_cumtsn = seinfo.sinfo_cumtsn;
|
|
rcv->rcv_assoc_id = seinfo.sinfo_assoc_id;
|
|
*infolen = (socklen_t)sizeof(struct sctp_rcvinfo);
|
|
*infotype = SCTP_RECVV_RCVINFO;
|
|
} else {
|
|
*infotype = SCTP_RECVV_NOINFO;
|
|
*infolen = 0;
|
|
}
|
|
}
|
|
if ((fromlenp != NULL) &&
|
|
(fromlen > 0) &&
|
|
(from != NULL) &&
|
|
(ulen > auio.uio_resid)) {
|
|
switch (from->sa_family) {
|
|
#if defined(INET)
|
|
case AF_INET:
|
|
*fromlenp = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
#if defined(INET6)
|
|
case AF_INET6:
|
|
*fromlenp = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
*fromlenp = sizeof(struct sockaddr_conn);
|
|
break;
|
|
default:
|
|
*fromlenp = 0;
|
|
break;
|
|
}
|
|
if (*fromlenp > fromlen) {
|
|
*fromlenp = fromlen;
|
|
}
|
|
}
|
|
out:
|
|
if (errno == 0) {
|
|
/* ready return value */
|
|
return (ulen - auio.uio_resid);
|
|
} else {
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c
|
|
* and modified for __Userspace__
|
|
* socreate returns a socket. The socket should be
|
|
* closed with soclose().
|
|
*/
|
|
int
|
|
socreate(int dom, struct socket **aso, int type, int proto)
|
|
{
|
|
struct socket *so;
|
|
int error;
|
|
|
|
if ((dom != AF_CONN) && (dom != AF_INET) && (dom != AF_INET6)) {
|
|
return (EINVAL);
|
|
}
|
|
if ((type != SOCK_STREAM) && (type != SOCK_SEQPACKET)) {
|
|
return (EINVAL);
|
|
}
|
|
if (proto != IPPROTO_SCTP) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
so = soalloc();
|
|
if (so == NULL) {
|
|
return (ENOBUFS);
|
|
}
|
|
|
|
/*
|
|
* so_incomp represents a queue of connections that
|
|
* must be completed at protocol level before being
|
|
* returned. so_comp field heads a list of sockets
|
|
* that are ready to be returned to the listening process
|
|
*__Userspace__ These queues are being used at a number of places like accept etc.
|
|
*/
|
|
TAILQ_INIT(&so->so_incomp);
|
|
TAILQ_INIT(&so->so_comp);
|
|
so->so_type = type;
|
|
so->so_count = 1;
|
|
so->so_dom = dom;
|
|
/*
|
|
* Auto-sizing of socket buffers is managed by the protocols and
|
|
* the appropriate flags must be set in the pru_attach function.
|
|
* For __Userspace__ The pru_attach function in this case is sctp_attach.
|
|
*/
|
|
switch (dom) {
|
|
#if defined(INET)
|
|
case AF_INET:
|
|
error = sctp_attach(so, proto, SCTP_DEFAULT_VRFID);
|
|
break;
|
|
#endif
|
|
#if defined(INET6)
|
|
case AF_INET6:
|
|
error = sctp6_attach(so, proto, SCTP_DEFAULT_VRFID);
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
error = sctpconn_attach(so, proto, SCTP_DEFAULT_VRFID);
|
|
break;
|
|
default:
|
|
error = EAFNOSUPPORT;
|
|
break;
|
|
}
|
|
if (error) {
|
|
KASSERT(so->so_count == 1, ("socreate: so_count %d", so->so_count));
|
|
so->so_count = 0;
|
|
sodealloc(so);
|
|
return (error);
|
|
}
|
|
*aso = so;
|
|
return (0);
|
|
}
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_syscalls.c
|
|
* and modified for __Userspace__
|
|
* Removing struct thread td.
|
|
*/
|
|
struct socket *
|
|
userspace_socket(int domain, int type, int protocol)
|
|
{
|
|
struct socket *so = NULL;
|
|
|
|
errno = socreate(domain, &so, type, protocol);
|
|
if (errno) {
|
|
return (NULL);
|
|
}
|
|
/*
|
|
* The original socket call returns the file descriptor fd.
|
|
* td->td_retval[0] = fd.
|
|
* We are returning struct socket *so.
|
|
*/
|
|
return (so);
|
|
}
|
|
|
|
struct socket *
|
|
usrsctp_socket(int domain, int type, int protocol,
|
|
int (*receive_cb)(struct socket *sock, union sctp_sockstore addr, void *data,
|
|
size_t datalen, struct sctp_rcvinfo, int flags, void *ulp_info),
|
|
int (*send_cb)(struct socket *sock, uint32_t sb_free),
|
|
uint32_t sb_threshold,
|
|
void *ulp_info)
|
|
{
|
|
struct socket *so;
|
|
|
|
if ((protocol == IPPROTO_SCTP) && (SCTP_BASE_VAR(sctp_pcb_initialized) == 0)) {
|
|
errno = EPROTONOSUPPORT;
|
|
return (NULL);
|
|
}
|
|
if ((receive_cb == NULL) &&
|
|
((send_cb != NULL) || (sb_threshold != 0) || (ulp_info != NULL))) {
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
if ((domain == AF_CONN) && (SCTP_BASE_VAR(conn_output) == NULL)) {
|
|
errno = EAFNOSUPPORT;
|
|
return (NULL);
|
|
}
|
|
errno = socreate(domain, &so, type, protocol);
|
|
if (errno) {
|
|
return (NULL);
|
|
}
|
|
/*
|
|
* The original socket call returns the file descriptor fd.
|
|
* td->td_retval[0] = fd.
|
|
* We are returning struct socket *so.
|
|
*/
|
|
register_recv_cb(so, receive_cb);
|
|
register_send_cb(so, sb_threshold, send_cb);
|
|
register_ulp_info(so, ulp_info);
|
|
return (so);
|
|
}
|
|
|
|
|
|
u_long sb_max = SB_MAX;
|
|
u_long sb_max_adj =
|
|
SB_MAX * MCLBYTES / (MSIZE + MCLBYTES); /* adjusted sb_max */
|
|
|
|
static u_long sb_efficiency = 8; /* parameter for sbreserve() */
|
|
|
|
/*
|
|
* Allot mbufs to a sockbuf. Attempt to scale mbmax so that mbcnt doesn't
|
|
* become limiting if buffering efficiency is near the normal case.
|
|
*/
|
|
int
|
|
sbreserve_locked(struct sockbuf *sb, u_long cc, struct socket *so)
|
|
{
|
|
SOCKBUF_LOCK_ASSERT(sb);
|
|
sb->sb_mbmax = (u_int)min(cc * sb_efficiency, sb_max);
|
|
sb->sb_hiwat = (u_int)cc;
|
|
if (sb->sb_lowat > (int)sb->sb_hiwat)
|
|
sb->sb_lowat = (int)sb->sb_hiwat;
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
sbreserve(struct sockbuf *sb, u_long cc, struct socket *so)
|
|
{
|
|
int error;
|
|
|
|
SOCKBUF_LOCK(sb);
|
|
error = sbreserve_locked(sb, cc, so);
|
|
SOCKBUF_UNLOCK(sb);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
soreserve(struct socket *so, u_long sndcc, u_long rcvcc)
|
|
{
|
|
SOCKBUF_LOCK(&so->so_snd);
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
|
so->so_snd.sb_hiwat = (uint32_t)sndcc;
|
|
so->so_rcv.sb_hiwat = (uint32_t)rcvcc;
|
|
|
|
if (sbreserve_locked(&so->so_snd, sndcc, so) == 0) {
|
|
goto bad;
|
|
}
|
|
if (sbreserve_locked(&so->so_rcv, rcvcc, so) == 0) {
|
|
goto bad;
|
|
}
|
|
if (so->so_rcv.sb_lowat == 0)
|
|
so->so_rcv.sb_lowat = 1;
|
|
if (so->so_snd.sb_lowat == 0)
|
|
so->so_snd.sb_lowat = MCLBYTES;
|
|
if (so->so_snd.sb_lowat > (int)so->so_snd.sb_hiwat)
|
|
so->so_snd.sb_lowat = (int)so->so_snd.sb_hiwat;
|
|
SOCKBUF_UNLOCK(&so->so_rcv);
|
|
SOCKBUF_UNLOCK(&so->so_snd);
|
|
return (0);
|
|
|
|
bad:
|
|
SOCKBUF_UNLOCK(&so->so_rcv);
|
|
SOCKBUF_UNLOCK(&so->so_snd);
|
|
return (ENOBUFS);
|
|
}
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_sockbuf.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
|
|
void
|
|
sowakeup(struct socket *so, struct sockbuf *sb)
|
|
{
|
|
|
|
SOCKBUF_LOCK_ASSERT(sb);
|
|
|
|
sb->sb_flags &= ~SB_SEL;
|
|
if (sb->sb_flags & SB_WAIT) {
|
|
sb->sb_flags &= ~SB_WAIT;
|
|
#if defined(_WIN32)
|
|
WakeAllConditionVariable(&(sb)->sb_cond);
|
|
#else
|
|
pthread_cond_broadcast(&(sb)->sb_cond);
|
|
#endif
|
|
}
|
|
SOCKBUF_UNLOCK(sb);
|
|
}
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
|
|
int
|
|
sobind(struct socket *so, struct sockaddr *nam)
|
|
{
|
|
switch (nam->sa_family) {
|
|
#if defined(INET)
|
|
case AF_INET:
|
|
return (sctp_bind(so, nam));
|
|
#endif
|
|
#if defined(INET6)
|
|
case AF_INET6:
|
|
return (sctp6_bind(so, nam, NULL));
|
|
#endif
|
|
case AF_CONN:
|
|
return (sctpconn_bind(so, nam));
|
|
default:
|
|
return EAFNOSUPPORT;
|
|
}
|
|
}
|
|
|
|
/* Taken from /src/sys/kern/uipc_syscalls.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
|
|
int
|
|
usrsctp_bind(struct socket *so, struct sockaddr *name, int namelen)
|
|
{
|
|
struct sockaddr *sa;
|
|
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
if ((errno = getsockaddr(&sa, (caddr_t)name, namelen)) != 0)
|
|
return (-1);
|
|
|
|
errno = sobind(so, sa);
|
|
FREE(sa, M_SONAME);
|
|
if (errno) {
|
|
return (-1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
int
|
|
userspace_bind(struct socket *so, struct sockaddr *name, int namelen)
|
|
{
|
|
return (usrsctp_bind(so, name, namelen));
|
|
}
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
|
|
int
|
|
solisten(struct socket *so, int backlog)
|
|
{
|
|
if (so == NULL) {
|
|
return (EBADF);
|
|
} else {
|
|
return (sctp_listen(so, backlog, NULL));
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
solisten_proto_check(struct socket *so)
|
|
{
|
|
|
|
SOCK_LOCK_ASSERT(so);
|
|
|
|
if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING |
|
|
SS_ISDISCONNECTING))
|
|
return (EINVAL);
|
|
return (0);
|
|
}
|
|
|
|
static int somaxconn = SOMAXCONN;
|
|
|
|
void
|
|
solisten_proto(struct socket *so, int backlog)
|
|
{
|
|
|
|
SOCK_LOCK_ASSERT(so);
|
|
|
|
if (backlog < 0 || backlog > somaxconn)
|
|
backlog = somaxconn;
|
|
so->so_qlimit = backlog;
|
|
so->so_options |= SCTP_SO_ACCEPTCONN;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_syscalls.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
|
|
int
|
|
usrsctp_listen(struct socket *so, int backlog)
|
|
{
|
|
errno = solisten(so, backlog);
|
|
if (errno) {
|
|
return (-1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
int
|
|
userspace_listen(struct socket *so, int backlog)
|
|
{
|
|
return (usrsctp_listen(so, backlog));
|
|
}
|
|
|
|
/* Taken from /src/sys/kern/uipc_socket.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
|
|
int
|
|
soaccept(struct socket *so, struct sockaddr **nam)
|
|
{
|
|
int error;
|
|
|
|
SOCK_LOCK(so);
|
|
KASSERT((so->so_state & SS_NOFDREF) != 0, ("soaccept: !NOFDREF"));
|
|
so->so_state &= ~SS_NOFDREF;
|
|
SOCK_UNLOCK(so);
|
|
error = sctp_accept(so, nam);
|
|
return (error);
|
|
}
|
|
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_syscalls.c
|
|
* kern_accept modified for __Userspace__
|
|
*/
|
|
int
|
|
user_accept(struct socket *head, struct sockaddr **name, socklen_t *namelen, struct socket **ptr_accept_ret_sock)
|
|
{
|
|
struct sockaddr *sa = NULL;
|
|
int error;
|
|
struct socket *so = NULL;
|
|
|
|
|
|
if (name) {
|
|
*name = NULL;
|
|
}
|
|
|
|
if ((head->so_options & SCTP_SO_ACCEPTCONN) == 0) {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
ACCEPT_LOCK();
|
|
if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->so_comp)) {
|
|
ACCEPT_UNLOCK();
|
|
error = EWOULDBLOCK;
|
|
goto noconnection;
|
|
}
|
|
while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0) {
|
|
if (head->so_rcv.sb_state & SBS_CANTRCVMORE) {
|
|
head->so_error = ECONNABORTED;
|
|
break;
|
|
}
|
|
#if defined(_WIN32)
|
|
if (SleepConditionVariableCS(&accept_cond, &accept_mtx, INFINITE))
|
|
error = 0;
|
|
else
|
|
error = GetLastError();
|
|
#else
|
|
error = pthread_cond_wait(&accept_cond, &accept_mtx);
|
|
#endif
|
|
if (error) {
|
|
ACCEPT_UNLOCK();
|
|
goto noconnection;
|
|
}
|
|
}
|
|
if (head->so_error) {
|
|
error = head->so_error;
|
|
head->so_error = 0;
|
|
ACCEPT_UNLOCK();
|
|
goto noconnection;
|
|
}
|
|
so = TAILQ_FIRST(&head->so_comp);
|
|
KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP"));
|
|
KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP"));
|
|
|
|
/*
|
|
* 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() and so_state update */
|
|
soref(so); /* file descriptor reference */
|
|
|
|
TAILQ_REMOVE(&head->so_comp, so, so_list);
|
|
head->so_qlen--;
|
|
so->so_state |= (head->so_state & SS_NBIO);
|
|
so->so_qstate &= ~SQ_COMP;
|
|
so->so_head = NULL;
|
|
SOCK_UNLOCK(so);
|
|
ACCEPT_UNLOCK();
|
|
|
|
|
|
/*
|
|
* The original accept returns fd value via td->td_retval[0] = fd;
|
|
* we will return the socket for accepted connection.
|
|
*/
|
|
|
|
error = soaccept(so, &sa);
|
|
if (error) {
|
|
/*
|
|
* return a namelen of zero for older code which might
|
|
* ignore the return value from accept.
|
|
*/
|
|
if (name)
|
|
*namelen = 0;
|
|
goto noconnection;
|
|
}
|
|
if (sa == NULL) {
|
|
if (name)
|
|
*namelen = 0;
|
|
goto done;
|
|
}
|
|
if (name) {
|
|
#ifdef HAVE_SA_LEN
|
|
/* check sa_len before it is destroyed */
|
|
if (*namelen > sa->sa_len) {
|
|
*namelen = sa->sa_len;
|
|
}
|
|
#else
|
|
socklen_t sa_len;
|
|
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
sa_len = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
sa_len = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
sa_len = sizeof(struct sockaddr_conn);
|
|
break;
|
|
default:
|
|
sa_len = 0;
|
|
break;
|
|
}
|
|
if (*namelen > sa_len) {
|
|
*namelen = sa_len;
|
|
}
|
|
#endif
|
|
*name = sa;
|
|
sa = NULL;
|
|
}
|
|
noconnection:
|
|
if (sa) {
|
|
FREE(sa, M_SONAME);
|
|
}
|
|
|
|
done:
|
|
*ptr_accept_ret_sock = so;
|
|
return (error);
|
|
}
|
|
|
|
|
|
|
|
/* Taken from /src/sys/kern/uipc_syscalls.c
|
|
* and modified for __Userspace__
|
|
*/
|
|
/*
|
|
* accept1()
|
|
*/
|
|
static int
|
|
accept1(struct socket *so, struct sockaddr *aname, socklen_t *anamelen, struct socket **ptr_accept_ret_sock)
|
|
{
|
|
struct sockaddr *name;
|
|
socklen_t namelen;
|
|
int error;
|
|
|
|
if (so == NULL) {
|
|
return (EBADF);
|
|
}
|
|
if (aname == NULL) {
|
|
return (user_accept(so, NULL, NULL, ptr_accept_ret_sock));
|
|
}
|
|
|
|
error = copyin(anamelen, &namelen, sizeof (namelen));
|
|
if (error)
|
|
return (error);
|
|
|
|
error = user_accept(so, &name, &namelen, ptr_accept_ret_sock);
|
|
|
|
/*
|
|
* return a namelen of zero for older code which might
|
|
* ignore the return value from accept.
|
|
*/
|
|
if (error) {
|
|
(void) copyout(&namelen,
|
|
anamelen, sizeof(*anamelen));
|
|
return (error);
|
|
}
|
|
|
|
if (error == 0 && name != NULL) {
|
|
error = copyout(name, aname, namelen);
|
|
}
|
|
if (error == 0) {
|
|
error = copyout(&namelen, anamelen, sizeof(namelen));
|
|
}
|
|
|
|
if (name) {
|
|
FREE(name, M_SONAME);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
struct socket *
|
|
usrsctp_accept(struct socket *so, struct sockaddr *aname, socklen_t *anamelen)
|
|
{
|
|
struct socket *accept_return_sock = NULL;
|
|
|
|
errno = accept1(so, aname, anamelen, &accept_return_sock);
|
|
if (errno) {
|
|
return (NULL);
|
|
} else {
|
|
return (accept_return_sock);
|
|
}
|
|
}
|
|
|
|
struct socket *
|
|
userspace_accept(struct socket *so, struct sockaddr *aname, socklen_t *anamelen)
|
|
{
|
|
return (usrsctp_accept(so, aname, anamelen));
|
|
}
|
|
|
|
struct socket *
|
|
usrsctp_peeloff(struct socket *head, sctp_assoc_t id)
|
|
{
|
|
struct socket *so;
|
|
|
|
if ((errno = sctp_can_peel_off(head, id)) != 0) {
|
|
return (NULL);
|
|
}
|
|
if ((so = sonewconn(head, SS_ISCONNECTED)) == NULL) {
|
|
return (NULL);
|
|
}
|
|
ACCEPT_LOCK();
|
|
SOCK_LOCK(so);
|
|
soref(so);
|
|
TAILQ_REMOVE(&head->so_comp, so, so_list);
|
|
head->so_qlen--;
|
|
so->so_state |= (head->so_state & SS_NBIO);
|
|
so->so_qstate &= ~SQ_COMP;
|
|
so->so_head = NULL;
|
|
SOCK_UNLOCK(so);
|
|
ACCEPT_UNLOCK();
|
|
if ((errno = sctp_do_peeloff(head, so, id)) != 0) {
|
|
so->so_count = 0;
|
|
sodealloc(so);
|
|
return (NULL);
|
|
}
|
|
return (so);
|
|
}
|
|
|
|
int
|
|
sodisconnect(struct socket *so)
|
|
{
|
|
int error;
|
|
|
|
if ((so->so_state & SS_ISCONNECTED) == 0)
|
|
return (ENOTCONN);
|
|
if (so->so_state & SS_ISDISCONNECTING)
|
|
return (EALREADY);
|
|
error = sctp_disconnect(so);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
usrsctp_set_non_blocking(struct socket *so, int onoff)
|
|
{
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
SOCK_LOCK(so);
|
|
if (onoff != 0) {
|
|
so->so_state |= SS_NBIO;
|
|
} else {
|
|
so->so_state &= ~SS_NBIO;
|
|
}
|
|
SOCK_UNLOCK(so);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
usrsctp_get_non_blocking(struct socket *so)
|
|
{
|
|
int result;
|
|
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
SOCK_LOCK(so);
|
|
if (so->so_state & SS_NBIO) {
|
|
result = 1;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
SOCK_UNLOCK(so);
|
|
return (result);
|
|
}
|
|
|
|
int
|
|
soconnect(struct socket *so, struct sockaddr *nam)
|
|
{
|
|
int error;
|
|
|
|
if (so->so_options & SCTP_SO_ACCEPTCONN)
|
|
return (EOPNOTSUPP);
|
|
/*
|
|
* If protocol is connection-based, can only connect once.
|
|
* Otherwise, if connected, try to disconnect first. This allows
|
|
* user to disconnect by connecting to, e.g., a null address.
|
|
*/
|
|
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && (error = sodisconnect(so))) {
|
|
error = EISCONN;
|
|
} else {
|
|
/*
|
|
* Prevent accumulated error from previous connection from
|
|
* biting us.
|
|
*/
|
|
so->so_error = 0;
|
|
switch (nam->sa_family) {
|
|
#if defined(INET)
|
|
case AF_INET:
|
|
error = sctp_connect(so, nam);
|
|
break;
|
|
#endif
|
|
#if defined(INET6)
|
|
case AF_INET6:
|
|
error = sctp6_connect(so, nam);
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
error = sctpconn_connect(so, nam);
|
|
break;
|
|
default:
|
|
error = EAFNOSUPPORT;
|
|
}
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
|
|
|
|
int user_connect(struct socket *so, struct sockaddr *sa)
|
|
{
|
|
int error;
|
|
int interrupted = 0;
|
|
|
|
if (so == NULL) {
|
|
error = EBADF;
|
|
goto done1;
|
|
}
|
|
if (so->so_state & SS_ISCONNECTING) {
|
|
error = EALREADY;
|
|
goto done1;
|
|
}
|
|
|
|
error = soconnect(so, sa);
|
|
if (error) {
|
|
goto bad;
|
|
}
|
|
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
|
|
error = EINPROGRESS;
|
|
goto done1;
|
|
}
|
|
|
|
SOCK_LOCK(so);
|
|
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
|
|
#if defined(_WIN32)
|
|
if (SleepConditionVariableCS(SOCK_COND(so), SOCK_MTX(so), INFINITE))
|
|
error = 0;
|
|
else
|
|
error = -1;
|
|
#else
|
|
error = pthread_cond_wait(SOCK_COND(so), SOCK_MTX(so));
|
|
#endif
|
|
if (error) {
|
|
#if defined(__NetBSD__)
|
|
if (error == EINTR) {
|
|
#else
|
|
if (error == EINTR || error == ERESTART) {
|
|
#endif
|
|
interrupted = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (error == 0) {
|
|
error = so->so_error;
|
|
so->so_error = 0;
|
|
}
|
|
SOCK_UNLOCK(so);
|
|
|
|
bad:
|
|
if (!interrupted) {
|
|
so->so_state &= ~SS_ISCONNECTING;
|
|
}
|
|
#if !defined(__NetBSD__)
|
|
if (error == ERESTART) {
|
|
error = EINTR;
|
|
}
|
|
#endif
|
|
done1:
|
|
return (error);
|
|
}
|
|
|
|
int usrsctp_connect(struct socket *so, struct sockaddr *name, int namelen)
|
|
{
|
|
struct sockaddr *sa = NULL;
|
|
|
|
errno = getsockaddr(&sa, (caddr_t)name, namelen);
|
|
if (errno)
|
|
return (-1);
|
|
|
|
errno = user_connect(so, sa);
|
|
FREE(sa, M_SONAME);
|
|
if (errno) {
|
|
return (-1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
int userspace_connect(struct socket *so, struct sockaddr *name, int namelen)
|
|
{
|
|
return (usrsctp_connect(so, name, namelen));
|
|
}
|
|
|
|
#define SCTP_STACK_BUF_SIZE 2048
|
|
|
|
void
|
|
usrsctp_close(struct socket *so) {
|
|
if (so != NULL) {
|
|
if (so->so_options & SCTP_SO_ACCEPTCONN) {
|
|
struct socket *sp;
|
|
|
|
ACCEPT_LOCK();
|
|
while ((sp = TAILQ_FIRST(&so->so_comp)) != NULL) {
|
|
TAILQ_REMOVE(&so->so_comp, sp, so_list);
|
|
so->so_qlen--;
|
|
sp->so_qstate &= ~SQ_COMP;
|
|
sp->so_head = NULL;
|
|
ACCEPT_UNLOCK();
|
|
soabort(sp);
|
|
ACCEPT_LOCK();
|
|
}
|
|
ACCEPT_UNLOCK();
|
|
}
|
|
ACCEPT_LOCK();
|
|
SOCK_LOCK(so);
|
|
sorele(so);
|
|
}
|
|
}
|
|
|
|
void
|
|
userspace_close(struct socket *so)
|
|
{
|
|
usrsctp_close(so);
|
|
}
|
|
|
|
int
|
|
usrsctp_shutdown(struct socket *so, int how)
|
|
{
|
|
if (!(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
sctp_flush(so, how);
|
|
if (how != SHUT_WR)
|
|
socantrcvmore(so);
|
|
if (how != SHUT_RD) {
|
|
errno = sctp_shutdown(so);
|
|
if (errno) {
|
|
return (-1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
userspace_shutdown(struct socket *so, int how)
|
|
{
|
|
return (usrsctp_shutdown(so, how));
|
|
}
|
|
|
|
int
|
|
usrsctp_finish(void)
|
|
{
|
|
if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
|
|
return (0);
|
|
}
|
|
if (SCTP_INP_INFO_TRYLOCK()) {
|
|
if (!LIST_EMPTY(&SCTP_BASE_INFO(listhead))) {
|
|
SCTP_INP_INFO_RUNLOCK();
|
|
return (-1);
|
|
}
|
|
SCTP_INP_INFO_RUNLOCK();
|
|
} else {
|
|
return (-1);
|
|
}
|
|
sctp_finish();
|
|
#if defined(_WIN32)
|
|
DeleteConditionVariable(&accept_cond);
|
|
DeleteCriticalSection(&accept_mtx);
|
|
#if defined(INET) || defined(INET6)
|
|
WSACleanup();
|
|
#endif
|
|
#else
|
|
pthread_cond_destroy(&accept_cond);
|
|
pthread_mutex_destroy(&accept_mtx);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
userspace_finish(void)
|
|
{
|
|
return (usrsctp_finish());
|
|
}
|
|
|
|
/* needed from sctp_usrreq.c */
|
|
int
|
|
sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, void *p);
|
|
|
|
int
|
|
usrsctp_setsockopt(struct socket *so, int level, int option_name,
|
|
const void *option_value, socklen_t option_len)
|
|
{
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
switch (level) {
|
|
case SOL_SOCKET:
|
|
{
|
|
switch (option_name) {
|
|
case SO_RCVBUF:
|
|
if (option_len < (socklen_t)sizeof(int)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
int *buf_size;
|
|
|
|
buf_size = (int *)option_value;
|
|
if (*buf_size < 1) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
sbreserve(&so->so_rcv, (u_long)*buf_size, so);
|
|
return (0);
|
|
}
|
|
break;
|
|
case SO_SNDBUF:
|
|
if (option_len < (socklen_t)sizeof(int)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
int *buf_size;
|
|
|
|
buf_size = (int *)option_value;
|
|
if (*buf_size < 1) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
sbreserve(&so->so_snd, (u_long)*buf_size, so);
|
|
return (0);
|
|
}
|
|
break;
|
|
case SO_LINGER:
|
|
if (option_len < (socklen_t)sizeof(struct linger)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
struct linger *l;
|
|
|
|
l = (struct linger *)option_value;
|
|
so->so_linger = l->l_linger;
|
|
if (l->l_onoff) {
|
|
so->so_options |= SCTP_SO_LINGER;
|
|
} else {
|
|
so->so_options &= ~SCTP_SO_LINGER;
|
|
}
|
|
return (0);
|
|
}
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
}
|
|
case IPPROTO_SCTP:
|
|
errno = sctp_setopt(so, option_name, (void *) option_value, (size_t)option_len, NULL);
|
|
if (errno) {
|
|
return (-1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
default:
|
|
errno = ENOPROTOOPT;
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
int
|
|
userspace_setsockopt(struct socket *so, int level, int option_name,
|
|
const void *option_value, socklen_t option_len)
|
|
{
|
|
return (usrsctp_setsockopt(so, level, option_name, option_value, option_len));
|
|
}
|
|
|
|
/* needed from sctp_usrreq.c */
|
|
int
|
|
sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize,
|
|
void *p);
|
|
|
|
int
|
|
usrsctp_getsockopt(struct socket *so, int level, int option_name,
|
|
void *option_value, socklen_t *option_len)
|
|
{
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
if (option_len == NULL) {
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
switch (level) {
|
|
case SOL_SOCKET:
|
|
switch (option_name) {
|
|
case SO_RCVBUF:
|
|
if (*option_len < (socklen_t)sizeof(int)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
int *buf_size;
|
|
|
|
buf_size = (int *)option_value;
|
|
*buf_size = so->so_rcv.sb_hiwat;;
|
|
*option_len = (socklen_t)sizeof(int);
|
|
return (0);
|
|
}
|
|
break;
|
|
case SO_SNDBUF:
|
|
if (*option_len < (socklen_t)sizeof(int)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
int *buf_size;
|
|
|
|
buf_size = (int *)option_value;
|
|
*buf_size = so->so_snd.sb_hiwat;
|
|
*option_len = (socklen_t)sizeof(int);
|
|
return (0);
|
|
}
|
|
break;
|
|
case SO_LINGER:
|
|
if (*option_len < (socklen_t)sizeof(struct linger)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
struct linger *l;
|
|
|
|
l = (struct linger *)option_value;
|
|
l->l_linger = so->so_linger;
|
|
if (so->so_options & SCTP_SO_LINGER) {
|
|
l->l_onoff = 1;
|
|
} else {
|
|
l->l_onoff = 0;
|
|
}
|
|
*option_len = (socklen_t)sizeof(struct linger);
|
|
return (0);
|
|
}
|
|
break;
|
|
case SO_ERROR:
|
|
if (*option_len < (socklen_t)sizeof(int)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
} else {
|
|
int *intval;
|
|
|
|
intval = (int *)option_value;
|
|
*intval = so->so_error;
|
|
*option_len = (socklen_t)sizeof(int);
|
|
return (0);
|
|
}
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
case IPPROTO_SCTP:
|
|
{
|
|
size_t len;
|
|
|
|
len = (size_t)*option_len;
|
|
errno = sctp_getopt(so, option_name, option_value, &len, NULL);
|
|
*option_len = (socklen_t)len;
|
|
if (errno) {
|
|
return (-1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
default:
|
|
errno = ENOPROTOOPT;
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
int
|
|
userspace_getsockopt(struct socket *so, int level, int option_name,
|
|
void *option_value, socklen_t *option_len)
|
|
{
|
|
return (usrsctp_getsockopt(so, level, option_name, option_value, option_len));
|
|
}
|
|
|
|
int
|
|
usrsctp_opt_info(struct socket *so, sctp_assoc_t id, int opt, void *arg, socklen_t *size)
|
|
{
|
|
if (arg == NULL) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
if ((id == SCTP_CURRENT_ASSOC) ||
|
|
(id == SCTP_ALL_ASSOC)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
switch (opt) {
|
|
case SCTP_RTOINFO:
|
|
((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
|
|
break;
|
|
case SCTP_ASSOCINFO:
|
|
((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
|
|
break;
|
|
case SCTP_DEFAULT_SEND_PARAM:
|
|
((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
|
|
break;
|
|
case SCTP_PRIMARY_ADDR:
|
|
((struct sctp_setprim *)arg)->ssp_assoc_id = id;
|
|
break;
|
|
case SCTP_PEER_ADDR_PARAMS:
|
|
((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
|
|
break;
|
|
case SCTP_MAXSEG:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_AUTH_KEY:
|
|
((struct sctp_authkey *)arg)->sca_assoc_id = id;
|
|
break;
|
|
case SCTP_AUTH_ACTIVE_KEY:
|
|
((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
|
|
break;
|
|
case SCTP_DELAYED_SACK:
|
|
((struct sctp_sack_info *)arg)->sack_assoc_id = id;
|
|
break;
|
|
case SCTP_CONTEXT:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_STATUS:
|
|
((struct sctp_status *)arg)->sstat_assoc_id = id;
|
|
break;
|
|
case SCTP_GET_PEER_ADDR_INFO:
|
|
((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
|
|
break;
|
|
case SCTP_PEER_AUTH_CHUNKS:
|
|
((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
|
|
break;
|
|
case SCTP_LOCAL_AUTH_CHUNKS:
|
|
((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
|
|
break;
|
|
case SCTP_TIMEOUTS:
|
|
((struct sctp_timeouts *)arg)->stimo_assoc_id = id;
|
|
break;
|
|
case SCTP_EVENT:
|
|
((struct sctp_event *)arg)->se_assoc_id = id;
|
|
break;
|
|
case SCTP_DEFAULT_SNDINFO:
|
|
((struct sctp_sndinfo *)arg)->snd_assoc_id = id;
|
|
break;
|
|
case SCTP_DEFAULT_PRINFO:
|
|
((struct sctp_default_prinfo *)arg)->pr_assoc_id = id;
|
|
break;
|
|
case SCTP_PEER_ADDR_THLDS:
|
|
((struct sctp_paddrthlds *)arg)->spt_assoc_id = id;
|
|
break;
|
|
case SCTP_REMOTE_UDP_ENCAPS_PORT:
|
|
((struct sctp_udpencaps *)arg)->sue_assoc_id = id;
|
|
break;
|
|
case SCTP_ECN_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_PR_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_AUTH_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_ASCONF_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_RECONFIG_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_NRSACK_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_PKTDROP_SUPPORTED:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_MAX_BURST:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_ENABLE_STREAM_RESET:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
case SCTP_PR_STREAM_STATUS:
|
|
((struct sctp_prstatus *)arg)->sprstat_assoc_id = id;
|
|
break;
|
|
case SCTP_PR_ASSOC_STATUS:
|
|
((struct sctp_prstatus *)arg)->sprstat_assoc_id = id;
|
|
break;
|
|
case SCTP_MAX_CWND:
|
|
((struct sctp_assoc_value *)arg)->assoc_id = id;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return (usrsctp_getsockopt(so, IPPROTO_SCTP, opt, arg, size));
|
|
}
|
|
|
|
int
|
|
usrsctp_set_ulpinfo(struct socket *so, void *ulp_info)
|
|
{
|
|
return (register_ulp_info(so, ulp_info));
|
|
}
|
|
|
|
|
|
int
|
|
usrsctp_get_ulpinfo(struct socket *so, void **pulp_info)
|
|
{
|
|
return (retrieve_ulp_info(so, pulp_info));
|
|
}
|
|
|
|
int
|
|
usrsctp_bindx(struct socket *so, struct sockaddr *addrs, int addrcnt, int flags)
|
|
{
|
|
struct sockaddr *sa;
|
|
#ifdef INET
|
|
struct sockaddr_in *sin;
|
|
#endif
|
|
#ifdef INET6
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
int i;
|
|
#if defined(INET) || defined(INET6)
|
|
uint16_t sport;
|
|
bool fix_port;
|
|
#endif
|
|
|
|
/* validate the flags */
|
|
if ((flags != SCTP_BINDX_ADD_ADDR) &&
|
|
(flags != SCTP_BINDX_REM_ADDR)) {
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
/* validate the address count and list */
|
|
if ((addrcnt <= 0) || (addrs == NULL)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
#if defined(INET) || defined(INET6)
|
|
sport = 0;
|
|
fix_port = false;
|
|
#endif
|
|
/* First pre-screen the addresses */
|
|
sa = addrs;
|
|
for (i = 0; i < addrcnt; i++) {
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
#ifdef HAVE_SA_LEN
|
|
if (sa->sa_len != sizeof(struct sockaddr_in)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
#endif
|
|
sin = (struct sockaddr_in *)sa;
|
|
if (sin->sin_port) {
|
|
/* non-zero port, check or save */
|
|
if (sport) {
|
|
/* Check against our port */
|
|
if (sport != sin->sin_port) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
} else {
|
|
/* save off the port */
|
|
sport = sin->sin_port;
|
|
fix_port = (i > 0);
|
|
}
|
|
}
|
|
#ifndef HAVE_SA_LEN
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in));
|
|
#endif
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
#ifdef HAVE_SA_LEN
|
|
if (sa->sa_len != sizeof(struct sockaddr_in6)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
#endif
|
|
sin6 = (struct sockaddr_in6 *)sa;
|
|
if (sin6->sin6_port) {
|
|
/* non-zero port, check or save */
|
|
if (sport) {
|
|
/* Check against our port */
|
|
if (sport != sin6->sin6_port) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
} else {
|
|
/* save off the port */
|
|
sport = sin6->sin6_port;
|
|
fix_port = (i > 0);
|
|
}
|
|
}
|
|
#ifndef HAVE_SA_LEN
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6));
|
|
#endif
|
|
break;
|
|
#endif
|
|
default:
|
|
/* Invalid address family specified. */
|
|
errno = EAFNOSUPPORT;
|
|
return (-1);
|
|
}
|
|
#ifdef HAVE_SA_LEN
|
|
sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
|
|
#endif
|
|
}
|
|
sa = addrs;
|
|
for (i = 0; i < addrcnt; i++) {
|
|
#ifndef HAVE_SA_LEN
|
|
size_t sa_len;
|
|
|
|
#endif
|
|
#ifdef HAVE_SA_LEN
|
|
#if defined(INET) || defined(INET6)
|
|
if (fix_port) {
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
((struct sockaddr_in *)sa)->sin_port = sport;
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
((struct sockaddr_in6 *)sa)->sin6_port = sport;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
if (usrsctp_setsockopt(so, IPPROTO_SCTP, flags, sa, sa->sa_len) != 0) {
|
|
return (-1);
|
|
}
|
|
sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
|
|
#else
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
sa_len = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
sa_len = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
default:
|
|
sa_len = 0;
|
|
break;
|
|
}
|
|
/*
|
|
* Now, if there was a port mentioned, assure that the
|
|
* first address has that port to make sure it fails or
|
|
* succeeds correctly.
|
|
*/
|
|
#if defined(INET) || defined(INET6)
|
|
if (fix_port) {
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
((struct sockaddr_in *)sa)->sin_port = sport;
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
((struct sockaddr_in6 *)sa)->sin6_port = sport;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
if (usrsctp_setsockopt(so, IPPROTO_SCTP, flags, sa, (socklen_t)sa_len) != 0) {
|
|
return (-1);
|
|
}
|
|
sa = (struct sockaddr *)((caddr_t)sa + sa_len);
|
|
#endif
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
usrsctp_connectx(struct socket *so,
|
|
const struct sockaddr *addrs, int addrcnt,
|
|
sctp_assoc_t *id)
|
|
{
|
|
#if defined(INET) || defined(INET6)
|
|
char buf[SCTP_STACK_BUF_SIZE];
|
|
int i, ret, cnt, *aa;
|
|
char *cpto;
|
|
const struct sockaddr *at;
|
|
sctp_assoc_t *p_id;
|
|
size_t len = sizeof(int);
|
|
|
|
/* validate the address count and list */
|
|
if ((addrs == NULL) || (addrcnt <= 0)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
at = addrs;
|
|
cnt = 0;
|
|
cpto = ((caddr_t)buf + sizeof(int));
|
|
/* validate all the addresses and get the size */
|
|
for (i = 0; i < addrcnt; i++) {
|
|
switch (at->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
#ifdef HAVE_SA_LEN
|
|
if (at->sa_len != sizeof(struct sockaddr_in)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
#endif
|
|
len += sizeof(struct sockaddr_in);
|
|
if (len > SCTP_STACK_BUF_SIZE) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
memcpy(cpto, at, sizeof(struct sockaddr_in));
|
|
cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
|
|
at = (struct sockaddr *)((caddr_t)at + sizeof(struct sockaddr_in));
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
#ifdef HAVE_SA_LEN
|
|
if (at->sa_len != sizeof(struct sockaddr_in6)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
#endif
|
|
#ifdef INET
|
|
if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
|
|
len += sizeof(struct sockaddr_in);
|
|
if (len > SCTP_STACK_BUF_SIZE) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
|
|
cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
|
|
} else {
|
|
len += sizeof(struct sockaddr_in6);
|
|
if (len > SCTP_STACK_BUF_SIZE) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
memcpy(cpto, at, sizeof(struct sockaddr_in6));
|
|
cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in6));
|
|
}
|
|
#else
|
|
len += sizeof(struct sockaddr_in6);
|
|
if (len > SCTP_STACK_BUF_SIZE) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
memcpy(cpto, at, sizeof(struct sockaddr_in6));
|
|
cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in6));
|
|
#endif
|
|
at = (struct sockaddr *)((caddr_t)at + sizeof(struct sockaddr_in6));
|
|
break;
|
|
#endif
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
cnt++;
|
|
}
|
|
aa = (int *)buf;
|
|
*aa = cnt;
|
|
ret = usrsctp_setsockopt(so, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf, (socklen_t)len);
|
|
if ((ret == 0) && id) {
|
|
p_id = (sctp_assoc_t *)buf;
|
|
*id = *p_id;
|
|
}
|
|
return (ret);
|
|
#else
|
|
errno = EINVAL;
|
|
return (-1);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
usrsctp_getpaddrs(struct socket *so, sctp_assoc_t id, struct sockaddr **raddrs)
|
|
{
|
|
struct sctp_getaddresses *addrs;
|
|
struct sockaddr *sa;
|
|
sctp_assoc_t asoc;
|
|
caddr_t lim;
|
|
socklen_t opt_len;
|
|
int cnt;
|
|
|
|
if (raddrs == NULL) {
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
asoc = id;
|
|
opt_len = (socklen_t)sizeof(sctp_assoc_t);
|
|
if (usrsctp_getsockopt(so, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE, &asoc, &opt_len) != 0) {
|
|
return (-1);
|
|
}
|
|
/* size required is returned in 'asoc' */
|
|
opt_len = (socklen_t)((size_t)asoc + sizeof(struct sctp_getaddresses));
|
|
addrs = calloc(1, (size_t)opt_len);
|
|
if (addrs == NULL) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
addrs->sget_assoc_id = id;
|
|
/* Now lets get the array of addresses */
|
|
if (usrsctp_getsockopt(so, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES, addrs, &opt_len) != 0) {
|
|
free(addrs);
|
|
return (-1);
|
|
}
|
|
*raddrs = &addrs->addr[0].sa;
|
|
cnt = 0;
|
|
sa = &addrs->addr[0].sa;
|
|
lim = (caddr_t)addrs + opt_len;
|
|
#ifdef HAVE_SA_LEN
|
|
while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
|
|
sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
|
|
#else
|
|
while ((caddr_t)sa < lim) {
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in));
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6));
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_conn));
|
|
break;
|
|
default:
|
|
return (cnt);
|
|
break;
|
|
}
|
|
#endif
|
|
cnt++;
|
|
}
|
|
return (cnt);
|
|
}
|
|
|
|
void
|
|
usrsctp_freepaddrs(struct sockaddr *addrs)
|
|
{
|
|
/* Take away the hidden association id */
|
|
void *fr_addr;
|
|
|
|
fr_addr = (void *)((caddr_t)addrs - offsetof(struct sctp_getaddresses, addr));
|
|
/* Now free it */
|
|
free(fr_addr);
|
|
}
|
|
|
|
int
|
|
usrsctp_getladdrs(struct socket *so, sctp_assoc_t id, struct sockaddr **raddrs)
|
|
{
|
|
struct sctp_getaddresses *addrs;
|
|
caddr_t lim;
|
|
struct sockaddr *sa;
|
|
size_t size_of_addresses;
|
|
socklen_t opt_len;
|
|
int cnt;
|
|
|
|
if (raddrs == NULL) {
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
size_of_addresses = 0;
|
|
opt_len = (socklen_t)sizeof(int);
|
|
if (usrsctp_getsockopt(so, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE, &size_of_addresses, &opt_len) != 0) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
if (size_of_addresses == 0) {
|
|
errno = ENOTCONN;
|
|
return (-1);
|
|
}
|
|
opt_len = (socklen_t)(size_of_addresses + sizeof(struct sctp_getaddresses));
|
|
addrs = calloc(1, (size_t)opt_len);
|
|
if (addrs == NULL) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
addrs->sget_assoc_id = id;
|
|
/* Now lets get the array of addresses */
|
|
if (usrsctp_getsockopt(so, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs, &opt_len) != 0) {
|
|
free(addrs);
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
*raddrs = &addrs->addr[0].sa;
|
|
cnt = 0;
|
|
sa = &addrs->addr[0].sa;
|
|
lim = (caddr_t)addrs + opt_len;
|
|
#ifdef HAVE_SA_LEN
|
|
while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
|
|
sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
|
|
#else
|
|
while ((caddr_t)sa < lim) {
|
|
switch (sa->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in));
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6));
|
|
break;
|
|
#endif
|
|
case AF_CONN:
|
|
sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_conn));
|
|
break;
|
|
default:
|
|
return (cnt);
|
|
break;
|
|
}
|
|
#endif
|
|
cnt++;
|
|
}
|
|
return (cnt);
|
|
}
|
|
|
|
void
|
|
usrsctp_freeladdrs(struct sockaddr *addrs)
|
|
{
|
|
/* Take away the hidden association id */
|
|
void *fr_addr;
|
|
|
|
fr_addr = (void *)((caddr_t)addrs - offsetof(struct sctp_getaddresses, addr));
|
|
/* Now free it */
|
|
free(fr_addr);
|
|
}
|
|
|
|
#ifdef INET
|
|
void
|
|
sctp_userspace_ip_output(int *result, struct mbuf *o_pak,
|
|
sctp_route_t *ro, void *stcb,
|
|
uint32_t vrf_id)
|
|
{
|
|
struct mbuf *m;
|
|
struct mbuf *m_orig;
|
|
int iovcnt;
|
|
int len;
|
|
int send_count;
|
|
struct ip *ip;
|
|
struct udphdr *udp;
|
|
struct sockaddr_in dst;
|
|
#if defined(_WIN32)
|
|
WSAMSG win_msg_hdr;
|
|
DWORD win_sent_len;
|
|
WSABUF send_iovec[MAXLEN_MBUF_CHAIN];
|
|
WSABUF winbuf;
|
|
#else
|
|
struct iovec send_iovec[MAXLEN_MBUF_CHAIN];
|
|
struct msghdr msg_hdr;
|
|
#endif
|
|
int use_udp_tunneling;
|
|
|
|
*result = 0;
|
|
|
|
m = SCTP_HEADER_TO_CHAIN(o_pak);
|
|
m_orig = m;
|
|
|
|
len = sizeof(struct ip);
|
|
if (SCTP_BUF_LEN(m) < len) {
|
|
if ((m = m_pullup(m, len)) == 0) {
|
|
SCTP_PRINTF("Can not get the IP header in the first mbuf.\n");
|
|
return;
|
|
}
|
|
}
|
|
ip = mtod(m, struct ip *);
|
|
use_udp_tunneling = (ip->ip_p == IPPROTO_UDP);
|
|
|
|
if (use_udp_tunneling) {
|
|
len = sizeof(struct ip) + sizeof(struct udphdr);
|
|
if (SCTP_BUF_LEN(m) < len) {
|
|
if ((m = m_pullup(m, len)) == 0) {
|
|
SCTP_PRINTF("Can not get the UDP/IP header in the first mbuf.\n");
|
|
return;
|
|
}
|
|
ip = mtod(m, struct ip *);
|
|
}
|
|
udp = (struct udphdr *)(ip + 1);
|
|
} else {
|
|
udp = NULL;
|
|
}
|
|
|
|
if (!use_udp_tunneling) {
|
|
if (ip->ip_src.s_addr == INADDR_ANY) {
|
|
/* TODO get addr of outgoing interface */
|
|
SCTP_PRINTF("Why did the SCTP implementation did not choose a source address?\n");
|
|
}
|
|
/* TODO need to worry about ro->ro_dst as in ip_output? */
|
|
#if defined(__linux__) || defined(_WIN32) || (defined(__FreeBSD__) && (__FreeBSD_version >= 1100030))
|
|
/* need to put certain fields into network order for Linux */
|
|
ip->ip_len = htons(ip->ip_len);
|
|
#endif
|
|
}
|
|
|
|
memset((void *)&dst, 0, sizeof(struct sockaddr_in));
|
|
dst.sin_family = AF_INET;
|
|
dst.sin_addr.s_addr = ip->ip_dst.s_addr;
|
|
#ifdef HAVE_SIN_LEN
|
|
dst.sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
if (use_udp_tunneling) {
|
|
dst.sin_port = udp->uh_dport;
|
|
} else {
|
|
dst.sin_port = 0;
|
|
}
|
|
|
|
/* tweak the mbuf chain */
|
|
if (use_udp_tunneling) {
|
|
m_adj(m, sizeof(struct ip) + sizeof(struct udphdr));
|
|
}
|
|
|
|
send_count = 0;
|
|
for (iovcnt = 0; m != NULL && iovcnt < MAXLEN_MBUF_CHAIN; m = m->m_next, iovcnt++) {
|
|
#if !defined(_WIN32)
|
|
send_iovec[iovcnt].iov_base = (caddr_t)m->m_data;
|
|
send_iovec[iovcnt].iov_len = SCTP_BUF_LEN(m);
|
|
send_count += send_iovec[iovcnt].iov_len;
|
|
#else
|
|
send_iovec[iovcnt].buf = (caddr_t)m->m_data;
|
|
send_iovec[iovcnt].len = SCTP_BUF_LEN(m);
|
|
send_count += send_iovec[iovcnt].len;
|
|
#endif
|
|
}
|
|
|
|
if (m != NULL) {
|
|
SCTP_PRINTF("mbuf chain couldn't be copied completely\n");
|
|
goto free_mbuf;
|
|
}
|
|
|
|
#if !defined(_WIN32)
|
|
msg_hdr.msg_name = (struct sockaddr *) &dst;
|
|
msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
|
|
msg_hdr.msg_iov = send_iovec;
|
|
msg_hdr.msg_iovlen = iovcnt;
|
|
msg_hdr.msg_control = NULL;
|
|
msg_hdr.msg_controllen = 0;
|
|
msg_hdr.msg_flags = 0;
|
|
|
|
if ((!use_udp_tunneling) && (SCTP_BASE_VAR(userspace_rawsctp) != -1)) {
|
|
if (sendmsg(SCTP_BASE_VAR(userspace_rawsctp), &msg_hdr, MSG_DONTWAIT) < 0) {
|
|
*result = errno;
|
|
}
|
|
}
|
|
if ((use_udp_tunneling) && (SCTP_BASE_VAR(userspace_udpsctp) != -1)) {
|
|
if (sendmsg(SCTP_BASE_VAR(userspace_udpsctp), &msg_hdr, MSG_DONTWAIT) < 0) {
|
|
*result = errno;
|
|
}
|
|
}
|
|
#else
|
|
win_msg_hdr.name = (struct sockaddr *) &dst;
|
|
win_msg_hdr.namelen = sizeof(struct sockaddr_in);
|
|
win_msg_hdr.lpBuffers = (LPWSABUF)send_iovec;
|
|
win_msg_hdr.dwBufferCount = iovcnt;
|
|
winbuf.len = 0;
|
|
winbuf.buf = NULL;
|
|
win_msg_hdr.Control = winbuf;
|
|
win_msg_hdr.dwFlags = 0;
|
|
|
|
if ((!use_udp_tunneling) && (SCTP_BASE_VAR(userspace_rawsctp) != -1)) {
|
|
if (WSASendTo(SCTP_BASE_VAR(userspace_rawsctp), (LPWSABUF) send_iovec, iovcnt, &win_sent_len, win_msg_hdr.dwFlags, win_msg_hdr.name, (int) win_msg_hdr.namelen, NULL, NULL) != 0) {
|
|
*result = WSAGetLastError();
|
|
}
|
|
}
|
|
if ((use_udp_tunneling) && (SCTP_BASE_VAR(userspace_udpsctp) != -1)) {
|
|
if (WSASendTo(SCTP_BASE_VAR(userspace_udpsctp), (LPWSABUF) send_iovec, iovcnt, &win_sent_len, win_msg_hdr.dwFlags, win_msg_hdr.name, (int) win_msg_hdr.namelen, NULL, NULL) != 0) {
|
|
*result = WSAGetLastError();
|
|
}
|
|
}
|
|
#endif
|
|
free_mbuf:
|
|
sctp_m_freem(m_orig);
|
|
}
|
|
#endif
|
|
|
|
#if defined(INET6)
|
|
void sctp_userspace_ip6_output(int *result, struct mbuf *o_pak,
|
|
struct route_in6 *ro, void *stcb,
|
|
uint32_t vrf_id)
|
|
{
|
|
struct mbuf *m;
|
|
struct mbuf *m_orig;
|
|
int iovcnt;
|
|
int len;
|
|
int send_count;
|
|
struct ip6_hdr *ip6;
|
|
struct udphdr *udp;
|
|
struct sockaddr_in6 dst;
|
|
#if defined(_WIN32)
|
|
WSAMSG win_msg_hdr;
|
|
DWORD win_sent_len;
|
|
WSABUF send_iovec[MAXLEN_MBUF_CHAIN];
|
|
WSABUF winbuf;
|
|
#else
|
|
struct iovec send_iovec[MAXLEN_MBUF_CHAIN];
|
|
struct msghdr msg_hdr;
|
|
#endif
|
|
int use_udp_tunneling;
|
|
|
|
*result = 0;
|
|
|
|
m = SCTP_HEADER_TO_CHAIN(o_pak);
|
|
m_orig = m;
|
|
|
|
len = sizeof(struct ip6_hdr);
|
|
|
|
if (SCTP_BUF_LEN(m) < len) {
|
|
if ((m = m_pullup(m, len)) == 0) {
|
|
SCTP_PRINTF("Can not get the IP header in the first mbuf.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
use_udp_tunneling = (ip6->ip6_nxt == IPPROTO_UDP);
|
|
|
|
if (use_udp_tunneling) {
|
|
len = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
|
|
if (SCTP_BUF_LEN(m) < len) {
|
|
if ((m = m_pullup(m, len)) == 0) {
|
|
SCTP_PRINTF("Can not get the UDP/IP header in the first mbuf.\n");
|
|
return;
|
|
}
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
}
|
|
udp = (struct udphdr *)(ip6 + 1);
|
|
} else {
|
|
udp = NULL;
|
|
}
|
|
|
|
if (!use_udp_tunneling) {
|
|
if (ip6->ip6_src.s6_addr == in6addr_any.s6_addr) {
|
|
/* TODO get addr of outgoing interface */
|
|
SCTP_PRINTF("Why did the SCTP implementation did not choose a source address?\n");
|
|
}
|
|
/* TODO need to worry about ro->ro_dst as in ip_output? */
|
|
}
|
|
|
|
memset((void *)&dst, 0, sizeof(struct sockaddr_in6));
|
|
dst.sin6_family = AF_INET6;
|
|
dst.sin6_addr = ip6->ip6_dst;
|
|
#ifdef HAVE_SIN6_LEN
|
|
dst.sin6_len = sizeof(struct sockaddr_in6);
|
|
#endif
|
|
|
|
if (use_udp_tunneling) {
|
|
dst.sin6_port = udp->uh_dport;
|
|
} else {
|
|
dst.sin6_port = 0;
|
|
}
|
|
|
|
/* tweak the mbuf chain */
|
|
if (use_udp_tunneling) {
|
|
m_adj(m, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
|
|
} else {
|
|
m_adj(m, sizeof(struct ip6_hdr));
|
|
}
|
|
|
|
send_count = 0;
|
|
for (iovcnt = 0; m != NULL && iovcnt < MAXLEN_MBUF_CHAIN; m = m->m_next, iovcnt++) {
|
|
#if !defined(_WIN32)
|
|
send_iovec[iovcnt].iov_base = (caddr_t)m->m_data;
|
|
send_iovec[iovcnt].iov_len = SCTP_BUF_LEN(m);
|
|
send_count += send_iovec[iovcnt].iov_len;
|
|
#else
|
|
send_iovec[iovcnt].buf = (caddr_t)m->m_data;
|
|
send_iovec[iovcnt].len = SCTP_BUF_LEN(m);
|
|
send_count += send_iovec[iovcnt].len;
|
|
#endif
|
|
}
|
|
if (m != NULL) {
|
|
SCTP_PRINTF("mbuf chain couldn't be copied completely\n");
|
|
goto free_mbuf;
|
|
}
|
|
|
|
#if !defined(_WIN32)
|
|
msg_hdr.msg_name = (struct sockaddr *) &dst;
|
|
msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);
|
|
msg_hdr.msg_iov = send_iovec;
|
|
msg_hdr.msg_iovlen = iovcnt;
|
|
msg_hdr.msg_control = NULL;
|
|
msg_hdr.msg_controllen = 0;
|
|
msg_hdr.msg_flags = 0;
|
|
|
|
if ((!use_udp_tunneling) && (SCTP_BASE_VAR(userspace_rawsctp6) != -1)) {
|
|
if (sendmsg(SCTP_BASE_VAR(userspace_rawsctp6), &msg_hdr, MSG_DONTWAIT)< 0) {
|
|
*result = errno;
|
|
}
|
|
}
|
|
if ((use_udp_tunneling) && (SCTP_BASE_VAR(userspace_udpsctp6) != -1)) {
|
|
if (sendmsg(SCTP_BASE_VAR(userspace_udpsctp6), &msg_hdr, MSG_DONTWAIT) < 0) {
|
|
*result = errno;
|
|
}
|
|
}
|
|
#else
|
|
win_msg_hdr.name = (struct sockaddr *) &dst;
|
|
win_msg_hdr.namelen = sizeof(struct sockaddr_in6);
|
|
win_msg_hdr.lpBuffers = (LPWSABUF)send_iovec;
|
|
win_msg_hdr.dwBufferCount = iovcnt;
|
|
winbuf.len = 0;
|
|
winbuf.buf = NULL;
|
|
win_msg_hdr.Control = winbuf;
|
|
win_msg_hdr.dwFlags = 0;
|
|
|
|
if ((!use_udp_tunneling) && (SCTP_BASE_VAR(userspace_rawsctp6) != -1)) {
|
|
if (WSASendTo(SCTP_BASE_VAR(userspace_rawsctp6), (LPWSABUF) send_iovec, iovcnt, &win_sent_len, win_msg_hdr.dwFlags, win_msg_hdr.name, (int) win_msg_hdr.namelen, NULL, NULL) != 0) {
|
|
*result = WSAGetLastError();
|
|
}
|
|
}
|
|
if ((use_udp_tunneling) && (SCTP_BASE_VAR(userspace_udpsctp6) != -1)) {
|
|
if (WSASendTo(SCTP_BASE_VAR(userspace_udpsctp6), (LPWSABUF) send_iovec, iovcnt, &win_sent_len, win_msg_hdr.dwFlags, win_msg_hdr.name, (int) win_msg_hdr.namelen, NULL, NULL) != 0) {
|
|
*result = WSAGetLastError();
|
|
}
|
|
}
|
|
#endif
|
|
free_mbuf:
|
|
sctp_m_freem(m_orig);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
usrsctp_register_address(void *addr)
|
|
{
|
|
struct sockaddr_conn sconn;
|
|
|
|
memset(&sconn, 0, sizeof(struct sockaddr_conn));
|
|
sconn.sconn_family = AF_CONN;
|
|
#ifdef HAVE_SCONN_LEN
|
|
sconn.sconn_len = sizeof(struct sockaddr_conn);
|
|
#endif
|
|
sconn.sconn_port = 0;
|
|
sconn.sconn_addr = addr;
|
|
sctp_add_addr_to_vrf(SCTP_DEFAULT_VRFID,
|
|
NULL,
|
|
0xffffffff,
|
|
0,
|
|
"conn",
|
|
NULL,
|
|
(struct sockaddr *)&sconn,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
void
|
|
usrsctp_deregister_address(void *addr)
|
|
{
|
|
struct sockaddr_conn sconn;
|
|
|
|
memset(&sconn, 0, sizeof(struct sockaddr_conn));
|
|
sconn.sconn_family = AF_CONN;
|
|
#ifdef HAVE_SCONN_LEN
|
|
sconn.sconn_len = sizeof(struct sockaddr_conn);
|
|
#endif
|
|
sconn.sconn_port = 0;
|
|
sconn.sconn_addr = addr;
|
|
sctp_del_addr_from_vrf(SCTP_DEFAULT_VRFID,
|
|
(struct sockaddr *)&sconn,
|
|
0xffffffff,
|
|
"conn");
|
|
}
|
|
|
|
#define PREAMBLE_FORMAT "\n%c %02d:%02d:%02d.%06ld "
|
|
#define PREAMBLE_LENGTH 19
|
|
#define HEADER "0000 "
|
|
#define TRAILER "# SCTP_PACKET\n"
|
|
|
|
char *
|
|
usrsctp_dumppacket(const void *buf, size_t len, int outbound)
|
|
{
|
|
size_t i, pos;
|
|
char *dump_buf, *packet;
|
|
struct tm t;
|
|
#ifdef _WIN32
|
|
struct timeb tb;
|
|
#else
|
|
struct timeval tv;
|
|
time_t sec;
|
|
#endif
|
|
|
|
if ((len == 0) || (buf == NULL)) {
|
|
return (NULL);
|
|
}
|
|
if ((dump_buf = malloc(PREAMBLE_LENGTH + strlen(HEADER) + 3 * len + strlen(TRAILER) + 1)) == NULL) {
|
|
return (NULL);
|
|
}
|
|
pos = 0;
|
|
#ifdef _WIN32
|
|
ftime(&tb);
|
|
localtime_s(&t, &tb.time);
|
|
#if defined(__MINGW32__)
|
|
if (snprintf(dump_buf, PREAMBLE_LENGTH + 1, PREAMBLE_FORMAT,
|
|
outbound ? 'O' : 'I',
|
|
t.tm_hour, t.tm_min, t.tm_sec, (long)(1000 * tb.millitm)) < 0) {
|
|
free(dump_buf);
|
|
return (NULL);
|
|
}
|
|
#else
|
|
if (_snprintf_s(dump_buf, PREAMBLE_LENGTH + 1, PREAMBLE_LENGTH, PREAMBLE_FORMAT,
|
|
outbound ? 'O' : 'I',
|
|
t.tm_hour, t.tm_min, t.tm_sec, (long)(1000 * tb.millitm)) < 0) {
|
|
free(dump_buf);
|
|
return (NULL);
|
|
}
|
|
#endif
|
|
#else
|
|
gettimeofday(&tv, NULL);
|
|
sec = (time_t)tv.tv_sec;
|
|
localtime_r((const time_t *)&sec, &t);
|
|
if (snprintf(dump_buf, PREAMBLE_LENGTH + 1, PREAMBLE_FORMAT,
|
|
outbound ? 'O' : 'I',
|
|
t.tm_hour, t.tm_min, t.tm_sec, (long)tv.tv_usec) < 0) {
|
|
free(dump_buf);
|
|
return (NULL);
|
|
}
|
|
#endif
|
|
pos += PREAMBLE_LENGTH;
|
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
|
strncpy_s(dump_buf + pos, strlen(HEADER) + 1, HEADER, strlen(HEADER));
|
|
#else
|
|
strcpy(dump_buf + pos, HEADER);
|
|
#endif
|
|
pos += strlen(HEADER);
|
|
packet = (char *)buf;
|
|
for (i = 0; i < len; i++) {
|
|
uint8_t byte, low, high;
|
|
|
|
byte = (uint8_t)packet[i];
|
|
high = byte / 16;
|
|
low = byte % 16;
|
|
dump_buf[pos++] = high < 10 ? '0' + high : 'a' + (high - 10);
|
|
dump_buf[pos++] = low < 10 ? '0' + low : 'a' + (low - 10);
|
|
dump_buf[pos++] = ' ';
|
|
}
|
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
|
strncpy_s(dump_buf + pos, strlen(TRAILER) + 1, TRAILER, strlen(TRAILER));
|
|
#else
|
|
strcpy(dump_buf + pos, TRAILER);
|
|
#endif
|
|
pos += strlen(TRAILER);
|
|
dump_buf[pos++] = '\0';
|
|
return (dump_buf);
|
|
}
|
|
|
|
void
|
|
usrsctp_freedumpbuffer(char *buf)
|
|
{
|
|
free(buf);
|
|
}
|
|
|
|
void
|
|
usrsctp_enable_crc32c_offload(void)
|
|
{
|
|
SCTP_BASE_VAR(crc32c_offloaded) = 1;
|
|
}
|
|
|
|
void
|
|
usrsctp_disable_crc32c_offload(void)
|
|
{
|
|
SCTP_BASE_VAR(crc32c_offloaded) = 0;
|
|
}
|
|
|
|
/* Compute the CRC32C in network byte order */
|
|
uint32_t
|
|
usrsctp_crc32c(void *buffer, size_t length)
|
|
{
|
|
uint32_t base = 0xffffffff;
|
|
|
|
base = calculate_crc32c(0xffffffff, (unsigned char *)buffer, (unsigned int) length);
|
|
base = sctp_finalize_crc32c(base);
|
|
return (base);
|
|
}
|
|
|
|
void
|
|
usrsctp_conninput(void *addr, const void *buffer, size_t length, uint8_t ecn_bits)
|
|
{
|
|
struct sockaddr_conn src, dst;
|
|
struct mbuf *m, *mm;
|
|
struct sctphdr *sh;
|
|
struct sctp_chunkhdr *ch;
|
|
int remaining;
|
|
|
|
SCTP_STAT_INCR(sctps_recvpackets);
|
|
SCTP_STAT_INCR_COUNTER64(sctps_inpackets);
|
|
memset(&src, 0, sizeof(struct sockaddr_conn));
|
|
src.sconn_family = AF_CONN;
|
|
#ifdef HAVE_SCONN_LEN
|
|
src.sconn_len = sizeof(struct sockaddr_conn);
|
|
#endif
|
|
src.sconn_addr = addr;
|
|
memset(&dst, 0, sizeof(struct sockaddr_conn));
|
|
dst.sconn_family = AF_CONN;
|
|
#ifdef HAVE_SCONN_LEN
|
|
dst.sconn_len = sizeof(struct sockaddr_conn);
|
|
#endif
|
|
dst.sconn_addr = addr;
|
|
if ((m = sctp_get_mbuf_for_msg((unsigned int)length, 1, M_NOWAIT, 0, MT_DATA)) == NULL) {
|
|
return;
|
|
}
|
|
/* Set the lengths fields of the mbuf chain.
|
|
* This is expected by m_copyback().
|
|
*/
|
|
remaining = (int)length;
|
|
for (mm = m; mm != NULL; mm = mm->m_next) {
|
|
mm->m_len = min((int)M_SIZE(mm), remaining);
|
|
m->m_pkthdr.len += mm->m_len;
|
|
remaining -= mm->m_len;
|
|
}
|
|
KASSERT(remaining == 0, ("usrsctp_conninput: %zu bytes left", remaining));
|
|
m_copyback(m, 0, (int)length, (caddr_t)buffer);
|
|
if (SCTP_BUF_LEN(m) < (int)(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr))) {
|
|
if ((m = m_pullup(m, sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr))) == NULL) {
|
|
SCTP_STAT_INCR(sctps_hdrops);
|
|
return;
|
|
}
|
|
}
|
|
sh = mtod(m, struct sctphdr *);;
|
|
ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr));
|
|
src.sconn_port = sh->src_port;
|
|
dst.sconn_port = sh->dest_port;
|
|
sctp_common_input_processing(&m, 0, sizeof(struct sctphdr), (int)length,
|
|
(struct sockaddr *)&src,
|
|
(struct sockaddr *)&dst,
|
|
sh, ch,
|
|
SCTP_BASE_VAR(crc32c_offloaded) == 1 ? 0 : 1,
|
|
ecn_bits,
|
|
SCTP_DEFAULT_VRFID, 0);
|
|
if (m) {
|
|
sctp_m_freem(m);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void usrsctp_handle_timers(uint32_t elapsed_milliseconds)
|
|
{
|
|
sctp_handle_tick(sctp_msecs_to_ticks(elapsed_milliseconds));
|
|
}
|
|
|
|
int
|
|
usrsctp_get_events(struct socket *so)
|
|
{
|
|
int events = 0;
|
|
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
SOCK_LOCK(so);
|
|
if (soreadable(so)) {
|
|
events |= SCTP_EVENT_READ;
|
|
}
|
|
if (sowriteable(so)) {
|
|
events |= SCTP_EVENT_WRITE;
|
|
}
|
|
if (so->so_error) {
|
|
events |= SCTP_EVENT_ERROR;
|
|
}
|
|
SOCK_UNLOCK(so);
|
|
|
|
return events;
|
|
}
|
|
|
|
int
|
|
usrsctp_set_upcall(struct socket *so, void (*upcall)(struct socket *, void *, int), void *arg)
|
|
{
|
|
if (so == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
|
|
SOCK_LOCK(so);
|
|
so->so_upcall = upcall;
|
|
so->so_upcallarg = arg;
|
|
so->so_snd.sb_flags |= SB_UPCALL;
|
|
so->so_rcv.sb_flags |= SB_UPCALL;
|
|
SOCK_UNLOCK(so);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#define USRSCTP_TUNABLE_SET_DEF(__field, __prefix) \
|
|
int usrsctp_tunable_set_ ## __field(uint32_t value) \
|
|
{ \
|
|
if ((value < __prefix##_MIN) || \
|
|
(value > __prefix##_MAX)) { \
|
|
errno = EINVAL; \
|
|
return (-1); \
|
|
} else { \
|
|
SCTP_BASE_SYSCTL(__field) = value; \
|
|
return (0); \
|
|
} \
|
|
}
|
|
|
|
USRSCTP_TUNABLE_SET_DEF(sctp_hashtblsize, SCTPCTL_TCBHASHSIZE)
|
|
USRSCTP_TUNABLE_SET_DEF(sctp_pcbtblsize, SCTPCTL_PCBHASHSIZE)
|
|
USRSCTP_TUNABLE_SET_DEF(sctp_chunkscale, SCTPCTL_CHUNKSCALE)
|
|
|
|
#define USRSCTP_SYSCTL_SET_DEF(__field, __prefix) \
|
|
int usrsctp_sysctl_set_ ## __field(uint32_t value) \
|
|
{ \
|
|
if ((value < __prefix##_MIN) || \
|
|
(value > __prefix##_MAX)) { \
|
|
errno = EINVAL; \
|
|
return (-1); \
|
|
} else { \
|
|
SCTP_BASE_SYSCTL(__field) = value; \
|
|
return (0); \
|
|
} \
|
|
}
|
|
|
|
#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wtype-limits"
|
|
#endif
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_sendspace, SCTPCTL_MAXDGRAM)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_recvspace, SCTPCTL_RECVSPACE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_auto_asconf, SCTPCTL_AUTOASCONF)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_ecn_enable, SCTPCTL_ECN_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_pr_enable, SCTPCTL_PR_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_auth_enable, SCTPCTL_AUTH_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_asconf_enable, SCTPCTL_ASCONF_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_reconfig_enable, SCTPCTL_RECONFIG_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_nrsack_enable, SCTPCTL_NRSACK_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_pktdrop_enable, SCTPCTL_PKTDROP_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_no_csum_on_loopback, SCTPCTL_LOOPBACK_NOCSUM)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_peer_chunk_oh, SCTPCTL_PEER_CHKOH)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_max_burst_default, SCTPCTL_MAXBURST)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_max_chunks_on_queue, SCTPCTL_MAXCHUNKS)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_min_split_point, SCTPCTL_MIN_SPLIT_POINT)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_delayed_sack_time_default, SCTPCTL_DELAYED_SACK_TIME)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_sack_freq_default, SCTPCTL_SACK_FREQ)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_system_free_resc_limit, SCTPCTL_SYS_RESOURCE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_asoc_free_resc_limit, SCTPCTL_ASOC_RESOURCE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_heartbeat_interval_default, SCTPCTL_HEARTBEAT_INTERVAL)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_pmtu_raise_time_default, SCTPCTL_PMTU_RAISE_TIME)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_shutdown_guard_time_default, SCTPCTL_SHUTDOWN_GUARD_TIME)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_secret_lifetime_default, SCTPCTL_SECRET_LIFETIME)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_rto_max_default, SCTPCTL_RTO_MAX)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_rto_min_default, SCTPCTL_RTO_MIN)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_rto_initial_default, SCTPCTL_RTO_INITIAL)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_init_rto_max_default, SCTPCTL_INIT_RTO_MAX)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_valid_cookie_life_default, SCTPCTL_VALID_COOKIE_LIFE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_init_rtx_max_default, SCTPCTL_INIT_RTX_MAX)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_assoc_rtx_max_default, SCTPCTL_ASSOC_RTX_MAX)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_path_rtx_max_default, SCTPCTL_PATH_RTX_MAX)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_add_more_threshold, SCTPCTL_ADD_MORE_ON_OUTPUT)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_nr_incoming_streams_default, SCTPCTL_INCOMING_STREAMS)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_nr_outgoing_streams_default, SCTPCTL_OUTGOING_STREAMS)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_cmt_on_off, SCTPCTL_CMT_ON_OFF)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_cmt_use_dac, SCTPCTL_CMT_USE_DAC)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_use_cwnd_based_maxburst, SCTPCTL_CWND_MAXBURST)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_nat_friendly, SCTPCTL_NAT_FRIENDLY)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_L2_abc_variable, SCTPCTL_ABC_L_VAR)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_mbuf_threshold_count, SCTPCTL_MAX_CHAINED_MBUFS)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_do_drain, SCTPCTL_DO_SCTP_DRAIN)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_hb_maxburst, SCTPCTL_HB_MAX_BURST)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_abort_if_one_2_one_hits_limit, SCTPCTL_ABORT_AT_LIMIT)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_min_residual, SCTPCTL_MIN_RESIDUAL)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_max_retran_chunk, SCTPCTL_MAX_RETRAN_CHUNK)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_logging_level, SCTPCTL_LOGGING_LEVEL)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_default_cc_module, SCTPCTL_DEFAULT_CC_MODULE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_default_frag_interleave, SCTPCTL_DEFAULT_FRAG_INTERLEAVE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_mobility_base, SCTPCTL_MOBILITY_BASE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_mobility_fasthandoff, SCTPCTL_MOBILITY_FASTHANDOFF)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_inits_include_nat_friendly, SCTPCTL_NAT_FRIENDLY_INITS)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_udp_tunneling_port, SCTPCTL_UDP_TUNNELING_PORT)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_enable_sack_immediately, SCTPCTL_SACK_IMMEDIATELY_ENABLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_vtag_time_wait, SCTPCTL_TIME_WAIT)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_blackhole, SCTPCTL_BLACKHOLE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_diag_info_code, SCTPCTL_DIAG_INFO_CODE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_fr_max_burst_default, SCTPCTL_FRMAXBURST)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_path_pf_threshold, SCTPCTL_PATH_PF_THRESHOLD)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_default_ss_module, SCTPCTL_DEFAULT_SS_MODULE)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_rttvar_bw, SCTPCTL_RTTVAR_BW)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_rttvar_rtt, SCTPCTL_RTTVAR_RTT)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_rttvar_eqret, SCTPCTL_RTTVAR_EQRET)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_steady_step, SCTPCTL_RTTVAR_STEADYS)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_use_dccc_ecn, SCTPCTL_RTTVAR_DCCCECN)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_buffer_splitting, SCTPCTL_BUFFER_SPLITTING)
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_initial_cwnd, SCTPCTL_INITIAL_CWND)
|
|
#ifdef SCTP_DEBUG
|
|
USRSCTP_SYSCTL_SET_DEF(sctp_debug_on, SCTPCTL_DEBUG)
|
|
#endif
|
|
#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
#define USRSCTP_SYSCTL_GET_DEF(__field) \
|
|
uint32_t usrsctp_sysctl_get_ ## __field(void) { \
|
|
return SCTP_BASE_SYSCTL(__field); \
|
|
}
|
|
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_sendspace)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_recvspace)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_auto_asconf)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_multiple_asconfs)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_ecn_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_pr_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_auth_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_asconf_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_reconfig_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_nrsack_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_pktdrop_enable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_no_csum_on_loopback)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_peer_chunk_oh)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_max_burst_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_max_chunks_on_queue)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_hashtblsize)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_pcbtblsize)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_min_split_point)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_chunkscale)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_delayed_sack_time_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_sack_freq_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_system_free_resc_limit)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_asoc_free_resc_limit)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_heartbeat_interval_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_pmtu_raise_time_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_shutdown_guard_time_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_secret_lifetime_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_rto_max_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_rto_min_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_rto_initial_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_init_rto_max_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_valid_cookie_life_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_init_rtx_max_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_assoc_rtx_max_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_path_rtx_max_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_add_more_threshold)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_nr_incoming_streams_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_nr_outgoing_streams_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_cmt_on_off)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_cmt_use_dac)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_use_cwnd_based_maxburst)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_nat_friendly)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_L2_abc_variable)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_mbuf_threshold_count)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_do_drain)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_hb_maxburst)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_abort_if_one_2_one_hits_limit)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_min_residual)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_max_retran_chunk)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_logging_level)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_default_cc_module)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_default_frag_interleave)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_mobility_base)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_mobility_fasthandoff)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_inits_include_nat_friendly)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_udp_tunneling_port)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_enable_sack_immediately)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_vtag_time_wait)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_blackhole)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_diag_info_code)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_fr_max_burst_default)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_path_pf_threshold)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_default_ss_module)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_rttvar_bw)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_rttvar_rtt)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_rttvar_eqret)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_steady_step)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_use_dccc_ecn)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_buffer_splitting)
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_initial_cwnd)
|
|
#ifdef SCTP_DEBUG
|
|
USRSCTP_SYSCTL_GET_DEF(sctp_debug_on)
|
|
#endif
|
|
|
|
void usrsctp_get_stat(struct sctpstat *stat)
|
|
{
|
|
*stat = SCTP_BASE_STATS;
|
|
}
|