Page MenuHomeFreeBSD

D43413.diff
No OneTemporary

D43413.diff

diff --git a/sys/dev/hyperv/hvsock/hv_sock.h b/sys/dev/hyperv/hvsock/hv_sock.h
--- a/sys/dev/hyperv/hvsock/hv_sock.h
+++ b/sys/dev/hyperv/hvsock/hv_sock.h
@@ -110,7 +110,7 @@
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);
diff --git a/sys/dev/hyperv/hvsock/hv_sock.c b/sys/dev/hyperv/hvsock/hv_sock.c
--- a/sys/dev/hyperv/hvsock/hv_sock.c
+++ b/sys/dev/hyperv/hvsock/hv_sock.c
@@ -978,43 +978,43 @@
}
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);
}
diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c
--- a/sys/kern/uipc_domain.c
+++ b/sys/kern/uipc_domain.c
@@ -151,7 +151,7 @@
}
static int
-pr_shutdown_notsupp(struct socket *so)
+pr_shutdown_notsupp(struct socket *so, enum shutdown_how how)
{
return (EOPNOTSUPP);
}
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -2966,59 +2966,18 @@
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)
{
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1660,18 +1660,65 @@
}
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
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -450,10 +450,22 @@
}
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);
}
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -982,16 +982,27 @@
}
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 */
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -775,14 +775,39 @@
}
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 @@
* 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;
- }
- 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;
+ 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);
}
- }
- 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);
- 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;
+ 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;
+ }
+ }
+ 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);
- 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 {
+ error = ECONNABORTED;
+ goto out;
+ }
+ if (stcb != NULL) {
+ SCTP_TCB_UNLOCK(stcb);
+ }
+ SCTP_INP_WUNLOCK(inp);
/*
- * 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 @@
* 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_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp);
- return (0);
+ goto out;
}
NET_EPOCH_ENTER(et);
if (stcb->asoc.alternate) {
@@ -961,7 +950,7 @@
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_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp);
NET_EPOCH_EXIT(et);
- return (0);
}
+out:
+ wakeup(&so->so_timeo);
+
+ return (error);
}
/*
@@ -7523,7 +7515,6 @@
.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, \
diff --git a/sys/netinet/sctp_var.h b/sys/netinet/sctp_var.h
--- a/sys/netinet/sctp_var.h
+++ b/sys/netinet/sctp_var.h
@@ -331,7 +331,7 @@
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 *);
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -799,31 +799,57 @@
* 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);
}
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1670,16 +1670,42 @@
#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
diff --git a/sys/netinet/udp_var.h b/sys/netinet/udp_var.h
--- a/sys/netinet/udp_var.h
+++ b/sys/netinet/udp_var.h
@@ -168,7 +168,7 @@
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);
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -827,16 +827,27 @@
}
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);
}
diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c
--- a/sys/netinet6/sctp6_usrreq.c
+++ b/sys/netinet6/sctp6_usrreq.c
@@ -1095,7 +1095,6 @@
.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, \
diff --git a/sys/sys/protosw.h b/sys/sys/protosw.h
--- a/sys/sys/protosw.h
+++ b/sys/sys/protosw.h
@@ -39,6 +39,7 @@
struct sockaddr;
struct socket;
struct sockopt;
+enum shutdown_how;
/*#ifdef _KERNEL*/
/*
@@ -84,8 +85,7 @@
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 @@
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 */
};
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -633,14 +633,6 @@
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

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 18, 1:22 AM (20 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14687993
Default Alt Text
D43413.diff (21 KB)

Event Timeline