Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/uipc_socket.c
Show First 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_inet.h" | #include "opt_inet.h" | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include "opt_compat.h" | #include "opt_compat.h" | ||||
#include "opt_sctp.h" | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mac.h> | #include <sys/mac.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
Show All 32 Lines | |||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <compat/freebsd32/freebsd32.h> | #include <compat/freebsd32/freebsd32.h> | ||||
#endif | #endif | ||||
static int soreceive_rcvoob(struct socket *so, struct uio *uio, | static int soreceive_rcvoob(struct socket *so, struct uio *uio, | ||||
int flags); | int flags); | ||||
static void so_rdknl_lock(void *); | |||||
static void so_rdknl_unlock(void *); | |||||
static void so_rdknl_assert_locked(void *); | |||||
static void so_rdknl_assert_unlocked(void *); | |||||
static void so_wrknl_lock(void *); | |||||
static void so_wrknl_unlock(void *); | |||||
static void so_wrknl_assert_locked(void *); | |||||
static void so_wrknl_assert_unlocked(void *); | |||||
static void filt_sordetach(struct knote *kn); | static void filt_sordetach(struct knote *kn); | ||||
static int filt_soread(struct knote *kn, long hint); | static int filt_soread(struct knote *kn, long hint); | ||||
static void filt_sowdetach(struct knote *kn); | static void filt_sowdetach(struct knote *kn); | ||||
static int filt_sowrite(struct knote *kn, long hint); | static int filt_sowrite(struct knote *kn, long hint); | ||||
static int inline hhook_run_socket(struct socket *so, void *hctx, int32_t h_id); | |||||
static int filt_soempty(struct knote *kn, long hint); | static int filt_soempty(struct knote *kn, long hint); | ||||
static void solisten_wakeup(struct socket *); | |||||
static int inline hhook_run_socket(struct socket *so, void *hctx, int32_t h_id); | |||||
fo_kqfilter_t soo_kqfilter; | fo_kqfilter_t soo_kqfilter; | ||||
static struct filterops soread_filtops = { | static struct filterops soread_filtops = { | ||||
.f_isfd = 1, | .f_isfd = 1, | ||||
.f_detach = filt_sordetach, | .f_detach = filt_sordetach, | ||||
.f_event = filt_soread, | .f_event = filt_soread, | ||||
}; | }; | ||||
static struct filterops sowrite_filtops = { | static struct filterops sowrite_filtops = { | ||||
▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | if (mac_socket_init(so, M_NOWAIT) != 0) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
#endif | #endif | ||||
if (khelp_init_osd(HELPER_CLASS_SOCKET, &so->osd)) { | if (khelp_init_osd(HELPER_CLASS_SOCKET, &so->osd)) { | ||||
uma_zfree(socket_zone, so); | uma_zfree(socket_zone, so); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | |||||
* The socket locking protocol allows to lock 2 sockets at a time, | |||||
* however, the first one must be a listening socket. WITNESS lacks | |||||
* a feature to change class of an existing lock, so we use DUPOK. | |||||
*/ | |||||
mtx_init(&so->so_lock, "socket", NULL, MTX_DEF | MTX_DUPOK); | |||||
SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd"); | SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd"); | ||||
SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv"); | SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv"); | ||||
so->so_rcv.sb_sel = &so->so_rdsel; | |||||
so->so_snd.sb_sel = &so->so_wrsel; | |||||
sx_init(&so->so_snd.sb_sx, "so_snd_sx"); | sx_init(&so->so_snd.sb_sx, "so_snd_sx"); | ||||
sx_init(&so->so_rcv.sb_sx, "so_rcv_sx"); | sx_init(&so->so_rcv.sb_sx, "so_rcv_sx"); | ||||
TAILQ_INIT(&so->so_snd.sb_aiojobq); | TAILQ_INIT(&so->so_snd.sb_aiojobq); | ||||
TAILQ_INIT(&so->so_rcv.sb_aiojobq); | TAILQ_INIT(&so->so_rcv.sb_aiojobq); | ||||
TASK_INIT(&so->so_snd.sb_aiotask, 0, soaio_snd, so); | TASK_INIT(&so->so_snd.sb_aiotask, 0, soaio_snd, so); | ||||
TASK_INIT(&so->so_rcv.sb_aiotask, 0, soaio_rcv, so); | TASK_INIT(&so->so_rcv.sb_aiotask, 0, soaio_rcv, so); | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
VNET_ASSERT(vnet != NULL, ("%s:%d vnet is NULL, so=%p", | VNET_ASSERT(vnet != NULL, ("%s:%d vnet is NULL, so=%p", | ||||
Show All 39 Lines | |||||
#endif | #endif | ||||
mtx_unlock(&so_global_mtx); | mtx_unlock(&so_global_mtx); | ||||
if (so->so_rcv.sb_hiwat) | if (so->so_rcv.sb_hiwat) | ||||
(void)chgsbsize(so->so_cred->cr_uidinfo, | (void)chgsbsize(so->so_cred->cr_uidinfo, | ||||
&so->so_rcv.sb_hiwat, 0, RLIM_INFINITY); | &so->so_rcv.sb_hiwat, 0, RLIM_INFINITY); | ||||
if (so->so_snd.sb_hiwat) | if (so->so_snd.sb_hiwat) | ||||
(void)chgsbsize(so->so_cred->cr_uidinfo, | (void)chgsbsize(so->so_cred->cr_uidinfo, | ||||
&so->so_snd.sb_hiwat, 0, RLIM_INFINITY); | &so->so_snd.sb_hiwat, 0, RLIM_INFINITY); | ||||
/* remove accept filter if one is present. */ | |||||
if (so->so_accf != NULL) | |||||
do_setopt_accept_filter(so, NULL); | |||||
#ifdef MAC | #ifdef MAC | ||||
mac_socket_destroy(so); | mac_socket_destroy(so); | ||||
#endif | #endif | ||||
hhook_run_socket(so, NULL, HHOOK_SOCKET_CLOSE); | hhook_run_socket(so, NULL, HHOOK_SOCKET_CLOSE); | ||||
crfree(so->so_cred); | crfree(so->so_cred); | ||||
khelp_destroy_osd(&so->osd); | khelp_destroy_osd(&so->osd); | ||||
if (SOLISTENING(so)) { | |||||
if (so->sol_accept_filter != NULL) | |||||
accept_filt_setopt(so, NULL); | |||||
} else { | |||||
sx_destroy(&so->so_snd.sb_sx); | sx_destroy(&so->so_snd.sb_sx); | ||||
sx_destroy(&so->so_rcv.sb_sx); | sx_destroy(&so->so_rcv.sb_sx); | ||||
SOCKBUF_LOCK_DESTROY(&so->so_snd); | SOCKBUF_LOCK_DESTROY(&so->so_snd); | ||||
SOCKBUF_LOCK_DESTROY(&so->so_rcv); | SOCKBUF_LOCK_DESTROY(&so->so_rcv); | ||||
} | |||||
mtx_destroy(&so->so_lock); | |||||
uma_zfree(socket_zone, so); | uma_zfree(socket_zone, so); | ||||
} | } | ||||
/* | /* | ||||
* socreate returns a socket with a ref count of 1. The socket should be | * socreate returns a socket with a ref count of 1. The socket should be | ||||
* closed with soclose(). | * closed with soclose(). | ||||
*/ | */ | ||||
int | int | ||||
Show All 26 Lines | if (prison_check_af(cred, prp->pr_domain->dom_family) != 0) | ||||
return (EPROTONOSUPPORT); | return (EPROTONOSUPPORT); | ||||
if (prp->pr_type != type) | if (prp->pr_type != type) | ||||
return (EPROTOTYPE); | return (EPROTOTYPE); | ||||
so = soalloc(CRED_TO_VNET(cred)); | so = soalloc(CRED_TO_VNET(cred)); | ||||
if (so == NULL) | if (so == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
TAILQ_INIT(&so->so_incomp); | |||||
TAILQ_INIT(&so->so_comp); | |||||
so->so_type = type; | so->so_type = type; | ||||
so->so_cred = crhold(cred); | so->so_cred = crhold(cred); | ||||
if ((prp->pr_domain->dom_family == PF_INET) || | if ((prp->pr_domain->dom_family == PF_INET) || | ||||
(prp->pr_domain->dom_family == PF_INET6) || | (prp->pr_domain->dom_family == PF_INET6) || | ||||
(prp->pr_domain->dom_family == PF_ROUTE)) | (prp->pr_domain->dom_family == PF_ROUTE)) | ||||
so->so_fibnum = td->td_proc->p_fibnum; | so->so_fibnum = td->td_proc->p_fibnum; | ||||
else | else | ||||
so->so_fibnum = 0; | so->so_fibnum = 0; | ||||
so->so_proto = prp; | so->so_proto = prp; | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_socket_create(cred, so); | mac_socket_create(cred, so); | ||||
#endif | #endif | ||||
knlist_init_mtx(&so->so_rcv.sb_sel.si_note, SOCKBUF_MTX(&so->so_rcv)); | knlist_init(&so->so_rdsel.si_note, so, so_rdknl_lock, so_rdknl_unlock, | ||||
knlist_init_mtx(&so->so_snd.sb_sel.si_note, SOCKBUF_MTX(&so->so_snd)); | so_rdknl_assert_locked, so_rdknl_assert_unlocked); | ||||
so->so_count = 1; | knlist_init(&so->so_wrsel.si_note, so, so_wrknl_lock, so_wrknl_unlock, | ||||
so_wrknl_assert_locked, so_wrknl_assert_unlocked); | |||||
/* | /* | ||||
* Auto-sizing of socket buffers is managed by the protocols and | * Auto-sizing of socket buffers is managed by the protocols and | ||||
* the appropriate flags must be set in the pru_attach function. | * the appropriate flags must be set in the pru_attach function. | ||||
*/ | */ | ||||
CURVNET_SET(so->so_vnet); | CURVNET_SET(so->so_vnet); | ||||
error = (*prp->pr_usrreqs->pru_attach)(so, proto, td); | error = (*prp->pr_usrreqs->pru_attach)(so, proto, td); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
if (error) { | if (error) { | ||||
KASSERT(so->so_count == 1, ("socreate: so_count %d", | |||||
so->so_count)); | |||||
so->so_count = 0; | |||||
sodealloc(so); | sodealloc(so); | ||||
return (error); | return (error); | ||||
} | } | ||||
soref(so); | |||||
*aso = so; | *aso = so; | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef REGRESSION | #ifdef REGRESSION | ||||
static int regression_sonewconn_earlytest = 1; | static int regression_sonewconn_earlytest = 1; | ||||
SYSCTL_INT(_regression, OID_AUTO, sonewconn_earlytest, CTLFLAG_RW, | SYSCTL_INT(_regression, OID_AUTO, sonewconn_earlytest, CTLFLAG_RW, | ||||
®ression_sonewconn_earlytest, 0, "Perform early sonewconn limit test"); | ®ression_sonewconn_earlytest, 0, "Perform early sonewconn limit test"); | ||||
Show All 11 Lines | |||||
struct socket * | struct socket * | ||||
sonewconn(struct socket *head, int connstatus) | sonewconn(struct socket *head, int connstatus) | ||||
{ | { | ||||
static struct timeval lastover; | static struct timeval lastover; | ||||
static struct timeval overinterval = { 60, 0 }; | static struct timeval overinterval = { 60, 0 }; | ||||
static int overcount; | static int overcount; | ||||
struct socket *so; | struct socket *so; | ||||
int over; | u_int over; | ||||
ACCEPT_LOCK(); | SOLISTEN_LOCK(head); | ||||
over = (head->so_qlen > 3 * head->so_qlimit / 2); | over = (head->sol_qlen > 3 * head->sol_qlimit / 2); | ||||
ACCEPT_UNLOCK(); | SOLISTEN_UNLOCK(head); | ||||
#ifdef REGRESSION | #ifdef REGRESSION | ||||
if (regression_sonewconn_earlytest && over) { | if (regression_sonewconn_earlytest && over) { | ||||
#else | #else | ||||
if (over) { | if (over) { | ||||
#endif | #endif | ||||
overcount++; | overcount++; | ||||
if (ratecheck(&lastover, &overinterval)) { | if (ratecheck(&lastover, &overinterval)) { | ||||
log(LOG_DEBUG, "%s: pcb %p: Listen queue overflow: " | log(LOG_DEBUG, "%s: pcb %p: Listen queue overflow: " | ||||
"%i already in queue awaiting acceptance " | "%i already in queue awaiting acceptance " | ||||
"(%d occurrences)\n", | "(%d occurrences)\n", | ||||
__func__, head->so_pcb, head->so_qlen, overcount); | __func__, head->so_pcb, head->sol_qlen, overcount); | ||||
overcount = 0; | overcount = 0; | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
VNET_ASSERT(head->so_vnet != NULL, ("%s:%d so_vnet is NULL, head=%p", | VNET_ASSERT(head->so_vnet != NULL, ("%s: so %p vnet is NULL", | ||||
__func__, __LINE__, head)); | __func__, head)); | ||||
so = soalloc(head->so_vnet); | so = soalloc(head->so_vnet); | ||||
if (so == NULL) { | if (so == NULL) { | ||||
log(LOG_DEBUG, "%s: pcb %p: New socket allocation failure: " | log(LOG_DEBUG, "%s: pcb %p: New socket allocation failure: " | ||||
"limit reached or out of memory\n", | "limit reached or out of memory\n", | ||||
__func__, head->so_pcb); | __func__, head->so_pcb); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if ((head->so_options & SO_ACCEPTFILTER) != 0) | if (head->sol_accept_filter != NULL) | ||||
connstatus = 0; | connstatus = 0; | ||||
so->so_head = head; | so->so_listen = head; | ||||
so->so_type = head->so_type; | so->so_type = head->so_type; | ||||
so->so_options = head->so_options &~ SO_ACCEPTCONN; | so->so_options = head->so_options &~ SO_ACCEPTCONN; | ||||
so->so_linger = head->so_linger; | so->so_linger = head->so_linger; | ||||
so->so_state = head->so_state | SS_NOFDREF; | so->so_state = head->so_state | SS_NOFDREF; | ||||
so->so_fibnum = head->so_fibnum; | so->so_fibnum = head->so_fibnum; | ||||
so->so_proto = head->so_proto; | so->so_proto = head->so_proto; | ||||
so->so_cred = crhold(head->so_cred); | so->so_cred = crhold(head->so_cred); | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_socket_newconn(head, so); | mac_socket_newconn(head, so); | ||||
#endif | #endif | ||||
knlist_init_mtx(&so->so_rcv.sb_sel.si_note, SOCKBUF_MTX(&so->so_rcv)); | knlist_init(&so->so_rdsel.si_note, so, so_rdknl_lock, so_rdknl_unlock, | ||||
knlist_init_mtx(&so->so_snd.sb_sel.si_note, SOCKBUF_MTX(&so->so_snd)); | so_rdknl_assert_locked, so_rdknl_assert_unlocked); | ||||
knlist_init(&so->so_wrsel.si_note, so, so_wrknl_lock, so_wrknl_unlock, | |||||
so_wrknl_assert_locked, so_wrknl_assert_unlocked); | |||||
VNET_SO_ASSERT(head); | VNET_SO_ASSERT(head); | ||||
if (soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat)) { | if (soreserve(so, head->sol_sbsnd_hiwat, head->sol_sbrcv_hiwat)) { | ||||
sodealloc(so); | sodealloc(so); | ||||
log(LOG_DEBUG, "%s: pcb %p: soreserve() failed\n", | log(LOG_DEBUG, "%s: pcb %p: soreserve() failed\n", | ||||
__func__, head->so_pcb); | __func__, head->so_pcb); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0, NULL)) { | if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0, NULL)) { | ||||
sodealloc(so); | sodealloc(so); | ||||
log(LOG_DEBUG, "%s: pcb %p: pru_attach() failed\n", | log(LOG_DEBUG, "%s: pcb %p: pru_attach() failed\n", | ||||
__func__, head->so_pcb); | __func__, head->so_pcb); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
so->so_rcv.sb_lowat = head->so_rcv.sb_lowat; | so->so_rcv.sb_lowat = head->sol_sbrcv_lowat; | ||||
so->so_snd.sb_lowat = head->so_snd.sb_lowat; | so->so_snd.sb_lowat = head->sol_sbsnd_lowat; | ||||
so->so_rcv.sb_timeo = head->so_rcv.sb_timeo; | so->so_rcv.sb_timeo = head->sol_sbrcv_timeo; | ||||
so->so_snd.sb_timeo = head->so_snd.sb_timeo; | so->so_snd.sb_timeo = head->sol_sbsnd_timeo; | ||||
so->so_rcv.sb_flags |= head->so_rcv.sb_flags & SB_AUTOSIZE; | so->so_rcv.sb_flags |= head->sol_sbrcv_flags & SB_AUTOSIZE; | ||||
so->so_snd.sb_flags |= head->so_snd.sb_flags & SB_AUTOSIZE; | so->so_snd.sb_flags |= head->sol_sbsnd_flags & SB_AUTOSIZE; | ||||
so->so_state |= connstatus; | so->so_state |= connstatus; | ||||
ACCEPT_LOCK(); | |||||
/* | SOLISTEN_LOCK(head); | ||||
* The accept socket may be tearing down but we just | soref(head); /* A socket on (in)complete queue refs head. */ | ||||
* won a race on the ACCEPT_LOCK. | |||||
* However, if sctp_peeloff() is called on a 1-to-many | |||||
* style socket, the SO_ACCEPTCONN doesn't need to be set. | |||||
*/ | |||||
if (!(head->so_options & SO_ACCEPTCONN) && | |||||
((head->so_proto->pr_protocol != IPPROTO_SCTP) || | |||||
(head->so_type != SOCK_SEQPACKET))) { | |||||
SOCK_LOCK(so); | |||||
so->so_head = NULL; | |||||
sofree(so); /* NB: returns ACCEPT_UNLOCK'ed. */ | |||||
return (NULL); | |||||
} | |||||
if (connstatus) { | if (connstatus) { | ||||
TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); | TAILQ_INSERT_TAIL(&head->sol_comp, so, so_list); | ||||
so->so_qstate |= SQ_COMP; | so->so_qstate = SQ_COMP; | ||||
head->so_qlen++; | head->sol_qlen++; | ||||
solisten_wakeup(head); /* unlocks */ | |||||
} else { | } else { | ||||
/* | /* | ||||
* Keep removing sockets from the head until there's room for | * Keep removing sockets from the head until there's room for | ||||
* us to insert on the tail. In pre-locking revisions, this | * us to insert on the tail. In pre-locking revisions, this | ||||
* was a simple if(), but as we could be racing with other | * was a simple if(), but as we could be racing with other | ||||
* threads and soabort() requires dropping locks, we must | * threads and soabort() requires dropping locks, we must | ||||
* loop waiting for the condition to be true. | * loop waiting for the condition to be true. | ||||
*/ | */ | ||||
while (head->so_incqlen > head->so_qlimit) { | while (head->sol_incqlen > head->sol_qlimit) { | ||||
struct socket *sp; | struct socket *sp; | ||||
sp = TAILQ_FIRST(&head->so_incomp); | |||||
TAILQ_REMOVE(&head->so_incomp, sp, so_list); | sp = TAILQ_FIRST(&head->sol_incomp); | ||||
head->so_incqlen--; | TAILQ_REMOVE(&head->sol_incomp, sp, so_list); | ||||
sp->so_qstate &= ~SQ_INCOMP; | head->sol_incqlen--; | ||||
sp->so_head = NULL; | SOCK_LOCK(sp); | ||||
ACCEPT_UNLOCK(); | sp->so_qstate = SQ_NONE; | ||||
sp->so_listen = NULL; | |||||
SOCK_UNLOCK(sp); | |||||
sorele(head); /* does SOLISTEN_UNLOCK, head stays */ | |||||
soabort(sp); | soabort(sp); | ||||
ACCEPT_LOCK(); | SOLISTEN_LOCK(head); | ||||
} | } | ||||
TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list); | TAILQ_INSERT_TAIL(&head->sol_incomp, so, so_list); | ||||
so->so_qstate |= SQ_INCOMP; | so->so_qstate = SQ_INCOMP; | ||||
head->so_incqlen++; | head->sol_incqlen++; | ||||
SOLISTEN_UNLOCK(head); | |||||
} | } | ||||
ACCEPT_UNLOCK(); | return (so); | ||||
if (connstatus) { | |||||
sorwakeup(head); | |||||
wakeup_one(&head->so_timeo); | |||||
} | } | ||||
#ifdef SCTP | |||||
/* | |||||
* Socket part of sctp_peeloff(). Detach a new socket from an | |||||
* association. The new socket is returned with a reference. | |||||
*/ | |||||
struct socket * | |||||
sopeeloff(struct socket *head) | |||||
{ | |||||
struct socket *so; | |||||
VNET_ASSERT(head->so_vnet != NULL, ("%s:%d so_vnet is NULL, head=%p", | |||||
__func__, __LINE__, head)); | |||||
so = soalloc(head->so_vnet); | |||||
if (so == NULL) { | |||||
log(LOG_DEBUG, "%s: pcb %p: New socket allocation failure: " | |||||
"limit reached or out of memory\n", | |||||
__func__, head->so_pcb); | |||||
return (NULL); | |||||
} | |||||
so->so_type = head->so_type; | |||||
so->so_options = head->so_options; | |||||
so->so_linger = head->so_linger; | |||||
so->so_state = (head->so_state & SS_NBIO) | SS_ISCONNECTED; | |||||
so->so_fibnum = head->so_fibnum; | |||||
so->so_proto = head->so_proto; | |||||
so->so_cred = crhold(head->so_cred); | |||||
#ifdef MAC | |||||
mac_socket_newconn(head, so); | |||||
#endif | |||||
knlist_init(&so->so_rdsel.si_note, so, so_rdknl_lock, so_rdknl_unlock, | |||||
so_rdknl_assert_locked, so_rdknl_assert_unlocked); | |||||
knlist_init(&so->so_wrsel.si_note, so, so_wrknl_lock, so_wrknl_unlock, | |||||
so_wrknl_assert_locked, so_wrknl_assert_unlocked); | |||||
VNET_SO_ASSERT(head); | |||||
if (soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat)) { | |||||
sodealloc(so); | |||||
log(LOG_DEBUG, "%s: pcb %p: soreserve() failed\n", | |||||
__func__, head->so_pcb); | |||||
return (NULL); | |||||
} | |||||
if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0, NULL)) { | |||||
sodealloc(so); | |||||
log(LOG_DEBUG, "%s: pcb %p: pru_attach() failed\n", | |||||
__func__, head->so_pcb); | |||||
return (NULL); | |||||
} | |||||
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; | |||||
soref(so); | |||||
return (so); | return (so); | ||||
} | } | ||||
#endif /* SCTP */ | |||||
int | int | ||||
sobind(struct socket *so, struct sockaddr *nam, struct thread *td) | sobind(struct socket *so, struct sockaddr *nam, struct thread *td) | ||||
{ | { | ||||
int error; | int error; | ||||
CURVNET_SET(so->so_vnet); | CURVNET_SET(so->so_vnet); | ||||
error = (*so->so_proto->pr_usrreqs->pru_bind)(so, nam, td); | error = (*so->so_proto->pr_usrreqs->pru_bind)(so, nam, td); | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING | | ||||
SS_ISDISCONNECTING)) | SS_ISDISCONNECTING)) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
solisten_proto(struct socket *so, int backlog) | solisten_proto(struct socket *so, int backlog) | ||||
{ | { | ||||
int sbrcv_lowat, sbsnd_lowat; | |||||
u_int sbrcv_hiwat, sbsnd_hiwat; | |||||
short sbrcv_flags, sbsnd_flags; | |||||
sbintime_t sbrcv_timeo, sbsnd_timeo; | |||||
SOCK_LOCK_ASSERT(so); | SOCK_LOCK_ASSERT(so); | ||||
if (SOLISTENING(so)) | |||||
goto listening; | |||||
/* | |||||
* Change this socket to listening state. | |||||
*/ | |||||
sbrcv_lowat = so->so_rcv.sb_lowat; | |||||
sbsnd_lowat = so->so_snd.sb_lowat; | |||||
sbrcv_hiwat = so->so_rcv.sb_hiwat; | |||||
sbsnd_hiwat = so->so_snd.sb_hiwat; | |||||
sbrcv_flags = so->so_rcv.sb_flags; | |||||
sbsnd_flags = so->so_snd.sb_flags; | |||||
sbrcv_timeo = so->so_rcv.sb_timeo; | |||||
sbsnd_timeo = so->so_snd.sb_timeo; | |||||
sbdestroy(&so->so_snd, so); | |||||
sbdestroy(&so->so_rcv, so); | |||||
sx_destroy(&so->so_snd.sb_sx); | |||||
sx_destroy(&so->so_rcv.sb_sx); | |||||
SOCKBUF_LOCK_DESTROY(&so->so_snd); | |||||
SOCKBUF_LOCK_DESTROY(&so->so_rcv); | |||||
#ifdef INVARIANTS | |||||
bzero(&so->so_rcv, | |||||
sizeof(struct socket) - offsetof(struct socket, so_rcv)); | |||||
#endif | |||||
so->sol_sbrcv_lowat = sbrcv_lowat; | |||||
so->sol_sbsnd_lowat = sbsnd_lowat; | |||||
so->sol_sbrcv_hiwat = sbrcv_hiwat; | |||||
so->sol_sbsnd_hiwat = sbsnd_hiwat; | |||||
so->sol_sbrcv_flags = sbrcv_flags; | |||||
so->sol_sbsnd_flags = sbsnd_flags; | |||||
so->sol_sbrcv_timeo = sbrcv_timeo; | |||||
so->sol_sbsnd_timeo = sbsnd_timeo; | |||||
so->sol_qlen = so->sol_incqlen = 0; | |||||
TAILQ_INIT(&so->sol_incomp); | |||||
TAILQ_INIT(&so->sol_comp); | |||||
so->sol_accept_filter = NULL; | |||||
so->sol_accept_filter_arg = NULL; | |||||
so->sol_accept_filter_str = NULL; | |||||
so->so_options |= SO_ACCEPTCONN; | |||||
listening: | |||||
if (backlog < 0 || backlog > somaxconn) | if (backlog < 0 || backlog > somaxconn) | ||||
backlog = somaxconn; | backlog = somaxconn; | ||||
so->so_qlimit = backlog; | so->sol_qlimit = backlog; | ||||
so->so_options |= SO_ACCEPTCONN; | |||||
} | } | ||||
/* | /* | ||||
* Wakeup listeners/subsystems once we have a complete connection. | |||||
* Enters with lock, returns unlocked. | |||||
*/ | |||||
static void | |||||
solisten_wakeup(struct socket *sol) | |||||
{ | |||||
if (sol->sol_upcall != NULL) | |||||
(void )sol->sol_upcall(sol, sol->sol_upcallarg, M_NOWAIT); | |||||
else { | |||||
selwakeuppri(&sol->so_rdsel, PSOCK); | |||||
KNOTE_LOCKED(&sol->so_rdsel.si_note, 0); | |||||
} | |||||
SOLISTEN_UNLOCK(sol); | |||||
wakeup_one(&sol->sol_comp); | |||||
} | |||||
/* | |||||
* Return single connection off a listening socket queue. Main consumer of | |||||
* the function is kern_accept4(). Some modules, that do their own accept | |||||
* management also use the function. | |||||
* | |||||
* Listening socket must be locked on entry and is returned unlocked on | |||||
* return. | |||||
* The flags argument is set of accept4(2) flags and ACCEPT4_INHERIT. | |||||
*/ | |||||
int | |||||
solisten_dequeue(struct socket *head, struct socket **ret, int flags) | |||||
{ | |||||
struct socket *so; | |||||
int error; | |||||
SOLISTEN_LOCK_ASSERT(head); | |||||
while (!(head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->sol_comp) && | |||||
head->so_error == 0) { | |||||
error = msleep(&head->sol_comp, &head->so_lock, PSOCK | PCATCH, | |||||
"accept", 0); | |||||
if (error != 0) { | |||||
SOLISTEN_UNLOCK(head); | |||||
return (error); | |||||
} | |||||
} | |||||
if (head->so_error) { | |||||
error = head->so_error; | |||||
head->so_error = 0; | |||||
SOLISTEN_UNLOCK(head); | |||||
return (error); | |||||
} | |||||
if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->sol_comp)) { | |||||
SOLISTEN_UNLOCK(head); | |||||
return (EWOULDBLOCK); | |||||
} | |||||
so = TAILQ_FIRST(&head->sol_comp); | |||||
SOCK_LOCK(so); | |||||
KASSERT(so->so_qstate == SQ_COMP, | |||||
("%s: so %p not SQ_COMP", __func__, so)); | |||||
soref(so); | |||||
head->sol_qlen--; | |||||
so->so_qstate = SQ_NONE; | |||||
so->so_listen = NULL; | |||||
TAILQ_REMOVE(&head->sol_comp, so, so_list); | |||||
if (flags & ACCEPT4_INHERIT) | |||||
so->so_state |= (head->so_state & SS_NBIO); | |||||
else | |||||
so->so_state |= (flags & SOCK_NONBLOCK) ? SS_NBIO : 0; | |||||
SOCK_UNLOCK(so); | |||||
sorele(head); | |||||
*ret = so; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Evaluate the reference count and named references on a socket; if no | * Evaluate the reference count and named references on a socket; if no | ||||
* references remain, free it. This should be called whenever a reference is | * references remain, free it. This should be called whenever a reference is | ||||
* released, such as in sorele(), but also when named reference flags are | * released, such as in sorele(), but also when named reference flags are | ||||
* cleared in socket or protocol code. | * cleared in socket or protocol code. | ||||
* | * | ||||
* sofree() will free the socket if: | * sofree() will free the socket if: | ||||
* | * | ||||
* - There are no outstanding file descriptor references or related consumers | * - There are no outstanding file descriptor references or related consumers | ||||
* (so_count == 0). | * (so_count == 0). | ||||
* | * | ||||
* - The socket has been closed by user space, if ever open (SS_NOFDREF). | * - The socket has been closed by user space, if ever open (SS_NOFDREF). | ||||
* | * | ||||
* - The protocol does not have an outstanding strong reference on the socket | * - The protocol does not have an outstanding strong reference on the socket | ||||
* (SS_PROTOREF). | * (SS_PROTOREF). | ||||
* | * | ||||
* - The socket is not in a completed connection queue, so a process has been | * - The socket is not in a completed connection queue, so a process has been | ||||
* notified that it is present. If it is removed, the user process may | * notified that it is present. If it is removed, the user process may | ||||
* block in accept() despite select() saying the socket was ready. | * block in accept() despite select() saying the socket was ready. | ||||
*/ | */ | ||||
void | void | ||||
sofree(struct socket *so) | sofree(struct socket *so) | ||||
{ | { | ||||
struct protosw *pr = so->so_proto; | struct protosw *pr = so->so_proto; | ||||
struct socket *head; | |||||
ACCEPT_LOCK_ASSERT(); | |||||
SOCK_LOCK_ASSERT(so); | SOCK_LOCK_ASSERT(so); | ||||
if ((so->so_state & SS_NOFDREF) == 0 || so->so_count != 0 || | if ((so->so_state & SS_NOFDREF) == 0 || so->so_count != 0 || | ||||
(so->so_state & SS_PROTOREF) || (so->so_qstate & SQ_COMP)) { | (so->so_state & SS_PROTOREF) || (so->so_qstate == SQ_COMP)) { | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
ACCEPT_UNLOCK(); | |||||
return; | return; | ||||
} | } | ||||
head = so->so_head; | if (!SOLISTENING(so) && so->so_qstate == SQ_INCOMP) { | ||||
if (head != NULL) { | struct socket *sol; | ||||
KASSERT((so->so_qstate & SQ_COMP) != 0 || | |||||
(so->so_qstate & SQ_INCOMP) != 0, | sol = so->so_listen; | ||||
("sofree: so_head != NULL, but neither SQ_COMP nor " | KASSERT(sol, ("%s: so %p on incomp of NULL", __func__, so)); | ||||
"SQ_INCOMP")); | |||||
KASSERT((so->so_qstate & SQ_COMP) == 0 || | /* | ||||
(so->so_qstate & SQ_INCOMP) == 0, | * To solve race between close of a listening socket and | ||||
("sofree: so->so_qstate is SQ_COMP and also SQ_INCOMP")); | * a socket on its incomplete queue, we need to lock both. | ||||
TAILQ_REMOVE(&head->so_incomp, so, so_list); | * The order is first listening socket, then regular. | ||||
head->so_incqlen--; | * Since we don't have SS_NOFDREF neither SS_PROTOREF, this | ||||
so->so_qstate &= ~SQ_INCOMP; | * function and the listening socket are the only pointers | ||||
so->so_head = NULL; | * to so. To preserve so and sol, we reference both and then | ||||
* relock. | |||||
* After relock the socket may not move to so_comp since it | |||||
* doesn't have PCB already, but it may be removed from | |||||
* so_incomp. If that happens, we share responsiblity on | |||||
* freeing the socket, but soclose() has already removed | |||||
* it from queue. | |||||
*/ | |||||
soref(sol); | |||||
soref(so); | |||||
SOCK_UNLOCK(so); | |||||
SOLISTEN_LOCK(sol); | |||||
SOCK_LOCK(so); | |||||
if (so->so_qstate == SQ_INCOMP) { | |||||
KASSERT(so->so_listen == sol, | |||||
("%s: so %p migrated out of sol %p", | |||||
__func__, so, sol)); | |||||
TAILQ_REMOVE(&sol->sol_incomp, so, so_list); | |||||
sol->sol_incqlen--; | |||||
/* This is guarenteed not to be the last. */ | |||||
refcount_release(&sol->so_count); | |||||
so->so_qstate = SQ_NONE; | |||||
so->so_listen = NULL; | |||||
} else | |||||
KASSERT(so->so_listen == NULL, | |||||
("%s: so %p not on (in)comp with so_listen", | |||||
__func__, so)); | |||||
sorele(sol); | |||||
KASSERT(so->so_count == 1, | |||||
("%s: so %p count %u", __func__, so, so->so_count)); | |||||
so->so_count = 0; | |||||
} | } | ||||
KASSERT((so->so_qstate & SQ_COMP) == 0 && | if (SOLISTENING(so)) | ||||
(so->so_qstate & SQ_INCOMP) == 0, | so->so_error = ECONNABORTED; | ||||
("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 & SO_ACCEPTCONN) { | |||||
KASSERT((TAILQ_EMPTY(&so->so_comp)), | |||||
("sofree: so_comp populated")); | |||||
KASSERT((TAILQ_EMPTY(&so->so_incomp)), | |||||
("sofree: so_incomp populated")); | |||||
} | |||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
ACCEPT_UNLOCK(); | |||||
VNET_SO_ASSERT(so); | VNET_SO_ASSERT(so); | ||||
if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) | if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) | ||||
(*pr->pr_domain->dom_dispose)(so); | (*pr->pr_domain->dom_dispose)(so); | ||||
if (pr->pr_usrreqs->pru_detach != NULL) | if (pr->pr_usrreqs->pru_detach != NULL) | ||||
(*pr->pr_usrreqs->pru_detach)(so); | (*pr->pr_usrreqs->pru_detach)(so); | ||||
/* | /* | ||||
* From this point on, we assume that no other references to this | * From this point on, we assume that no other references to this | ||||
* socket exist anywhere else in the stack. Therefore, no locks need | * socket exist anywhere else in the stack. Therefore, no locks need | ||||
* to be acquired or held. | * to be acquired or held. | ||||
* | * | ||||
* We used to do a lot of socket buffer and socket locking here, as | * 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 | * well as invoke sorflush() and perform wakeups. The direct call to | ||||
* dom_dispose() and sbrelease_internal() are an inlining of what was | * dom_dispose() and sbrelease_internal() are an inlining of what was | ||||
* necessary from sorflush(). | * necessary from sorflush(). | ||||
* | * | ||||
* Notice that the socket buffer and kqueue state are torn down | * Notice that the socket buffer and kqueue state are torn down | ||||
* before calling pru_detach. This means that protocols shold not | * before calling pru_detach. This means that protocols shold not | ||||
* assume they can perform socket wakeups, etc, in their detach code. | * assume they can perform socket wakeups, etc, in their detach code. | ||||
*/ | */ | ||||
if (!SOLISTENING(so)) { | |||||
sbdestroy(&so->so_snd, so); | sbdestroy(&so->so_snd, so); | ||||
sbdestroy(&so->so_rcv, so); | sbdestroy(&so->so_rcv, so); | ||||
seldrain(&so->so_snd.sb_sel); | } | ||||
seldrain(&so->so_rcv.sb_sel); | seldrain(&so->so_rdsel); | ||||
knlist_destroy(&so->so_rcv.sb_sel.si_note); | seldrain(&so->so_wrsel); | ||||
knlist_destroy(&so->so_snd.sb_sel.si_note); | knlist_destroy(&so->so_rdsel.si_note); | ||||
knlist_destroy(&so->so_wrsel.si_note); | |||||
sodealloc(so); | sodealloc(so); | ||||
} | } | ||||
/* | /* | ||||
* Close a socket on last file table reference removal. Initiate disconnect | * Close a socket on last file table reference removal. Initiate disconnect | ||||
* if connected. Free socket when disconnect complete. | * if connected. Free socket when disconnect complete. | ||||
* | * | ||||
* This function will sorele() the socket. Note that soclose() may be called | * This function will sorele() the socket. Note that soclose() may be called | ||||
* prior to the ref count reaching zero. The actual socket structure will | * prior to the ref count reaching zero. The actual socket structure will | ||||
* not be freed until the ref count reaches zero. | * not be freed until the ref count reaches zero. | ||||
*/ | */ | ||||
int | int | ||||
soclose(struct socket *so) | soclose(struct socket *so) | ||||
{ | { | ||||
struct accept_queue lqueue; | |||||
bool listening; | |||||
int error = 0; | int error = 0; | ||||
KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter")); | KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter")); | ||||
CURVNET_SET(so->so_vnet); | CURVNET_SET(so->so_vnet); | ||||
funsetown(&so->so_sigio); | funsetown(&so->so_sigio); | ||||
if (so->so_state & SS_ISCONNECTED) { | if (so->so_state & SS_ISCONNECTED) { | ||||
if ((so->so_state & SS_ISDISCONNECTING) == 0) { | if ((so->so_state & SS_ISDISCONNECTING) == 0) { | ||||
Show All 16 Lines | if (so->so_options & SO_LINGER) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
drop: | drop: | ||||
if (so->so_proto->pr_usrreqs->pru_close != NULL) | if (so->so_proto->pr_usrreqs->pru_close != NULL) | ||||
(*so->so_proto->pr_usrreqs->pru_close)(so); | (*so->so_proto->pr_usrreqs->pru_close)(so); | ||||
ACCEPT_LOCK(); | |||||
if (so->so_options & SO_ACCEPTCONN) { | SOCK_LOCK(so); | ||||
if ((listening = (so->so_options & SO_ACCEPTCONN))) { | |||||
struct socket *sp; | struct socket *sp; | ||||
/* | |||||
* Prevent new additions to the accept queues due | TAILQ_INIT(&lqueue); | ||||
* to ACCEPT_LOCK races while we are draining them. | TAILQ_SWAP(&lqueue, &so->sol_incomp, socket, so_list); | ||||
*/ | TAILQ_CONCAT(&lqueue, &so->sol_comp, so_list); | ||||
so->so_options &= ~SO_ACCEPTCONN; | |||||
while ((sp = TAILQ_FIRST(&so->so_incomp)) != NULL) { | so->sol_qlen = so->sol_incqlen = 0; | ||||
TAILQ_REMOVE(&so->so_incomp, sp, so_list); | |||||
so->so_incqlen--; | TAILQ_FOREACH(sp, &lqueue, so_list) { | ||||
sp->so_qstate &= ~SQ_INCOMP; | SOCK_LOCK(sp); | ||||
sp->so_head = NULL; | sp->so_qstate = SQ_NONE; | ||||
ACCEPT_UNLOCK(); | sp->so_listen = NULL; | ||||
soabort(sp); | SOCK_UNLOCK(sp); | ||||
ACCEPT_LOCK(); | /* Guaranteed not to be the last. */ | ||||
refcount_release(&so->so_count); | |||||
} | } | ||||
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(); | |||||
} | } | ||||
KASSERT((TAILQ_EMPTY(&so->so_comp)), | |||||
("%s: so_comp populated", __func__)); | |||||
KASSERT((TAILQ_EMPTY(&so->so_incomp)), | |||||
("%s: so_incomp populated", __func__)); | |||||
} | |||||
SOCK_LOCK(so); | |||||
KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF")); | KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF")); | ||||
so->so_state |= SS_NOFDREF; | so->so_state |= SS_NOFDREF; | ||||
sorele(so); /* NB: Returns with ACCEPT_UNLOCK(). */ | sorele(so); | ||||
if (listening) { | |||||
struct socket *sp; | |||||
TAILQ_FOREACH(sp, &lqueue, so_list) { | |||||
SOCK_LOCK(sp); | |||||
if (sp->so_count == 0) { | |||||
SOCK_UNLOCK(sp); | |||||
soabort(sp); | |||||
} else | |||||
/* sp is now in sofree() */ | |||||
SOCK_UNLOCK(sp); | |||||
} | |||||
} | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* soabort() is used to abruptly tear down a connection, such as when a | * soabort() is used to abruptly tear down a connection, such as when a | ||||
* resource limit is reached (listen queue depth exceeded), or if a listen | * resource limit is reached (listen queue depth exceeded), or if a listen | ||||
* socket is closed while there are sockets waiting to be accepted. | * socket is closed while there are sockets waiting to be accepted. | ||||
Show All 15 Lines | soabort(struct socket *so) | ||||
* In as much as is possible, assert that no references to this | * In as much as is possible, assert that no references to this | ||||
* socket are held. This is not quite the same as asserting that the | * socket are held. This is not quite the same as asserting that the | ||||
* current thread is responsible for arranging for no references, but | * current thread is responsible for arranging for no references, but | ||||
* is as close as we can get for now. | * is as close as we can get for now. | ||||
*/ | */ | ||||
KASSERT(so->so_count == 0, ("soabort: so_count")); | KASSERT(so->so_count == 0, ("soabort: so_count")); | ||||
KASSERT((so->so_state & SS_PROTOREF) == 0, ("soabort: SS_PROTOREF")); | KASSERT((so->so_state & SS_PROTOREF) == 0, ("soabort: SS_PROTOREF")); | ||||
KASSERT(so->so_state & SS_NOFDREF, ("soabort: !SS_NOFDREF")); | KASSERT(so->so_state & SS_NOFDREF, ("soabort: !SS_NOFDREF")); | ||||
KASSERT((so->so_state & SQ_COMP) == 0, ("soabort: SQ_COMP")); | KASSERT(so->so_qstate == SQ_NONE, ("soabort: !SQ_NONE")); | ||||
KASSERT((so->so_state & SQ_INCOMP) == 0, ("soabort: SQ_INCOMP")); | |||||
VNET_SO_ASSERT(so); | VNET_SO_ASSERT(so); | ||||
if (so->so_proto->pr_usrreqs->pru_abort != NULL) | if (so->so_proto->pr_usrreqs->pru_abort != NULL) | ||||
(*so->so_proto->pr_usrreqs->pru_abort)(so); | (*so->so_proto->pr_usrreqs->pru_abort)(so); | ||||
ACCEPT_LOCK(); | |||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
sofree(so); | sofree(so); | ||||
} | } | ||||
int | int | ||||
soaccept(struct socket *so, struct sockaddr **nam) | soaccept(struct socket *so, struct sockaddr **nam) | ||||
{ | { | ||||
int error; | int error; | ||||
▲ Show 20 Lines • Show All 1,537 Lines • ▼ Show 20 Lines | if (so->so_proto->pr_ctloutput != NULL) { | ||||
error = (*so->so_proto->pr_ctloutput)(so, sopt); | error = (*so->so_proto->pr_ctloutput)(so, sopt); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return (error); | return (error); | ||||
} | } | ||||
error = ENOPROTOOPT; | error = ENOPROTOOPT; | ||||
} else { | } else { | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
case SO_ACCEPTFILTER: | case SO_ACCEPTFILTER: | ||||
error = do_setopt_accept_filter(so, sopt); | error = accept_filt_setopt(so, sopt); | ||||
if (error) | if (error) | ||||
goto bad; | goto bad; | ||||
break; | break; | ||||
case SO_LINGER: | case SO_LINGER: | ||||
error = sooptcopyin(sopt, &l, sizeof l, sizeof l); | error = sooptcopyin(sopt, &l, sizeof l, sizeof l); | ||||
if (error) | if (error) | ||||
goto bad; | goto bad; | ||||
▲ Show 20 Lines • Show All 241 Lines • ▼ Show 20 Lines | if (so->so_proto->pr_ctloutput != NULL) | ||||
error = (*so->so_proto->pr_ctloutput)(so, sopt); | error = (*so->so_proto->pr_ctloutput)(so, sopt); | ||||
else | else | ||||
error = ENOPROTOOPT; | error = ENOPROTOOPT; | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return (error); | return (error); | ||||
} else { | } else { | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
case SO_ACCEPTFILTER: | case SO_ACCEPTFILTER: | ||||
error = do_getopt_accept_filter(so, sopt); | error = accept_filt_getopt(so, sopt); | ||||
break; | break; | ||||
case SO_LINGER: | case SO_LINGER: | ||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
l.l_onoff = so->so_options & SO_LINGER; | l.l_onoff = so->so_options & SO_LINGER; | ||||
l.l_linger = so->so_linger; | l.l_linger = so->so_linger; | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
error = sooptcopyout(sopt, &l, sizeof l); | error = sooptcopyout(sopt, &l, sizeof l); | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | #ifdef MAC | ||||
goto bad; | goto bad; | ||||
error = sooptcopyout(sopt, &extmac, sizeof extmac); | error = sooptcopyout(sopt, &extmac, sizeof extmac); | ||||
#else | #else | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
#endif | #endif | ||||
break; | break; | ||||
case SO_LISTENQLIMIT: | case SO_LISTENQLIMIT: | ||||
optval = so->so_qlimit; | optval = SOLISTENING(so) ? so->sol_qlimit : 0; | ||||
goto integer; | goto integer; | ||||
case SO_LISTENQLEN: | case SO_LISTENQLEN: | ||||
optval = so->so_qlen; | optval = SOLISTENING(so) ? so->sol_qlen : 0; | ||||
goto integer; | goto integer; | ||||
case SO_LISTENINCQLEN: | case SO_LISTENINCQLEN: | ||||
optval = so->so_incqlen; | optval = SOLISTENING(so) ? so->sol_incqlen : 0; | ||||
goto integer; | goto integer; | ||||
case SO_TS_CLOCK: | case SO_TS_CLOCK: | ||||
optval = so->so_ts_clock; | optval = so->so_ts_clock; | ||||
goto integer; | goto integer; | ||||
case SO_MAX_PACING_RATE: | case SO_MAX_PACING_RATE: | ||||
optval = so->so_max_pacing_rate; | optval = so->so_max_pacing_rate; | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | |||||
* out-of-band data, which will then notify socket consumers. | * out-of-band data, which will then notify socket consumers. | ||||
*/ | */ | ||||
void | void | ||||
sohasoutofband(struct socket *so) | sohasoutofband(struct socket *so) | ||||
{ | { | ||||
if (so->so_sigio != NULL) | if (so->so_sigio != NULL) | ||||
pgsigio(&so->so_sigio, SIGURG, 0); | pgsigio(&so->so_sigio, SIGURG, 0); | ||||
selwakeuppri(&so->so_rcv.sb_sel, PSOCK); | selwakeuppri(&so->so_rdsel, PSOCK); | ||||
} | } | ||||
int | int | ||||
sopoll(struct socket *so, int events, struct ucred *active_cred, | sopoll(struct socket *so, int events, struct ucred *active_cred, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
/* | /* | ||||
* We do not need to set or assert curvnet as long as everyone uses | * We do not need to set or assert curvnet as long as everyone uses | ||||
* sopoll_generic(). | * sopoll_generic(). | ||||
*/ | */ | ||||
return (so->so_proto->pr_usrreqs->pru_sopoll(so, events, active_cred, | return (so->so_proto->pr_usrreqs->pru_sopoll(so, events, active_cred, | ||||
td)); | td)); | ||||
} | } | ||||
int | int | ||||
sopoll_generic(struct socket *so, int events, struct ucred *active_cred, | sopoll_generic(struct socket *so, int events, struct ucred *active_cred, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
int revents = 0; | int revents; | ||||
SOCK_LOCK(so); | |||||
if (SOLISTENING(so)) { | |||||
if (!(events & (POLLIN | POLLRDNORM))) | |||||
revents = 0; | |||||
else if (!TAILQ_EMPTY(&so->sol_comp)) | |||||
revents = events & (POLLIN | POLLRDNORM); | |||||
else { | |||||
selrecord(td, &so->so_rdsel); | |||||
revents = 0; | |||||
} | |||||
} else { | |||||
revents = 0; | |||||
SOCKBUF_LOCK(&so->so_snd); | SOCKBUF_LOCK(&so->so_snd); | ||||
SOCKBUF_LOCK(&so->so_rcv); | SOCKBUF_LOCK(&so->so_rcv); | ||||
if (events & (POLLIN | POLLRDNORM)) | if (events & (POLLIN | POLLRDNORM)) | ||||
if (soreadabledata(so)) | if (soreadabledata(so)) | ||||
revents |= events & (POLLIN | POLLRDNORM); | revents |= events & (POLLIN | POLLRDNORM); | ||||
if (events & (POLLOUT | POLLWRNORM)) | if (events & (POLLOUT | POLLWRNORM)) | ||||
if (sowriteable(so)) | if (sowriteable(so)) | ||||
revents |= events & (POLLOUT | POLLWRNORM); | revents |= events & (POLLOUT | POLLWRNORM); | ||||
if (events & (POLLPRI | POLLRDBAND)) | if (events & (POLLPRI | POLLRDBAND)) | ||||
if (so->so_oobmark || (so->so_rcv.sb_state & SBS_RCVATMARK)) | if (so->so_oobmark || | ||||
(so->so_rcv.sb_state & SBS_RCVATMARK)) | |||||
revents |= events & (POLLPRI | POLLRDBAND); | revents |= events & (POLLPRI | POLLRDBAND); | ||||
if ((events & POLLINIGNEOF) == 0) { | if ((events & POLLINIGNEOF) == 0) { | ||||
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { | if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { | ||||
revents |= events & (POLLIN | POLLRDNORM); | revents |= events & (POLLIN | POLLRDNORM); | ||||
if (so->so_snd.sb_state & SBS_CANTSENDMORE) | if (so->so_snd.sb_state & SBS_CANTSENDMORE) | ||||
revents |= POLLHUP; | revents |= POLLHUP; | ||||
} | } | ||||
} | } | ||||
if (revents == 0) { | if (revents == 0) { | ||||
if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { | if (events & | ||||
selrecord(td, &so->so_rcv.sb_sel); | (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { | ||||
selrecord(td, &so->so_rdsel); | |||||
so->so_rcv.sb_flags |= SB_SEL; | so->so_rcv.sb_flags |= SB_SEL; | ||||
} | } | ||||
if (events & (POLLOUT | POLLWRNORM)) { | if (events & (POLLOUT | POLLWRNORM)) { | ||||
selrecord(td, &so->so_snd.sb_sel); | selrecord(td, &so->so_wrsel); | ||||
so->so_snd.sb_flags |= SB_SEL; | so->so_snd.sb_flags |= SB_SEL; | ||||
} | } | ||||
} | } | ||||
SOCKBUF_UNLOCK(&so->so_rcv); | SOCKBUF_UNLOCK(&so->so_rcv); | ||||
SOCKBUF_UNLOCK(&so->so_snd); | SOCKBUF_UNLOCK(&so->so_snd); | ||||
} | |||||
SOCK_UNLOCK(so); | |||||
return (revents); | return (revents); | ||||
} | } | ||||
int | int | ||||
soo_kqfilter(struct file *fp, struct knote *kn) | soo_kqfilter(struct file *fp, struct knote *kn) | ||||
{ | { | ||||
struct socket *so = kn->kn_fp->f_data; | struct socket *so = kn->kn_fp->f_data; | ||||
struct sockbuf *sb; | struct sockbuf *sb; | ||||
struct knlist *knl; | |||||
switch (kn->kn_filter) { | switch (kn->kn_filter) { | ||||
case EVFILT_READ: | case EVFILT_READ: | ||||
kn->kn_fop = &soread_filtops; | kn->kn_fop = &soread_filtops; | ||||
knl = &so->so_rdsel.si_note; | |||||
sb = &so->so_rcv; | sb = &so->so_rcv; | ||||
break; | break; | ||||
case EVFILT_WRITE: | case EVFILT_WRITE: | ||||
kn->kn_fop = &sowrite_filtops; | kn->kn_fop = &sowrite_filtops; | ||||
knl = &so->so_wrsel.si_note; | |||||
sb = &so->so_snd; | sb = &so->so_snd; | ||||
break; | break; | ||||
case EVFILT_EMPTY: | case EVFILT_EMPTY: | ||||
kn->kn_fop = &soempty_filtops; | kn->kn_fop = &soempty_filtops; | ||||
knl = &so->so_wrsel.si_note; | |||||
sb = &so->so_snd; | sb = &so->so_snd; | ||||
break; | break; | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
SOCK_LOCK(so); | |||||
if (SOLISTENING(so)) { | |||||
knlist_add(knl, kn, 1); | |||||
} else { | |||||
SOCKBUF_LOCK(sb); | SOCKBUF_LOCK(sb); | ||||
knlist_add(&sb->sb_sel.si_note, kn, 1); | knlist_add(knl, kn, 1); | ||||
sb->sb_flags |= SB_KNOTE; | sb->sb_flags |= SB_KNOTE; | ||||
SOCKBUF_UNLOCK(sb); | SOCKBUF_UNLOCK(sb); | ||||
} | |||||
SOCK_UNLOCK(so); | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Some routines that return EOPNOTSUPP for entry points that are not | * Some routines that return EOPNOTSUPP for entry points that are not | ||||
* supported by a protocol. Fill in as needed. | * supported by a protocol. Fill in as needed. | ||||
*/ | */ | ||||
int | int | ||||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | pru_sopoll_notsupp(struct socket *so, int events, struct ucred *cred, | ||||
return EOPNOTSUPP; | return EOPNOTSUPP; | ||||
} | } | ||||
static void | static void | ||||
filt_sordetach(struct knote *kn) | filt_sordetach(struct knote *kn) | ||||
{ | { | ||||
struct socket *so = kn->kn_fp->f_data; | struct socket *so = kn->kn_fp->f_data; | ||||
SOCKBUF_LOCK(&so->so_rcv); | so_rdknl_lock(so); | ||||
knlist_remove(&so->so_rcv.sb_sel.si_note, kn, 1); | knlist_remove(&so->so_rdsel.si_note, kn, 1); | ||||
if (knlist_empty(&so->so_rcv.sb_sel.si_note)) | if (!SOLISTENING(so) && knlist_empty(&so->so_rdsel.si_note)) | ||||
so->so_rcv.sb_flags &= ~SB_KNOTE; | so->so_rcv.sb_flags &= ~SB_KNOTE; | ||||
SOCKBUF_UNLOCK(&so->so_rcv); | so_rdknl_unlock(so); | ||||
} | } | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
filt_soread(struct knote *kn, long hint) | filt_soread(struct knote *kn, long hint) | ||||
{ | { | ||||
struct socket *so; | struct socket *so; | ||||
so = kn->kn_fp->f_data; | so = kn->kn_fp->f_data; | ||||
if (so->so_options & SO_ACCEPTCONN) { | |||||
kn->kn_data = so->so_qlen; | |||||
return (!TAILQ_EMPTY(&so->so_comp)); | |||||
if (SOLISTENING(so)) { | |||||
SOCK_LOCK_ASSERT(so); | |||||
kn->kn_data = so->sol_qlen; | |||||
return (!TAILQ_EMPTY(&so->sol_comp)); | |||||
} | } | ||||
SOCKBUF_LOCK_ASSERT(&so->so_rcv); | SOCKBUF_LOCK_ASSERT(&so->so_rcv); | ||||
kn->kn_data = sbavail(&so->so_rcv) - so->so_rcv.sb_ctl; | kn->kn_data = sbavail(&so->so_rcv) - so->so_rcv.sb_ctl; | ||||
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { | if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { | ||||
kn->kn_flags |= EV_EOF; | kn->kn_flags |= EV_EOF; | ||||
kn->kn_fflags = so->so_error; | kn->kn_fflags = so->so_error; | ||||
return (1); | return (1); | ||||
} else if (so->so_error) /* temporary udp error */ | } else if (so->so_error) /* temporary udp error */ | ||||
Show All 9 Lines | filt_soread(struct knote *kn, long hint) | ||||
return (hhook_run_socket(so, NULL, HHOOK_FILT_SOREAD)); | return (hhook_run_socket(so, NULL, HHOOK_FILT_SOREAD)); | ||||
} | } | ||||
static void | static void | ||||
filt_sowdetach(struct knote *kn) | filt_sowdetach(struct knote *kn) | ||||
{ | { | ||||
struct socket *so = kn->kn_fp->f_data; | struct socket *so = kn->kn_fp->f_data; | ||||
SOCKBUF_LOCK(&so->so_snd); | so_wrknl_lock(so); | ||||
knlist_remove(&so->so_snd.sb_sel.si_note, kn, 1); | knlist_remove(&so->so_wrsel.si_note, kn, 1); | ||||
if (knlist_empty(&so->so_snd.sb_sel.si_note)) | if (!SOLISTENING(so) && knlist_empty(&so->so_wrsel.si_note)) | ||||
so->so_snd.sb_flags &= ~SB_KNOTE; | so->so_snd.sb_flags &= ~SB_KNOTE; | ||||
SOCKBUF_UNLOCK(&so->so_snd); | so_wrknl_unlock(so); | ||||
} | } | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
filt_sowrite(struct knote *kn, long hint) | filt_sowrite(struct knote *kn, long hint) | ||||
{ | { | ||||
struct socket *so; | struct socket *so; | ||||
so = kn->kn_fp->f_data; | so = kn->kn_fp->f_data; | ||||
if (SOLISTENING(so)) | |||||
return (0); | |||||
SOCKBUF_LOCK_ASSERT(&so->so_snd); | SOCKBUF_LOCK_ASSERT(&so->so_snd); | ||||
kn->kn_data = sbspace(&so->so_snd); | kn->kn_data = sbspace(&so->so_snd); | ||||
hhook_run_socket(so, kn, HHOOK_FILT_SOWRITE); | hhook_run_socket(so, kn, HHOOK_FILT_SOWRITE); | ||||
if (so->so_snd.sb_state & SBS_CANTSENDMORE) { | if (so->so_snd.sb_state & SBS_CANTSENDMORE) { | ||||
kn->kn_flags |= EV_EOF; | kn->kn_flags |= EV_EOF; | ||||
kn->kn_fflags = so->so_error; | kn->kn_fflags = so->so_error; | ||||
Show All 10 Lines | |||||
} | } | ||||
static int | static int | ||||
filt_soempty(struct knote *kn, long hint) | filt_soempty(struct knote *kn, long hint) | ||||
{ | { | ||||
struct socket *so; | struct socket *so; | ||||
so = kn->kn_fp->f_data; | so = kn->kn_fp->f_data; | ||||
if (SOLISTENING(so)) | |||||
return (1); | |||||
SOCKBUF_LOCK_ASSERT(&so->so_snd); | SOCKBUF_LOCK_ASSERT(&so->so_snd); | ||||
kn->kn_data = sbused(&so->so_snd); | kn->kn_data = sbused(&so->so_snd); | ||||
if (kn->kn_data == 0) | if (kn->kn_data == 0) | ||||
return (1); | return (1); | ||||
else | else | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
void | void | ||||
soisconnected(struct socket *so) | soisconnected(struct socket *so) | ||||
{ | { | ||||
struct socket *head; | struct socket *head; | ||||
int ret; | int ret; | ||||
restart: | restart: | ||||
ACCEPT_LOCK(); | if ((head = so->so_listen) != NULL) | ||||
SOLISTEN_LOCK(head); | |||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
/* | |||||
* XXXGL: should we re-check so->so_listen? | |||||
*/ | |||||
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); | so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); | ||||
so->so_state |= SS_ISCONNECTED; | so->so_state |= SS_ISCONNECTED; | ||||
head = so->so_head; | if (head != NULL && (so->so_qstate == SQ_INCOMP)) { | ||||
if (head != NULL && (so->so_qstate & SQ_INCOMP)) { | |||||
if ((so->so_options & SO_ACCEPTFILTER) == 0) { | if ((so->so_options & SO_ACCEPTFILTER) == 0) { | ||||
TAILQ_REMOVE(&head->sol_incomp, so, so_list); | |||||
head->sol_incqlen--; | |||||
TAILQ_INSERT_TAIL(&head->sol_comp, so, so_list); | |||||
head->sol_qlen++; | |||||
so->so_qstate = SQ_COMP; | |||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
TAILQ_REMOVE(&head->so_incomp, so, so_list); | solisten_wakeup(head); /* unlocks */ | ||||
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); | |||||
} else { | } else { | ||||
ACCEPT_UNLOCK(); | SOLISTEN_UNLOCK(head); | ||||
soupcall_set(so, SO_RCV, | soupcall_set(so, SO_RCV, | ||||
head->so_accf->so_accept_filter->accf_callback, | head->sol_accept_filter->accf_callback, | ||||
head->so_accf->so_accept_filter_arg); | head->sol_accept_filter_arg); | ||||
so->so_options &= ~SO_ACCEPTFILTER; | so->so_options &= ~SO_ACCEPTFILTER; | ||||
ret = head->so_accf->so_accept_filter->accf_callback(so, | ret = head->sol_accept_filter->accf_callback(so, | ||||
head->so_accf->so_accept_filter_arg, M_NOWAIT); | head->sol_accept_filter_arg, M_NOWAIT); | ||||
if (ret == SU_ISCONNECTED) | if (ret == SU_ISCONNECTED) | ||||
soupcall_clear(so, SO_RCV); | soupcall_clear(so, SO_RCV); | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
if (ret == SU_ISCONNECTED) | if (ret == SU_ISCONNECTED) | ||||
goto restart; | goto restart; | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
if (head != NULL) | |||||
SOLISTEN_UNLOCK(head); | |||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
ACCEPT_UNLOCK(); | |||||
wakeup(&so->so_timeo); | wakeup(&so->so_timeo); | ||||
sorwakeup(so); | sorwakeup(so); | ||||
sowwakeup(so); | sowwakeup(so); | ||||
} | } | ||||
void | void | ||||
soisdisconnecting(struct socket *so) | soisdisconnecting(struct socket *so) | ||||
{ | { | ||||
/* | SOCK_LOCK(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_ISCONNECTING; | ||||
so->so_state |= SS_ISDISCONNECTING; | so->so_state |= SS_ISDISCONNECTING; | ||||
if (!SOLISTENING(so)) { | |||||
SOCKBUF_LOCK(&so->so_rcv); | |||||
socantrcvmore_locked(so); | socantrcvmore_locked(so); | ||||
SOCKBUF_LOCK(&so->so_snd); | SOCKBUF_LOCK(&so->so_snd); | ||||
socantsendmore_locked(so); | socantsendmore_locked(so); | ||||
} | |||||
SOCK_UNLOCK(so); | |||||
wakeup(&so->so_timeo); | wakeup(&so->so_timeo); | ||||
} | } | ||||
void | void | ||||
soisdisconnected(struct socket *so) | soisdisconnected(struct socket *so) | ||||
{ | { | ||||
/* | SOCK_LOCK(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|SS_ISCONNECTED|SS_ISDISCONNECTING); | so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); | ||||
so->so_state |= SS_ISDISCONNECTED; | so->so_state |= SS_ISDISCONNECTED; | ||||
if (!SOLISTENING(so)) { | |||||
SOCKBUF_LOCK(&so->so_rcv); | |||||
socantrcvmore_locked(so); | socantrcvmore_locked(so); | ||||
SOCKBUF_LOCK(&so->so_snd); | SOCKBUF_LOCK(&so->so_snd); | ||||
sbdrop_locked(&so->so_snd, sbused(&so->so_snd)); | sbdrop_locked(&so->so_snd, sbused(&so->so_snd)); | ||||
socantsendmore_locked(so); | socantsendmore_locked(so); | ||||
} | |||||
SOCK_UNLOCK(so); | |||||
wakeup(&so->so_timeo); | wakeup(&so->so_timeo); | ||||
} | } | ||||
/* | /* | ||||
* Make a copy of a sockaddr in a malloced buffer of type M_SONAME. | * Make a copy of a sockaddr in a malloced buffer of type M_SONAME. | ||||
*/ | */ | ||||
struct sockaddr * | struct sockaddr * | ||||
sodupsockaddr(const struct sockaddr *sa, int mflags) | sodupsockaddr(const struct sockaddr *sa, int mflags) | ||||
{ | { | ||||
struct sockaddr *sa2; | struct sockaddr *sa2; | ||||
sa2 = malloc(sa->sa_len, M_SONAME, mflags); | sa2 = malloc(sa->sa_len, M_SONAME, mflags); | ||||
if (sa2) | if (sa2) | ||||
bcopy(sa, sa2, sa->sa_len); | bcopy(sa, sa2, sa->sa_len); | ||||
return sa2; | return sa2; | ||||
} | } | ||||
/* | /* | ||||
* Register per-socket buffer upcalls. | * Register per-socket buffer upcalls. | ||||
*/ | */ | ||||
void | void | ||||
soupcall_set(struct socket *so, int which, | soupcall_set(struct socket *so, int which, so_upcall_t func, void *arg) | ||||
int (*func)(struct socket *, void *, int), void *arg) | |||||
{ | { | ||||
struct sockbuf *sb; | struct sockbuf *sb; | ||||
KASSERT(!SOLISTENING(so), ("%s: so %p listening", __func__, so)); | |||||
switch (which) { | switch (which) { | ||||
case SO_RCV: | case SO_RCV: | ||||
sb = &so->so_rcv; | sb = &so->so_rcv; | ||||
break; | break; | ||||
case SO_SND: | case SO_SND: | ||||
sb = &so->so_snd; | sb = &so->so_snd; | ||||
break; | break; | ||||
default: | default: | ||||
panic("soupcall_set: bad which"); | panic("soupcall_set: bad which"); | ||||
} | } | ||||
SOCKBUF_LOCK_ASSERT(sb); | SOCKBUF_LOCK_ASSERT(sb); | ||||
#if 0 | |||||
/* XXX: accf_http actually wants to do this on purpose. */ | |||||
KASSERT(sb->sb_upcall == NULL, ("soupcall_set: overwriting upcall")); | |||||
#endif | |||||
sb->sb_upcall = func; | sb->sb_upcall = func; | ||||
sb->sb_upcallarg = arg; | sb->sb_upcallarg = arg; | ||||
sb->sb_flags |= SB_UPCALL; | sb->sb_flags |= SB_UPCALL; | ||||
} | } | ||||
void | void | ||||
soupcall_clear(struct socket *so, int which) | soupcall_clear(struct socket *so, int which) | ||||
{ | { | ||||
struct sockbuf *sb; | struct sockbuf *sb; | ||||
KASSERT(!SOLISTENING(so), ("%s: so %p listening", __func__, so)); | |||||
switch (which) { | switch (which) { | ||||
case SO_RCV: | case SO_RCV: | ||||
sb = &so->so_rcv; | sb = &so->so_rcv; | ||||
break; | break; | ||||
case SO_SND: | case SO_SND: | ||||
sb = &so->so_snd; | sb = &so->so_snd; | ||||
break; | break; | ||||
default: | default: | ||||
panic("soupcall_clear: bad which"); | panic("soupcall_clear: bad which"); | ||||
} | } | ||||
SOCKBUF_LOCK_ASSERT(sb); | SOCKBUF_LOCK_ASSERT(sb); | ||||
KASSERT(sb->sb_upcall != NULL, ("soupcall_clear: no upcall to clear")); | KASSERT(sb->sb_upcall != NULL, | ||||
("%s: so %p no upcall to clear", __func__, so)); | |||||
sb->sb_upcall = NULL; | sb->sb_upcall = NULL; | ||||
sb->sb_upcallarg = NULL; | sb->sb_upcallarg = NULL; | ||||
sb->sb_flags &= ~SB_UPCALL; | sb->sb_flags &= ~SB_UPCALL; | ||||
} | } | ||||
void | |||||
solisten_upcall_set(struct socket *so, so_upcall_t func, void *arg) | |||||
{ | |||||
SOLISTEN_LOCK_ASSERT(so); | |||||
so->sol_upcall = func; | |||||
so->sol_upcallarg = arg; | |||||
} | |||||
static void | |||||
so_rdknl_lock(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_LOCK(so); | |||||
else | |||||
SOCKBUF_LOCK(&so->so_rcv); | |||||
} | |||||
static void | |||||
so_rdknl_unlock(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_UNLOCK(so); | |||||
else | |||||
SOCKBUF_UNLOCK(&so->so_rcv); | |||||
} | |||||
static void | |||||
so_rdknl_assert_locked(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_LOCK_ASSERT(so); | |||||
else | |||||
SOCKBUF_LOCK_ASSERT(&so->so_rcv); | |||||
} | |||||
static void | |||||
so_rdknl_assert_unlocked(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_UNLOCK_ASSERT(so); | |||||
else | |||||
SOCKBUF_UNLOCK_ASSERT(&so->so_rcv); | |||||
} | |||||
static void | |||||
so_wrknl_lock(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_LOCK(so); | |||||
else | |||||
SOCKBUF_LOCK(&so->so_snd); | |||||
} | |||||
static void | |||||
so_wrknl_unlock(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_UNLOCK(so); | |||||
else | |||||
SOCKBUF_UNLOCK(&so->so_snd); | |||||
} | |||||
static void | |||||
so_wrknl_assert_locked(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_LOCK_ASSERT(so); | |||||
else | |||||
SOCKBUF_LOCK_ASSERT(&so->so_snd); | |||||
} | |||||
static void | |||||
so_wrknl_assert_unlocked(void *arg) | |||||
{ | |||||
struct socket *so = arg; | |||||
if (SOLISTENING(so)) | |||||
SOCK_UNLOCK_ASSERT(so); | |||||
else | |||||
SOCKBUF_UNLOCK_ASSERT(&so->so_snd); | |||||
} | |||||
/* | /* | ||||
* Create an external-format (``xsocket'') structure using the information in | * Create an external-format (``xsocket'') structure using the information in | ||||
* the kernel-format socket structure pointed to by so. This is done to | * the kernel-format socket structure pointed to by so. This is done to | ||||
* reduce the spew of irrelevant information over this interface, to isolate | * reduce the spew of irrelevant information over this interface, to isolate | ||||
* user code from changes in the kernel structure, and potentially to provide | * user code from changes in the kernel structure, and potentially to provide | ||||
* information-hiding if we decide that some of this information should be | * information-hiding if we decide that some of this information should be | ||||
* hidden from users. | * hidden from users. | ||||
*/ | */ | ||||
void | void | ||||
sotoxsocket(struct socket *so, struct xsocket *xso) | sotoxsocket(struct socket *so, struct xsocket *xso) | ||||
{ | { | ||||
xso->xso_len = sizeof *xso; | xso->xso_len = sizeof *xso; | ||||
xso->xso_so = so; | xso->xso_so = so; | ||||
xso->so_type = so->so_type; | xso->so_type = so->so_type; | ||||
xso->so_options = so->so_options; | xso->so_options = so->so_options; | ||||
xso->so_linger = so->so_linger; | xso->so_linger = so->so_linger; | ||||
xso->so_state = so->so_state; | xso->so_state = so->so_state; | ||||
xso->so_pcb = so->so_pcb; | xso->so_pcb = so->so_pcb; | ||||
xso->xso_protocol = so->so_proto->pr_protocol; | xso->xso_protocol = so->so_proto->pr_protocol; | ||||
xso->xso_family = so->so_proto->pr_domain->dom_family; | xso->xso_family = so->so_proto->pr_domain->dom_family; | ||||
xso->so_qlen = so->so_qlen; | |||||
xso->so_incqlen = so->so_incqlen; | |||||
xso->so_qlimit = so->so_qlimit; | |||||
xso->so_timeo = so->so_timeo; | xso->so_timeo = so->so_timeo; | ||||
xso->so_error = so->so_error; | xso->so_error = so->so_error; | ||||
xso->so_uid = so->so_cred->cr_uid; | |||||
xso->so_pgid = so->so_sigio ? so->so_sigio->sio_pgid : 0; | xso->so_pgid = so->so_sigio ? so->so_sigio->sio_pgid : 0; | ||||
if (SOLISTENING(so)) { | |||||
xso->so_qlen = so->sol_qlen; | |||||
xso->so_incqlen = so->sol_incqlen; | |||||
xso->so_qlimit = so->sol_qlimit; | |||||
xso->so_oobmark = 0; | |||||
bzero(&xso->so_snd, sizeof(xso->so_snd)); | |||||
bzero(&xso->so_rcv, sizeof(xso->so_rcv)); | |||||
} else { | |||||
xso->so_state |= so->so_qstate; | |||||
xso->so_qlen = xso->so_incqlen = xso->so_qlimit = 0; | |||||
xso->so_oobmark = so->so_oobmark; | xso->so_oobmark = so->so_oobmark; | ||||
sbtoxsockbuf(&so->so_snd, &xso->so_snd); | sbtoxsockbuf(&so->so_snd, &xso->so_snd); | ||||
sbtoxsockbuf(&so->so_rcv, &xso->so_rcv); | sbtoxsockbuf(&so->so_rcv, &xso->so_rcv); | ||||
xso->so_uid = so->so_cred->cr_uid; | |||||
} | } | ||||
/* | |||||
* Socket accessor functions to provide external consumers with | |||||
* a safe interface to socket state | |||||
* | |||||
*/ | |||||
void | |||||
so_listeners_apply_all(struct socket *so, void (*func)(struct socket *, void *), | |||||
void *arg) | |||||
{ | |||||
TAILQ_FOREACH(so, &so->so_comp, so_list) | |||||
func(so, arg); | |||||
} | } | ||||
struct sockbuf * | struct sockbuf * | ||||
so_sockbuf_rcv(struct socket *so) | so_sockbuf_rcv(struct socket *so) | ||||
{ | { | ||||
return (&so->so_rcv); | return (&so->so_rcv); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 119 Lines • Show Last 20 Lines |