mirror of
https://github.com/freebsd/freebsd-src
synced 2024-07-21 18:27:22 +00:00
sockets: make pr_shutdown fully protocol specific method
Disassemble a one-for-all soshutdown() into protocol specific methods. This creates a small amount of copy & paste, but makes code a lot more self documented, as protocol specific method would execute only the code that is relevant to that protocol and nothing else. This also fixes a couple recent regressions and reduces risk of future regressions. The extended KPI for the new pr_shutdown removes need for the extra pr_flush which was added for the sake of SCTP which could not perform its shutdown properly with the old one. Particularly for SCTP this change streamlines a lot of code. Some notes on why certain parts of code were copied or were not to certain protocols: * The (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING) check is needed only for those protocols that may be connected or disconnected. * The above reduces into only SS_ISCONNECTED for those protocols that always connect instantly. * The ENOTCONN and continue processing hack is left only for datagram protocols. * The SOLISTENING(so) block is copied to those protocols that listen(2). * sorflush() on SHUT_RD is copied almost to every protocol, but that will be refactored later. * wakeup(&so->so_timeo) is copied to protocols that can make a non-instant connect(2), can SO_LINGER or can accept(2). There are three protocols (netgraph(4), Bluetooth, SDP) that did not have pr_shutdown, but old soshutdown() would still perform sorflush() on SHUT_RD for them and also wakeup(9). Those protocols partially supported shutdown(2) returning EOPNOTSUP for SHUT_WR/SHUT_RDWR, now they fully lost shutdown(2) support. I'm pretty sure netgraph(4) and Bluetooth are okay about that and SDP is almost abandoned anyway. Reviewed by: tuexen Differential Revision: https://reviews.freebsd.org/D43413
This commit is contained in:
parent
c3276e02be
commit
5bba272807
|
@ -978,43 +978,43 @@ hvs_trans_abort(struct socket *so)
|
|||
}
|
||||
|
||||
int
|
||||
hvs_trans_shutdown(struct socket *so)
|
||||
hvs_trans_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct hvs_pcb *pcb = so2hvspcb(so);
|
||||
struct sockbuf *sb;
|
||||
|
||||
HVSOCK_DBG(HVSOCK_DBG_VERBOSE,
|
||||
"%s: HyperV Socket hvs_trans_shutdown called\n", __func__);
|
||||
|
||||
SOCK_LOCK(so);
|
||||
if ((so->so_state &
|
||||
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
if (pcb == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* Only get called with the shutdown method is SHUT_WR or
|
||||
* SHUT_RDWR.
|
||||
* When the method is SHUT_RD or SHUT_RDWR, the caller
|
||||
* already set the SBS_CANTRCVMORE on receive side socket
|
||||
* buffer.
|
||||
*/
|
||||
if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0) {
|
||||
/*
|
||||
* SHUT_WR only case.
|
||||
* Receive side is still open. Just close
|
||||
* the send side.
|
||||
*/
|
||||
socantsendmore(so);
|
||||
} else {
|
||||
/* SHUT_RDWR case */
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
socantrcvmore(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
socantrcvmore(so);
|
||||
if (so->so_state & SS_ISCONNECTED) {
|
||||
/* Send a FIN to peer */
|
||||
sb = &so->so_snd;
|
||||
SOCKBUF_LOCK(sb);
|
||||
(void) hvsock_send_data(pcb->chan, NULL, 0, sb);
|
||||
SOCKBUF_UNLOCK(sb);
|
||||
|
||||
SOCK_SENDBUF_LOCK(so);
|
||||
(void) hvsock_send_data(pcb->chan, NULL, 0,
|
||||
&so->so_snd);
|
||||
SOCK_SENDBUF_UNLOCK(so);
|
||||
soisdisconnecting(so);
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
socantsendmore(so);
|
||||
}
|
||||
wakeup(&so->so_timeo);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ int hvs_trans_soreceive(struct socket *, struct sockaddr **,
|
|||
int hvs_trans_sosend(struct socket *, struct sockaddr *, struct uio *,
|
||||
struct mbuf *, struct mbuf *, int, struct thread *);
|
||||
int hvs_trans_disconnect(struct socket *);
|
||||
int hvs_trans_shutdown(struct socket *);
|
||||
int hvs_trans_shutdown(struct socket *, enum shutdown_how);
|
||||
|
||||
int hvs_trans_lock(void);
|
||||
void hvs_trans_unlock(void);
|
||||
|
|
|
@ -151,7 +151,7 @@ pr_ready_notsupp(struct socket *so, struct mbuf *m, int count)
|
|||
}
|
||||
|
||||
static int
|
||||
pr_shutdown_notsupp(struct socket *so)
|
||||
pr_shutdown_notsupp(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
|
|
@ -2966,59 +2966,18 @@ soreceive(struct socket *so, struct sockaddr **psa, struct uio *uio,
|
|||
int
|
||||
soshutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct protosw *pr;
|
||||
int error, soerror_enotconn;
|
||||
|
||||
soerror_enotconn = 0;
|
||||
SOCK_LOCK(so);
|
||||
if ((so->so_state &
|
||||
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
|
||||
/*
|
||||
* POSIX mandates us to return ENOTCONN when shutdown(2) is
|
||||
* invoked on a datagram sockets, however historically we would
|
||||
* actually tear socket down. This is known to be leveraged by
|
||||
* some applications to unblock process waiting in recvXXX(2)
|
||||
* by other process that it shares that socket with. Try to meet
|
||||
* both backward-compatibility and POSIX requirements by forcing
|
||||
* ENOTCONN but still asking protocol to perform pru_shutdown().
|
||||
*/
|
||||
if (so->so_type != SOCK_DGRAM && !SOLISTENING(so)) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
soerror_enotconn = 1;
|
||||
}
|
||||
|
||||
if (SOLISTENING(so)) {
|
||||
if (how != SHUT_WR) {
|
||||
so->so_error = ECONNABORTED;
|
||||
solisten_wakeup(so); /* unlocks so */
|
||||
} else {
|
||||
SOCK_UNLOCK(so);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
int error;
|
||||
|
||||
CURVNET_SET(so->so_vnet);
|
||||
pr = so->so_proto;
|
||||
if (pr->pr_flush != NULL)
|
||||
pr->pr_flush(so, how);
|
||||
if (how != SHUT_WR && !(pr->pr_flags & PR_SOCKBUF))
|
||||
sorflush(so);
|
||||
if (how != SHUT_RD) {
|
||||
error = pr->pr_shutdown(so);
|
||||
wakeup(&so->so_timeo);
|
||||
CURVNET_RESTORE();
|
||||
return ((error == 0 && soerror_enotconn) ? ENOTCONN : error);
|
||||
}
|
||||
wakeup(&so->so_timeo);
|
||||
error = so->so_proto->pr_shutdown(so, how);
|
||||
CURVNET_RESTORE();
|
||||
|
||||
done:
|
||||
return (soerror_enotconn ? ENOTCONN : 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by several pr_shutdown implementations that use generic socket buffers.
|
||||
*/
|
||||
void
|
||||
sorflush(struct socket *so)
|
||||
{
|
||||
|
|
|
@ -1660,18 +1660,65 @@ uipc_sense(struct socket *so, struct stat *sb)
|
|||
}
|
||||
|
||||
static int
|
||||
uipc_shutdown(struct socket *so)
|
||||
uipc_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct unpcb *unp;
|
||||
struct unpcb *unp = sotounpcb(so);
|
||||
int error;
|
||||
|
||||
unp = sotounpcb(so);
|
||||
KASSERT(unp != NULL, ("uipc_shutdown: unp == NULL"));
|
||||
SOCK_LOCK(so);
|
||||
if ((so->so_state &
|
||||
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
|
||||
/*
|
||||
* POSIX mandates us to just return ENOTCONN when shutdown(2) is
|
||||
* invoked on a datagram sockets, however historically we would
|
||||
* actually tear socket down. This is known to be leveraged by
|
||||
* some applications to unblock process waiting in recv(2) by
|
||||
* other process that it shares that socket with. Try to meet
|
||||
* both backward-compatibility and POSIX requirements by forcing
|
||||
* ENOTCONN but still flushing buffers and performing wakeup(9).
|
||||
*
|
||||
* XXXGL: it remains unknown what applications expect this
|
||||
* behavior and is this isolated to unix/dgram or inet/dgram or
|
||||
* both. See: D10351, D3039.
|
||||
*/
|
||||
error = ENOTCONN;
|
||||
if (so->so_type != SOCK_DGRAM) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (error);
|
||||
}
|
||||
} else
|
||||
error = 0;
|
||||
if (SOLISTENING(so)) {
|
||||
if (how != SHUT_WR) {
|
||||
so->so_error = ECONNABORTED;
|
||||
solisten_wakeup(so); /* unlocks so */
|
||||
} else
|
||||
SOCK_UNLOCK(so);
|
||||
return (0);
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
UNP_PCB_LOCK(unp);
|
||||
socantsendmore(so);
|
||||
unp_shutdown(unp);
|
||||
UNP_PCB_UNLOCK(unp);
|
||||
return (0);
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
/*
|
||||
* XXXGL: so far it is safe to call sorflush() on unix/dgram,
|
||||
* because PR_RIGHTS flag saves us from destructive sbrelease()
|
||||
* on our protocol specific buffers.
|
||||
*/
|
||||
sorflush(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
sorflush(so);
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
UNP_PCB_LOCK(unp);
|
||||
socantsendmore(so);
|
||||
unp_shutdown(unp);
|
||||
UNP_PCB_UNLOCK(unp);
|
||||
}
|
||||
wakeup(&so->so_timeo);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -450,10 +450,22 @@ rts_disconnect(struct socket *so)
|
|||
}
|
||||
|
||||
static int
|
||||
rts_shutdown(struct socket *so)
|
||||
rts_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
/*
|
||||
* Note: route socket marks itself as connected through its lifetime.
|
||||
*/
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
sorflush(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
sorflush(so);
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
socantsendmore(so);
|
||||
}
|
||||
|
||||
socantsendmore(so);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
|
|
@ -982,16 +982,27 @@ rip_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
|||
}
|
||||
|
||||
static int
|
||||
rip_shutdown(struct socket *so)
|
||||
rip_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct inpcb *inp;
|
||||
|
||||
inp = sotoinpcb(so);
|
||||
KASSERT(inp != NULL, ("rip_shutdown: inp == NULL"));
|
||||
SOCK_LOCK(so);
|
||||
if (!(so->so_state & SS_ISCONNECTED)) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
sorflush(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
sorflush(so);
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
socantsendmore(so);
|
||||
}
|
||||
|
||||
INP_WLOCK(inp);
|
||||
socantsendmore(so);
|
||||
INP_WUNLOCK(inp);
|
||||
return (0);
|
||||
}
|
||||
#endif /* INET */
|
||||
|
|
|
@ -775,14 +775,39 @@ sctp_disconnect(struct socket *so)
|
|||
}
|
||||
|
||||
int
|
||||
sctp_flush(struct socket *so, int how)
|
||||
sctp_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct sctp_inpcb *inp = (struct sctp_inpcb *)so->so_pcb;
|
||||
struct epoch_tracker et;
|
||||
struct sctp_tcb *stcb;
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_nets *netp;
|
||||
struct sctp_queued_to_read *control, *ncontrol;
|
||||
struct sctp_inpcb *inp;
|
||||
struct mbuf *m, *op_err;
|
||||
bool need_to_abort = false;
|
||||
int error = 0;
|
||||
|
||||
MPASS(inp);
|
||||
|
||||
if (!((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
|
||||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)))
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
SOCK_LOCK(so);
|
||||
if ((so->so_state &
|
||||
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
if (SOLISTENING(so)) {
|
||||
if (how != SHUT_WR) {
|
||||
so->so_error = ECONNABORTED;
|
||||
solisten_wakeup(so); /* unlocks so */
|
||||
} else
|
||||
SOCK_UNLOCK(so);
|
||||
return (0);
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
/*
|
||||
* For 1-to-1 style sockets, flush the read queue and trigger an
|
||||
|
@ -790,106 +815,70 @@ sctp_flush(struct socket *so, int how)
|
|||
* messages are lost. Loosing notifications does not need to be
|
||||
* signalled to the peer.
|
||||
*/
|
||||
if (how == PRU_FLUSH_WR) {
|
||||
/* This function is only relevant for the read directions. */
|
||||
return (0);
|
||||
}
|
||||
inp = (struct sctp_inpcb *)so->so_pcb;
|
||||
if (inp == NULL) {
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
return (EINVAL);
|
||||
}
|
||||
SCTP_INP_WLOCK(inp);
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) {
|
||||
/* For 1-to-many style sockets this function does nothing. */
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
return (0);
|
||||
}
|
||||
stcb = LIST_FIRST(&inp->sctp_asoc_list);
|
||||
if (stcb != NULL) {
|
||||
SCTP_TCB_LOCK(stcb);
|
||||
}
|
||||
SCTP_INP_READ_LOCK(inp);
|
||||
inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_CANT_READ;
|
||||
SOCK_LOCK(so);
|
||||
TAILQ_FOREACH_SAFE(control, &inp->read_queue, next, ncontrol) {
|
||||
if ((control->spec_flags & M_NOTIFICATION) == 0) {
|
||||
need_to_abort = true;
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
case SHUT_RDWR:
|
||||
SCTP_INP_WLOCK(inp);
|
||||
stcb = LIST_FIRST(&inp->sctp_asoc_list);
|
||||
if (stcb != NULL) {
|
||||
SCTP_TCB_LOCK(stcb);
|
||||
}
|
||||
TAILQ_REMOVE(&inp->read_queue, control, next);
|
||||
control->on_read_q = 0;
|
||||
for (m = control->data; m; m = SCTP_BUF_NEXT(m)) {
|
||||
sctp_sbfree(control, control->stcb, &so->so_rcv, m);
|
||||
}
|
||||
if (control->on_strm_q == 0) {
|
||||
sctp_free_remote_addr(control->whoFrom);
|
||||
if (control->data) {
|
||||
sctp_m_freem(control->data);
|
||||
control->data = NULL;
|
||||
SCTP_INP_READ_LOCK(inp);
|
||||
inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_CANT_READ;
|
||||
SOCK_LOCK(so);
|
||||
TAILQ_FOREACH_SAFE(control, &inp->read_queue, next, ncontrol) {
|
||||
if ((control->spec_flags & M_NOTIFICATION) == 0) {
|
||||
need_to_abort = true;
|
||||
}
|
||||
TAILQ_REMOVE(&inp->read_queue, control, next);
|
||||
control->on_read_q = 0;
|
||||
for (m = control->data; m; m = SCTP_BUF_NEXT(m)) {
|
||||
sctp_sbfree(control, control->stcb,
|
||||
&so->so_rcv, m);
|
||||
}
|
||||
if (control->on_strm_q == 0) {
|
||||
sctp_free_remote_addr(control->whoFrom);
|
||||
if (control->data) {
|
||||
sctp_m_freem(control->data);
|
||||
control->data = NULL;
|
||||
}
|
||||
sctp_free_a_readq(stcb, control);
|
||||
} else {
|
||||
stcb->asoc.size_on_all_streams +=
|
||||
control->length;
|
||||
}
|
||||
sctp_free_a_readq(stcb, control);
|
||||
} else {
|
||||
stcb->asoc.size_on_all_streams += control->length;
|
||||
}
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
SCTP_INP_READ_UNLOCK(inp);
|
||||
if (need_to_abort && (stcb != NULL)) {
|
||||
inp->last_abort_code = SCTP_FROM_SCTP_USRREQ + SCTP_LOC_6;
|
||||
SOCK_UNLOCK(so);
|
||||
SCTP_INP_READ_UNLOCK(inp);
|
||||
if (need_to_abort && (stcb != NULL)) {
|
||||
inp->last_abort_code = SCTP_FROM_SCTP_USRREQ +
|
||||
SCTP_LOC_6;
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_OUT_OF_RESC,
|
||||
"");
|
||||
NET_EPOCH_ENTER(et);
|
||||
sctp_abort_an_association(inp, stcb, op_err, false,
|
||||
SCTP_SO_LOCKED);
|
||||
NET_EPOCH_EXIT(et);
|
||||
|
||||
error = ECONNABORTED;
|
||||
goto out;
|
||||
}
|
||||
if (stcb != NULL) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
}
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_OUT_OF_RESC, "");
|
||||
NET_EPOCH_ENTER(et);
|
||||
sctp_abort_an_association(inp, stcb, op_err, false, SCTP_SO_LOCKED);
|
||||
NET_EPOCH_EXIT(et);
|
||||
return (ECONNABORTED);
|
||||
}
|
||||
if (stcb != NULL) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
}
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
sctp_shutdown(struct socket *so)
|
||||
{
|
||||
struct sctp_inpcb *inp;
|
||||
|
||||
inp = (struct sctp_inpcb *)so->so_pcb;
|
||||
if (inp == NULL) {
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
return (EINVAL);
|
||||
}
|
||||
SCTP_INP_RLOCK(inp);
|
||||
/* For UDP model this is a invalid call */
|
||||
if (!((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
|
||||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))) {
|
||||
/* Restore the flags that the soshutdown took away. */
|
||||
SOCKBUF_LOCK(&so->so_rcv);
|
||||
so->so_rcv.sb_state &= ~SBS_CANTRCVMORE;
|
||||
SOCKBUF_UNLOCK(&so->so_rcv);
|
||||
/* This proc will wakeup for read and do nothing (I hope) */
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EOPNOTSUPP);
|
||||
return (EOPNOTSUPP);
|
||||
} else {
|
||||
/*
|
||||
* Ok, if we reach here its the TCP model and it is either a
|
||||
* SHUT_WR or SHUT_RDWR. This means we put the shutdown flag
|
||||
* against it.
|
||||
* XXXGL: does SCTP need sorflush()? This is what old
|
||||
* soshutdown() used to do for all kinds of sockets.
|
||||
*/
|
||||
struct epoch_tracker et;
|
||||
struct sctp_tcb *stcb;
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_nets *netp;
|
||||
sorflush(so);
|
||||
if (how == SHUT_RD)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
if ((so->so_state &
|
||||
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
case SHUT_WR:
|
||||
socantsendmore(so);
|
||||
|
||||
stcb = LIST_FIRST(&inp->sctp_asoc_list);
|
||||
if (stcb == NULL) {
|
||||
/*
|
||||
|
@ -898,14 +887,14 @@ sctp_shutdown(struct socket *so)
|
|||
* now.
|
||||
*/
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
return (0);
|
||||
goto out;
|
||||
}
|
||||
SCTP_TCB_LOCK(stcb);
|
||||
asoc = &stcb->asoc;
|
||||
if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
return (0);
|
||||
goto out;
|
||||
}
|
||||
if ((SCTP_GET_STATE(stcb) != SCTP_STATE_COOKIE_WAIT) &&
|
||||
(SCTP_GET_STATE(stcb) != SCTP_STATE_COOKIE_ECHOED) &&
|
||||
|
@ -916,7 +905,7 @@ sctp_shutdown(struct socket *so)
|
|||
*/
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
return (0);
|
||||
goto out;
|
||||
}
|
||||
NET_EPOCH_ENTER(et);
|
||||
if (stcb->asoc.alternate) {
|
||||
|
@ -961,7 +950,7 @@ sctp_shutdown(struct socket *so)
|
|||
sctp_abort_an_association(stcb->sctp_ep, stcb,
|
||||
op_err, false, SCTP_SO_LOCKED);
|
||||
NET_EPOCH_EXIT(et);
|
||||
return (0);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
@ -972,8 +961,11 @@ sctp_shutdown(struct socket *so)
|
|||
SCTP_TCB_UNLOCK(stcb);
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
NET_EPOCH_EXIT(et);
|
||||
return (0);
|
||||
}
|
||||
out:
|
||||
wakeup(&so->so_timeo);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -7523,7 +7515,6 @@ sctp_peeraddr(struct socket *so, struct sockaddr *sa)
|
|||
.pr_close = sctp_close, \
|
||||
.pr_detach = sctp_close, \
|
||||
.pr_sopoll = sopoll_generic, \
|
||||
.pr_flush = sctp_flush, \
|
||||
.pr_disconnect = sctp_disconnect, \
|
||||
.pr_listen = sctp_listen, \
|
||||
.pr_peeraddr = sctp_peeraddr, \
|
||||
|
|
|
@ -331,7 +331,7 @@ void
|
|||
sctp_notify(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *,
|
||||
uint8_t, uint8_t, uint16_t, uint32_t);
|
||||
int sctp_flush(struct socket *, int);
|
||||
int sctp_shutdown(struct socket *);
|
||||
int sctp_shutdown(struct socket *, enum shutdown_how);
|
||||
int
|
||||
sctp_bindx(struct socket *, int, struct sockaddr_storage *,
|
||||
int, int, struct proc *);
|
||||
|
|
|
@ -799,31 +799,57 @@ tcp6_usr_accept(struct socket *so, struct sockaddr *sa)
|
|||
* Mark the connection as being incapable of further output.
|
||||
*/
|
||||
static int
|
||||
tcp_usr_shutdown(struct socket *so)
|
||||
tcp_usr_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
int error = 0;
|
||||
struct inpcb *inp;
|
||||
struct tcpcb *tp;
|
||||
struct epoch_tracker et;
|
||||
struct inpcb *inp = sotoinpcb(so);
|
||||
struct tcpcb *tp = intotcpcb(inp);
|
||||
int error = 0;
|
||||
|
||||
inp = sotoinpcb(so);
|
||||
KASSERT(inp != NULL, ("inp == NULL"));
|
||||
INP_WLOCK(inp);
|
||||
if (inp->inp_flags & INP_DROPPED) {
|
||||
INP_WUNLOCK(inp);
|
||||
return (ECONNRESET);
|
||||
SOCK_LOCK(so);
|
||||
if ((so->so_state &
|
||||
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
tp = intotcpcb(inp);
|
||||
if (SOLISTENING(so)) {
|
||||
if (how != SHUT_WR) {
|
||||
so->so_error = ECONNABORTED;
|
||||
solisten_wakeup(so); /* unlocks so */
|
||||
} else
|
||||
SOCK_UNLOCK(so);
|
||||
return (0);
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
NET_EPOCH_ENTER(et);
|
||||
socantsendmore(so);
|
||||
tcp_usrclosed(tp);
|
||||
if (!(inp->inp_flags & INP_DROPPED))
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
sorflush(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
sorflush(so);
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
/*
|
||||
* XXXGL: mimicing old soshutdown() here. But shouldn't we
|
||||
* return ECONNRESEST for SHUT_RD as well?
|
||||
*/
|
||||
INP_WLOCK(inp);
|
||||
if (inp->inp_flags & INP_DROPPED) {
|
||||
INP_WUNLOCK(inp);
|
||||
return (ECONNRESET);
|
||||
}
|
||||
|
||||
socantsendmore(so);
|
||||
NET_EPOCH_ENTER(et);
|
||||
tcp_usrclosed(tp);
|
||||
error = tcp_output_nodrop(tp);
|
||||
tcp_bblog_pru(tp, PRU_SHUTDOWN, error);
|
||||
TCP_PROBE2(debug__user, tp, PRU_SHUTDOWN);
|
||||
error = tcp_unlock_or_drop(tp, error);
|
||||
NET_EPOCH_EXIT(et);
|
||||
tcp_bblog_pru(tp, PRU_SHUTDOWN, error);
|
||||
TCP_PROBE2(debug__user, tp, PRU_SHUTDOWN);
|
||||
error = tcp_unlock_or_drop(tp, error);
|
||||
NET_EPOCH_EXIT(et);
|
||||
}
|
||||
wakeup(&so->so_timeo);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
|
|
@ -1670,16 +1670,42 @@ udp_disconnect(struct socket *so)
|
|||
#endif /* INET */
|
||||
|
||||
int
|
||||
udp_shutdown(struct socket *so)
|
||||
udp_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct inpcb *inp;
|
||||
int error;
|
||||
|
||||
inp = sotoinpcb(so);
|
||||
KASSERT(inp != NULL, ("udp_shutdown: inp == NULL"));
|
||||
INP_WLOCK(inp);
|
||||
socantsendmore(so);
|
||||
INP_WUNLOCK(inp);
|
||||
return (0);
|
||||
SOCK_LOCK(so);
|
||||
if (!(so->so_state & SS_ISCONNECTED))
|
||||
/*
|
||||
* POSIX mandates us to just return ENOTCONN when shutdown(2) is
|
||||
* invoked on a datagram sockets, however historically we would
|
||||
* actually tear socket down. This is known to be leveraged by
|
||||
* some applications to unblock process waiting in recv(2) by
|
||||
* other process that it shares that socket with. Try to meet
|
||||
* both backward-compatibility and POSIX requirements by forcing
|
||||
* ENOTCONN but still flushing buffers and performing wakeup(9).
|
||||
*
|
||||
* XXXGL: it remains unknown what applications expect this
|
||||
* behavior and is this isolated to unix/dgram or inet/dgram or
|
||||
* both. See: D10351, D3039.
|
||||
*/
|
||||
error = ENOTCONN;
|
||||
else
|
||||
error = 0;
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
sorflush(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
sorflush(so);
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
socantsendmore(so);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
|
|
|
@ -168,7 +168,7 @@ udp_get_inpcbinfo(int protocol)
|
|||
int udp_ctloutput(struct socket *, struct sockopt *);
|
||||
void udplite_input(struct mbuf *, int);
|
||||
struct inpcb *udp_notify(struct inpcb *inp, int errno);
|
||||
int udp_shutdown(struct socket *so);
|
||||
int udp_shutdown(struct socket *, enum shutdown_how);
|
||||
|
||||
int udp_set_kernel_tunneling(struct socket *so, udp_tun_func_t f,
|
||||
udp_tun_icmp_t i, void *ctx);
|
||||
|
|
|
@ -827,16 +827,27 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
|||
}
|
||||
|
||||
static int
|
||||
rip6_shutdown(struct socket *so)
|
||||
rip6_shutdown(struct socket *so, enum shutdown_how how)
|
||||
{
|
||||
struct inpcb *inp;
|
||||
|
||||
inp = sotoinpcb(so);
|
||||
KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL"));
|
||||
SOCK_LOCK(so);
|
||||
if (!(so->so_state & SS_ISCONNECTED)) {
|
||||
SOCK_UNLOCK(so);
|
||||
return (ENOTCONN);
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
sorflush(so);
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
sorflush(so);
|
||||
/* FALLTHROUGH */
|
||||
case SHUT_WR:
|
||||
socantsendmore(so);
|
||||
}
|
||||
|
||||
INP_WLOCK(inp);
|
||||
socantsendmore(so);
|
||||
INP_WUNLOCK(inp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
|
|
@ -1095,7 +1095,6 @@ sctp6_getpeeraddr(struct socket *so, struct sockaddr *sa)
|
|||
.pr_close = sctp6_close, \
|
||||
.pr_detach = sctp6_close, \
|
||||
.pr_sopoll = sopoll_generic, \
|
||||
.pr_flush = sctp_flush, \
|
||||
.pr_disconnect = sctp_disconnect, \
|
||||
.pr_listen = sctp_listen, \
|
||||
.pr_peeraddr = sctp6_getpeeraddr, \
|
||||
|
|
|
@ -39,6 +39,7 @@ struct thread;
|
|||
struct sockaddr;
|
||||
struct socket;
|
||||
struct sockopt;
|
||||
enum shutdown_how;
|
||||
|
||||
/*#ifdef _KERNEL*/
|
||||
/*
|
||||
|
@ -84,8 +85,7 @@ typedef int pr_send_t(struct socket *, int, struct mbuf *,
|
|||
struct sockaddr *, struct mbuf *, struct thread *);
|
||||
typedef int pr_ready_t(struct socket *, struct mbuf *, int);
|
||||
typedef int pr_sense_t(struct socket *, struct stat *);
|
||||
typedef int pr_shutdown_t(struct socket *);
|
||||
typedef int pr_flush_t(struct socket *, int);
|
||||
typedef int pr_shutdown_t(struct socket *, enum shutdown_how);
|
||||
typedef int pr_sockaddr_t(struct socket *, struct sockaddr *);
|
||||
typedef int pr_sosend_t(struct socket *, struct sockaddr *, struct uio *,
|
||||
struct mbuf *, struct mbuf *, int, struct thread *);
|
||||
|
@ -137,7 +137,6 @@ struct protosw {
|
|||
pr_peeraddr_t *pr_peeraddr; /* getpeername(2) */
|
||||
pr_sockaddr_t *pr_sockaddr; /* getsockname(2) */
|
||||
pr_sense_t *pr_sense; /* stat(2) */
|
||||
pr_flush_t *pr_flush; /* XXXGL: merge with pr_shutdown_t! */
|
||||
pr_sosetlabel_t *pr_sosetlabel; /* MAC, XXXGL: remove */
|
||||
pr_setsbopt_t *pr_setsbopt; /* Socket buffer ioctls */
|
||||
};
|
||||
|
|
|
@ -633,14 +633,6 @@ enum shutdown_how {
|
|||
SHUT_RDWR /* shut down both sides */
|
||||
};
|
||||
|
||||
#if __BSD_VISIBLE
|
||||
/* for SCTP */
|
||||
/* we cheat and use the SHUT_XX defines for these */
|
||||
#define PRU_FLUSH_RD SHUT_RD
|
||||
#define PRU_FLUSH_WR SHUT_WR
|
||||
#define PRU_FLUSH_RDWR SHUT_RDWR
|
||||
#endif
|
||||
|
||||
#if __BSD_VISIBLE
|
||||
/*
|
||||
* sendfile(2) header/trailer struct
|
||||
|
|
Loading…
Reference in a new issue