Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102777681
D43413.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D43413.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D43413: sockets: make pr_shutdown fully protocol specific method
Attached
Detach File
Event Timeline
Log In to Comment