mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
socket: Properly interlock when transitioning to a listening socket
Currently, most protocols implement pru_listen with something like the
following:
SOCK_LOCK(so);
error = solisten_proto_check(so);
if (error) {
SOCK_UNLOCK(so);
return (error);
}
solisten_proto(so);
SOCK_UNLOCK(so);
solisten_proto_check() fails if the socket is connected or connecting.
However, the socket lock is not used during I/O, so this pattern is
racy.
The change modifies solisten_proto_check() to additionally acquire
socket buffer locks, and the calling thread holds them until
solisten_proto() or solisten_proto_abort() is called. Now that the
socket buffer locks are preserved across a listen(2), this change allows
socket I/O paths to properly interlock with listen(2).
This fixes a large number of syzbot reports, only one is listed below
and the rest will be dup'ed to it.
Reported by: syzbot+9fece8a63c0e27273821@syzkaller.appspotmail.com
Reviewed by: tuexen, gallatin
MFC after: 1 month
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D31659
This commit is contained in:
parent
c67f3b8b78
commit
bd4a39cc93
7 changed files with 130 additions and 46 deletions
|
|
@ -418,12 +418,14 @@ soalloc(struct vnet *vnet)
|
||||||
* a feature to change class of an existing lock, so we use DUPOK.
|
* a feature to change class of an existing lock, so we use DUPOK.
|
||||||
*/
|
*/
|
||||||
mtx_init(&so->so_lock, "socket", NULL, MTX_DEF | MTX_DUPOK);
|
mtx_init(&so->so_lock, "socket", NULL, MTX_DEF | MTX_DUPOK);
|
||||||
|
so->so_snd.sb_mtx = &so->so_snd_mtx;
|
||||||
|
so->so_rcv.sb_mtx = &so->so_rcv_mtx;
|
||||||
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_rcv.sb_sel = &so->so_rdsel;
|
||||||
so->so_snd.sb_sel = &so->so_wrsel;
|
so->so_snd.sb_sel = &so->so_wrsel;
|
||||||
sx_init(&so->so_snd.sb_sx, "so_snd_sx");
|
sx_init(&so->so_snd_sx, "so_snd_sx");
|
||||||
sx_init(&so->so_rcv.sb_sx, "so_rcv_sx");
|
sx_init(&so->so_rcv_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);
|
||||||
|
|
@ -487,8 +489,8 @@ sodealloc(struct socket *so)
|
||||||
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);
|
||||||
sx_destroy(&so->so_snd.sb_sx);
|
sx_destroy(&so->so_snd_sx);
|
||||||
sx_destroy(&so->so_rcv.sb_sx);
|
sx_destroy(&so->so_rcv_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);
|
||||||
}
|
}
|
||||||
|
|
@ -899,18 +901,48 @@ solisten(struct socket *so, int backlog, struct thread *td)
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare for a call to solisten_proto(). Acquire all socket buffer locks in
|
||||||
|
* order to interlock with socket I/O.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
solisten_proto_check(struct socket *so)
|
solisten_proto_check(struct socket *so)
|
||||||
{
|
{
|
||||||
|
|
||||||
SOCK_LOCK_ASSERT(so);
|
SOCK_LOCK_ASSERT(so);
|
||||||
|
|
||||||
if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING |
|
if ((so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING |
|
||||||
SS_ISDISCONNECTING))
|
SS_ISDISCONNECTING)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sleeping is not permitted here, so simply fail if userspace is
|
||||||
|
* attempting to transmit or receive on the socket. This kind of
|
||||||
|
* transient failure is not ideal, but it should occur only if userspace
|
||||||
|
* is misusing the socket interfaces.
|
||||||
|
*/
|
||||||
|
if (!sx_try_xlock(&so->so_snd_sx))
|
||||||
|
return (EAGAIN);
|
||||||
|
if (!sx_try_xlock(&so->so_rcv_sx)) {
|
||||||
|
sx_xunlock(&so->so_snd_sx);
|
||||||
|
return (EAGAIN);
|
||||||
|
}
|
||||||
|
mtx_lock(&so->so_snd_mtx);
|
||||||
|
mtx_lock(&so->so_rcv_mtx);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Undo the setup done by solisten_proto_check().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
solisten_proto_abort(struct socket *so)
|
||||||
|
{
|
||||||
|
mtx_unlock(&so->so_snd_mtx);
|
||||||
|
mtx_unlock(&so->so_rcv_mtx);
|
||||||
|
sx_xunlock(&so->so_snd_sx);
|
||||||
|
sx_xunlock(&so->so_rcv_sx);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
solisten_proto(struct socket *so, int backlog)
|
solisten_proto(struct socket *so, int backlog)
|
||||||
{
|
{
|
||||||
|
|
@ -920,6 +952,9 @@ solisten_proto(struct socket *so, int backlog)
|
||||||
sbintime_t sbrcv_timeo, sbsnd_timeo;
|
sbintime_t sbrcv_timeo, sbsnd_timeo;
|
||||||
|
|
||||||
SOCK_LOCK_ASSERT(so);
|
SOCK_LOCK_ASSERT(so);
|
||||||
|
KASSERT((so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING |
|
||||||
|
SS_ISDISCONNECTING)) == 0,
|
||||||
|
("%s: bad socket state %p", __func__, so));
|
||||||
|
|
||||||
if (SOLISTENING(so))
|
if (SOLISTENING(so))
|
||||||
goto listening;
|
goto listening;
|
||||||
|
|
@ -938,10 +973,6 @@ solisten_proto(struct socket *so, int backlog)
|
||||||
|
|
||||||
sbdestroy(&so->so_snd, so);
|
sbdestroy(&so->so_snd, so);
|
||||||
sbdestroy(&so->so_rcv, 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
|
#ifdef INVARIANTS
|
||||||
bzero(&so->so_rcv,
|
bzero(&so->so_rcv,
|
||||||
|
|
@ -974,6 +1005,11 @@ listening:
|
||||||
if (backlog < 0 || backlog > somaxconn)
|
if (backlog < 0 || backlog > somaxconn)
|
||||||
backlog = somaxconn;
|
backlog = somaxconn;
|
||||||
so->sol_qlimit = backlog;
|
so->sol_qlimit = backlog;
|
||||||
|
|
||||||
|
mtx_unlock(&so->so_snd_mtx);
|
||||||
|
mtx_unlock(&so->so_rcv_mtx);
|
||||||
|
sx_xunlock(&so->so_snd_sx);
|
||||||
|
sx_xunlock(&so->so_rcv_sx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -890,13 +890,17 @@ uipc_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
if (so->so_type != SOCK_STREAM && so->so_type != SOCK_SEQPACKET)
|
if (so->so_type != SOCK_STREAM && so->so_type != SOCK_SEQPACKET)
|
||||||
return (EOPNOTSUPP);
|
return (EOPNOTSUPP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Synchronize with concurrent connection attempts.
|
||||||
|
*/
|
||||||
|
error = 0;
|
||||||
unp = sotounpcb(so);
|
unp = sotounpcb(so);
|
||||||
KASSERT(unp != NULL, ("uipc_listen: unp == NULL"));
|
|
||||||
|
|
||||||
UNP_PCB_LOCK(unp);
|
UNP_PCB_LOCK(unp);
|
||||||
if (unp->unp_vnode == NULL) {
|
if (unp->unp_conn != NULL || (unp->unp_flags & UNP_CONNECTING) != 0)
|
||||||
/* Already connected or not bound to an address. */
|
error = EINVAL;
|
||||||
error = unp->unp_conn != NULL ? EINVAL : EDESTADDRREQ;
|
else if (unp->unp_vnode == NULL)
|
||||||
|
error = EDESTADDRREQ;
|
||||||
|
if (error != 0) {
|
||||||
UNP_PCB_UNLOCK(unp);
|
UNP_PCB_UNLOCK(unp);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
@ -1523,6 +1527,7 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
|
||||||
bcopy(soun->sun_path, buf, len);
|
bcopy(soun->sun_path, buf, len);
|
||||||
buf[len] = 0;
|
buf[len] = 0;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
unp = sotounpcb(so);
|
unp = sotounpcb(so);
|
||||||
UNP_PCB_LOCK(unp);
|
UNP_PCB_LOCK(unp);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
@ -1538,13 +1543,16 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
|
||||||
* lock the peer socket, to ensure that unp_conn cannot
|
* lock the peer socket, to ensure that unp_conn cannot
|
||||||
* transition between two valid sockets while locks are dropped.
|
* transition between two valid sockets while locks are dropped.
|
||||||
*/
|
*/
|
||||||
if (unp->unp_conn != NULL) {
|
if (SOLISTENING(so))
|
||||||
UNP_PCB_UNLOCK(unp);
|
error = EOPNOTSUPP;
|
||||||
return (EISCONN);
|
else if (unp->unp_conn != NULL)
|
||||||
|
error = EISCONN;
|
||||||
|
else if ((unp->unp_flags & UNP_CONNECTING) != 0) {
|
||||||
|
error = EALREADY;
|
||||||
}
|
}
|
||||||
if ((unp->unp_flags & UNP_CONNECTING) != 0) {
|
if (error != 0) {
|
||||||
UNP_PCB_UNLOCK(unp);
|
UNP_PCB_UNLOCK(unp);
|
||||||
return (EALREADY);
|
return (error);
|
||||||
}
|
}
|
||||||
if (unp->unp_pairbusy > 0) {
|
if (unp->unp_pairbusy > 0) {
|
||||||
unp->unp_flags |= UNP_WAITING;
|
unp->unp_flags |= UNP_WAITING;
|
||||||
|
|
|
||||||
|
|
@ -2506,14 +2506,17 @@ ng_btsocket_l2cap_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (pcb == NULL) {
|
if (pcb == NULL) {
|
||||||
|
solisten_proto_abort(so);
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (ng_btsocket_l2cap_node == NULL) {
|
if (ng_btsocket_l2cap_node == NULL) {
|
||||||
|
solisten_proto_abort(so);
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (pcb->psm == 0) {
|
if (pcb->psm == 0) {
|
||||||
|
solisten_proto_abort(so);
|
||||||
error = EADDRNOTAVAIL;
|
error = EADDRNOTAVAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -894,6 +894,7 @@ ng_btsocket_rfcomm_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
* from socreate()
|
* from socreate()
|
||||||
*/
|
*/
|
||||||
if (l2so == NULL) {
|
if (l2so == NULL) {
|
||||||
|
solisten_proto_abort(so);
|
||||||
error = socreate_error;
|
error = socreate_error;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -907,8 +908,10 @@ ng_btsocket_rfcomm_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
*/
|
*/
|
||||||
error = ng_btsocket_rfcomm_session_create(&s, l2so,
|
error = ng_btsocket_rfcomm_session_create(&s, l2so,
|
||||||
NG_HCI_BDADDR_ANY, NULL, td);
|
NG_HCI_BDADDR_ANY, NULL, td);
|
||||||
if (error != 0)
|
if (error != 0) {
|
||||||
|
solisten_proto_abort(so);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
l2so = NULL;
|
l2so = NULL;
|
||||||
}
|
}
|
||||||
SOCK_LOCK(so);
|
SOCK_LOCK(so);
|
||||||
|
|
|
||||||
|
|
@ -7201,7 +7201,8 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SCTP_INP_RLOCK(inp);
|
SCTP_INP_INFO_WLOCK();
|
||||||
|
SCTP_INP_WLOCK(inp);
|
||||||
#ifdef SCTP_LOCK_LOGGING
|
#ifdef SCTP_LOCK_LOGGING
|
||||||
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) {
|
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) {
|
||||||
sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK);
|
sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK);
|
||||||
|
|
@ -7209,10 +7210,9 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
|
||||||
#endif
|
#endif
|
||||||
SOCK_LOCK(so);
|
SOCK_LOCK(so);
|
||||||
error = solisten_proto_check(so);
|
error = solisten_proto_check(so);
|
||||||
SOCK_UNLOCK(so);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
SCTP_INP_RUNLOCK(inp);
|
SOCK_UNLOCK(so);
|
||||||
return (error);
|
goto out;
|
||||||
}
|
}
|
||||||
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
|
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
|
||||||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
|
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
|
||||||
|
|
@ -7223,39 +7223,44 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
|
||||||
* move the guy that was listener to the TCP Pool.
|
* move the guy that was listener to the TCP Pool.
|
||||||
*/
|
*/
|
||||||
if (sctp_swap_inpcb_for_listen(inp)) {
|
if (sctp_swap_inpcb_for_listen(inp)) {
|
||||||
SCTP_INP_RUNLOCK(inp);
|
SOCK_UNLOCK(so);
|
||||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE);
|
solisten_proto_abort(so);
|
||||||
return (EADDRINUSE);
|
error = EADDRINUSE;
|
||||||
|
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
|
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
|
||||||
(inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
|
(inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
|
||||||
/* We are already connected AND the TCP model */
|
SOCK_UNLOCK(so);
|
||||||
SCTP_INP_RUNLOCK(inp);
|
solisten_proto_abort(so);
|
||||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE);
|
error = EADDRINUSE;
|
||||||
return (EADDRINUSE);
|
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
SCTP_INP_RUNLOCK(inp);
|
|
||||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
|
if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
|
||||||
/* We must do a bind. */
|
if ((error = sctp_inpcb_bind_locked(inp, NULL, NULL, p))) {
|
||||||
if ((error = sctp_inpcb_bind(so, NULL, NULL, p))) {
|
SOCK_UNLOCK(so);
|
||||||
|
solisten_proto_abort(so);
|
||||||
/* bind error, probably perm */
|
/* bind error, probably perm */
|
||||||
return (error);
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SCTP_INP_WLOCK(inp);
|
|
||||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) == 0) {
|
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) == 0) {
|
||||||
SOCK_LOCK(so);
|
|
||||||
solisten_proto(so, backlog);
|
solisten_proto(so, backlog);
|
||||||
SOCK_UNLOCK(so);
|
} else {
|
||||||
|
solisten_proto_abort(so);
|
||||||
}
|
}
|
||||||
|
SOCK_UNLOCK(so);
|
||||||
if (backlog > 0) {
|
if (backlog > 0) {
|
||||||
inp->sctp_flags |= SCTP_PCB_FLAGS_ACCEPTING;
|
inp->sctp_flags |= SCTP_PCB_FLAGS_ACCEPTING;
|
||||||
} else {
|
} else {
|
||||||
inp->sctp_flags &= ~SCTP_PCB_FLAGS_ACCEPTING;
|
inp->sctp_flags &= ~SCTP_PCB_FLAGS_ACCEPTING;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
SCTP_INP_WUNLOCK(inp);
|
SCTP_INP_WUNLOCK(inp);
|
||||||
|
SCTP_INP_INFO_WUNLOCK();
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -457,10 +457,15 @@ tcp_usr_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
TCPDEBUG1();
|
TCPDEBUG1();
|
||||||
SOCK_LOCK(so);
|
SOCK_LOCK(so);
|
||||||
error = solisten_proto_check(so);
|
error = solisten_proto_check(so);
|
||||||
INP_HASH_WLOCK(&V_tcbinfo);
|
if (error != 0) {
|
||||||
if (error == 0 && inp->inp_lport == 0)
|
SOCK_UNLOCK(so);
|
||||||
error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred);
|
goto out;
|
||||||
INP_HASH_WUNLOCK(&V_tcbinfo);
|
}
|
||||||
|
if (inp->inp_lport == 0) {
|
||||||
|
INP_HASH_WLOCK(&V_tcbinfo);
|
||||||
|
error = in_pcbbind(inp, NULL, td->td_ucred);
|
||||||
|
INP_HASH_WUNLOCK(&V_tcbinfo);
|
||||||
|
}
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
tcp_state_change(tp, TCPS_LISTEN);
|
tcp_state_change(tp, TCPS_LISTEN);
|
||||||
solisten_proto(so, backlog);
|
solisten_proto(so, backlog);
|
||||||
|
|
@ -468,6 +473,8 @@ tcp_usr_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
if ((so->so_options & SO_NO_OFFLOAD) == 0)
|
if ((so->so_options & SO_NO_OFFLOAD) == 0)
|
||||||
tcp_offload_listen_start(tp);
|
tcp_offload_listen_start(tp);
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
solisten_proto_abort(so);
|
||||||
}
|
}
|
||||||
SOCK_UNLOCK(so);
|
SOCK_UNLOCK(so);
|
||||||
|
|
||||||
|
|
@ -504,12 +511,16 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
TCPDEBUG1();
|
TCPDEBUG1();
|
||||||
SOCK_LOCK(so);
|
SOCK_LOCK(so);
|
||||||
error = solisten_proto_check(so);
|
error = solisten_proto_check(so);
|
||||||
|
if (error != 0) {
|
||||||
|
SOCK_UNLOCK(so);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
INP_HASH_WLOCK(&V_tcbinfo);
|
INP_HASH_WLOCK(&V_tcbinfo);
|
||||||
if (error == 0 && inp->inp_lport == 0) {
|
if (inp->inp_lport == 0) {
|
||||||
inp->inp_vflag &= ~INP_IPV4;
|
inp->inp_vflag &= ~INP_IPV4;
|
||||||
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0)
|
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0)
|
||||||
inp->inp_vflag |= INP_IPV4;
|
inp->inp_vflag |= INP_IPV4;
|
||||||
error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred);
|
error = in6_pcbbind(inp, NULL, td->td_ucred);
|
||||||
}
|
}
|
||||||
INP_HASH_WUNLOCK(&V_tcbinfo);
|
INP_HASH_WUNLOCK(&V_tcbinfo);
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
|
|
@ -519,6 +530,8 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
|
||||||
if ((so->so_options & SO_NO_OFFLOAD) == 0)
|
if ((so->so_options & SO_NO_OFFLOAD) == 0)
|
||||||
tcp_offload_listen_start(tp);
|
tcp_offload_listen_start(tp);
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
solisten_proto_abort(so);
|
||||||
}
|
}
|
||||||
SOCK_UNLOCK(so);
|
SOCK_UNLOCK(so);
|
||||||
|
|
||||||
|
|
@ -581,6 +594,10 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||||
error = ECONNREFUSED;
|
error = ECONNREFUSED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (SOLISTENING(so)) {
|
||||||
|
error = EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
tp = intotcpcb(inp);
|
tp = intotcpcb(inp);
|
||||||
TCPDEBUG1();
|
TCPDEBUG1();
|
||||||
NET_EPOCH_ENTER(et);
|
NET_EPOCH_ENTER(et);
|
||||||
|
|
@ -643,6 +660,10 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||||
error = ECONNREFUSED;
|
error = ECONNREFUSED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (SOLISTENING(so)) {
|
||||||
|
error = EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
tp = intotcpcb(inp);
|
tp = intotcpcb(inp);
|
||||||
TCPDEBUG1();
|
TCPDEBUG1();
|
||||||
#ifdef INET
|
#ifdef INET
|
||||||
|
|
@ -1021,6 +1042,10 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
|
||||||
|
|
||||||
TCPDEBUG1();
|
TCPDEBUG1();
|
||||||
if (nam != NULL && tp->t_state < TCPS_SYN_SENT) {
|
if (nam != NULL && tp->t_state < TCPS_SYN_SENT) {
|
||||||
|
if (tp->t_state == TCPS_LISTEN) {
|
||||||
|
error = EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
switch (nam->sa_family) {
|
switch (nam->sa_family) {
|
||||||
#ifdef INET
|
#ifdef INET
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
|
|
@ -1119,6 +1144,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
|
||||||
sbappendstream(&so->so_snd, m, flags);
|
sbappendstream(&so->so_snd, m, flags);
|
||||||
m = NULL;
|
m = NULL;
|
||||||
if (nam && tp->t_state < TCPS_SYN_SENT) {
|
if (nam && tp->t_state < TCPS_SYN_SENT) {
|
||||||
|
KASSERT(tp->t_state == TCPS_CLOSED,
|
||||||
|
("%s: tp %p is listening", __func__, tp));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do implied connect if not yet connected,
|
* Do implied connect if not yet connected,
|
||||||
* initialize window to default value, and
|
* initialize window to default value, and
|
||||||
|
|
|
||||||
|
|
@ -453,6 +453,7 @@ void sofree(struct socket *so);
|
||||||
void sohasoutofband(struct socket *so);
|
void sohasoutofband(struct socket *so);
|
||||||
int solisten(struct socket *so, int backlog, struct thread *td);
|
int solisten(struct socket *so, int backlog, struct thread *td);
|
||||||
void solisten_proto(struct socket *so, int backlog);
|
void solisten_proto(struct socket *so, int backlog);
|
||||||
|
void solisten_proto_abort(struct socket *so);
|
||||||
int solisten_proto_check(struct socket *so);
|
int solisten_proto_check(struct socket *so);
|
||||||
int solisten_dequeue(struct socket *, struct socket **, int);
|
int solisten_dequeue(struct socket *, struct socket **, int);
|
||||||
struct socket *
|
struct socket *
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue