Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_usrreq.c
Show First 20 Lines • Show All 388 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
#ifdef INET | #ifdef INET | ||||
/* | /* | ||||
* Prepare to accept connections. | * Prepare to accept connections. | ||||
*/ | */ | ||||
static int | static int | ||||
tcp_usr_listen(struct socket *so, int backlog, struct thread *td) | tcp_usr_listen(struct socket *so, struct solisten *sol, int backlog, | ||||
struct thread *td) | |||||
{ | { | ||||
int error = 0; | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp = NULL; | struct tcpcb *tp; | ||||
TCPDEBUG0; | TCPDEBUG0; | ||||
if (so == NULL) | |||||
return (0); | |||||
inp = sotoinpcb(so); | inp = sotoinpcb(so); | ||||
KASSERT(inp != NULL, ("tcp_usr_listen: inp == NULL")); | KASSERT(inp != NULL, ("tcp_usr_listen: inp == NULL")); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | ||||
error = EINVAL; | INP_WUNLOCK(inp); | ||||
goto out; | return (EINVAL); | ||||
} | } | ||||
tp = intotcpcb(inp); | tp = intotcpcb(inp); | ||||
TCPDEBUG1(); | TCPDEBUG1(); | ||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
error = solisten_proto_check(so); | if (inp->inp_lport == 0) { | ||||
int error; | |||||
INP_HASH_WLOCK(&V_tcbinfo); | INP_HASH_WLOCK(&V_tcbinfo); | ||||
if (error == 0 && inp->inp_lport == 0) | |||||
error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); | error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); | ||||
INP_HASH_WUNLOCK(&V_tcbinfo); | INP_HASH_WUNLOCK(&V_tcbinfo); | ||||
if (error == 0) { | if (error) { | ||||
SOCK_UNLOCK(so); | |||||
INP_WUNLOCK(inp); | |||||
return (error); | |||||
} | |||||
} | |||||
tcp_state_change(tp, TCPS_LISTEN); | tcp_state_change(tp, TCPS_LISTEN); | ||||
solisten_proto(so, backlog); | inp->inp_flags2 |= INP_LISTENING; | ||||
inp->inp_solisten = sol; | |||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
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 | ||||
} | |||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
#ifdef TCP_RFC7413 | #ifdef TCP_RFC7413 | ||||
if (IS_FASTOPEN(tp->t_flags)) | if (IS_FASTOPEN(tp->t_flags)) | ||||
tp->t_tfo_pending = tcp_fastopen_alloc_counter(); | tp->t_tfo_pending = tcp_fastopen_alloc_counter(); | ||||
#endif | #endif | ||||
out: | |||||
TCPDEBUG2(PRU_LISTEN); | TCPDEBUG2(PRU_LISTEN); | ||||
TCP_PROBE2(debug__user, tp, PRU_LISTEN); | TCP_PROBE2(debug__user, tp, PRU_LISTEN); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (error); | return (0); | ||||
} | } | ||||
static void | |||||
tcp_usr_listenclose(struct solisten *sol) | |||||
{ | |||||
struct inpcb *inp; | |||||
struct tcpcb *tp; | |||||
inp = soltoinpcb(sol); | |||||
INP_INFO_RLOCK(&V_tcbinfo); | |||||
INP_WLOCK(inp); | |||||
KASSERT(inp->inp_flags2 & INP_LISTENING, | |||||
("%s: inp %p not listening", __func__, inp)); | |||||
KASSERT(!(inp->inp_flags & INP_DROPPED), | |||||
("%s: listening inp %p dropped", __func__, inp)); | |||||
tp = intotcpcb(inp); | |||||
TCPDEBUG2(PRU_CLOSE); | |||||
TCP_PROBE2(debug__user, tp, PRU_CLOSE); | |||||
tcp_disconnect(tp); | |||||
/* inp gone */ | |||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
} | |||||
#endif /* INET */ | #endif /* INET */ | ||||
#ifdef INET6 | #ifdef INET6 | ||||
static int | static int | ||||
tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) | tcp6_usr_listen(struct socket *so, struct solisten *sol, int backlog, | ||||
struct thread *td) | |||||
{ | { | ||||
int error = 0; | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp = NULL; | struct tcpcb *tp; | ||||
TCPDEBUG0; | TCPDEBUG0; | ||||
if (so == NULL) | |||||
return (0); | |||||
inp = sotoinpcb(so); | inp = sotoinpcb(so); | ||||
KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL")); | KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL")); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | ||||
error = EINVAL; | INP_WUNLOCK(inp); | ||||
goto out; | return (EINVAL); | ||||
} | } | ||||
tp = intotcpcb(inp); | tp = intotcpcb(inp); | ||||
TCPDEBUG1(); | TCPDEBUG1(); | ||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
error = solisten_proto_check(so); | if (inp->inp_lport == 0) { | ||||
INP_HASH_WLOCK(&V_tcbinfo); | int error; | ||||
if (error == 0 && 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; | ||||
INP_HASH_WLOCK(&V_tcbinfo); | |||||
error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); | error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); | ||||
} | |||||
INP_HASH_WUNLOCK(&V_tcbinfo); | INP_HASH_WUNLOCK(&V_tcbinfo); | ||||
if (error == 0) { | if (error) { | ||||
SOCK_UNLOCK(so); | |||||
INP_WUNLOCK(inp); | |||||
return (error); | |||||
} | |||||
} | |||||
tcp_state_change(tp, TCPS_LISTEN); | tcp_state_change(tp, TCPS_LISTEN); | ||||
solisten_proto(so, backlog); | inp->inp_flags2 |= INP_LISTENING; | ||||
inp->inp_solisten = sol; | |||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
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 | ||||
} | |||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
#ifdef TCP_RFC7413 | #ifdef TCP_RFC7413 | ||||
if (IS_FASTOPEN(tp->t_flags)) | if (IS_FASTOPEN(tp->t_flags)) | ||||
tp->t_tfo_pending = tcp_fastopen_alloc_counter(); | tp->t_tfo_pending = tcp_fastopen_alloc_counter(); | ||||
#endif | #endif | ||||
out: | |||||
TCPDEBUG2(PRU_LISTEN); | TCPDEBUG2(PRU_LISTEN); | ||||
TCP_PROBE2(debug__user, tp, PRU_LISTEN); | TCP_PROBE2(debug__user, tp, PRU_LISTEN); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (error); | return (0); | ||||
} | } | ||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
#ifdef INET | #ifdef INET | ||||
/* | /* | ||||
* Initiate connection to peer. | * Initiate connection to peer. | ||||
* Create a template for use in transmissions on this connection. | * Create a template for use in transmissions on this connection. | ||||
* Enter SYN_SENT state, and mark socket as connecting. | * Enter SYN_SENT state, and mark socket as connecting. | ||||
▲ Show 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | struct pr_usrreqs tcp_usrreqs = { | ||||
.pru_accept = tcp_usr_accept, | .pru_accept = tcp_usr_accept, | ||||
.pru_attach = tcp_usr_attach, | .pru_attach = tcp_usr_attach, | ||||
.pru_bind = tcp_usr_bind, | .pru_bind = tcp_usr_bind, | ||||
.pru_connect = tcp_usr_connect, | .pru_connect = tcp_usr_connect, | ||||
.pru_control = in_control, | .pru_control = in_control, | ||||
.pru_detach = tcp_usr_detach, | .pru_detach = tcp_usr_detach, | ||||
.pru_disconnect = tcp_usr_disconnect, | .pru_disconnect = tcp_usr_disconnect, | ||||
.pru_listen = tcp_usr_listen, | .pru_listen = tcp_usr_listen, | ||||
.pru_listenclose = tcp_usr_listenclose, | |||||
.pru_peeraddr = in_getpeeraddr, | .pru_peeraddr = in_getpeeraddr, | ||||
.pru_rcvd = tcp_usr_rcvd, | .pru_rcvd = tcp_usr_rcvd, | ||||
.pru_rcvoob = tcp_usr_rcvoob, | .pru_rcvoob = tcp_usr_rcvoob, | ||||
.pru_send = tcp_usr_send, | .pru_send = tcp_usr_send, | ||||
.pru_ready = tcp_usr_ready, | .pru_ready = tcp_usr_ready, | ||||
.pru_shutdown = tcp_usr_shutdown, | .pru_shutdown = tcp_usr_shutdown, | ||||
.pru_sockaddr = in_getsockaddr, | .pru_sockaddr = in_getsockaddr, | ||||
.pru_sosetlabel = in_pcbsosetlabel, | .pru_sosetlabel = in_pcbsosetlabel, | ||||
.pru_close = tcp_usr_close, | .pru_close = tcp_usr_close, | ||||
}; | }; | ||||
#endif /* INET */ | #endif /* INET */ | ||||
#ifdef INET6 | #ifdef INET6 | ||||
struct pr_usrreqs tcp6_usrreqs = { | struct pr_usrreqs tcp6_usrreqs = { | ||||
.pru_abort = tcp_usr_abort, | .pru_abort = tcp_usr_abort, | ||||
.pru_accept = tcp6_usr_accept, | .pru_accept = tcp6_usr_accept, | ||||
.pru_attach = tcp_usr_attach, | .pru_attach = tcp_usr_attach, | ||||
.pru_bind = tcp6_usr_bind, | .pru_bind = tcp6_usr_bind, | ||||
.pru_connect = tcp6_usr_connect, | .pru_connect = tcp6_usr_connect, | ||||
.pru_control = in6_control, | .pru_control = in6_control, | ||||
.pru_detach = tcp_usr_detach, | .pru_detach = tcp_usr_detach, | ||||
.pru_disconnect = tcp_usr_disconnect, | .pru_disconnect = tcp_usr_disconnect, | ||||
.pru_listen = tcp6_usr_listen, | .pru_listen = tcp6_usr_listen, | ||||
.pru_listenclose = tcp_usr_listenclose, | |||||
.pru_peeraddr = in6_mapped_peeraddr, | .pru_peeraddr = in6_mapped_peeraddr, | ||||
.pru_rcvd = tcp_usr_rcvd, | .pru_rcvd = tcp_usr_rcvd, | ||||
.pru_rcvoob = tcp_usr_rcvoob, | .pru_rcvoob = tcp_usr_rcvoob, | ||||
.pru_send = tcp_usr_send, | .pru_send = tcp_usr_send, | ||||
.pru_ready = tcp_usr_ready, | .pru_ready = tcp_usr_ready, | ||||
.pru_shutdown = tcp_usr_shutdown, | .pru_shutdown = tcp_usr_shutdown, | ||||
.pru_sockaddr = in6_mapped_sockaddr, | .pru_sockaddr = in6_mapped_sockaddr, | ||||
.pru_sosetlabel = in_pcbsosetlabel, | .pru_sosetlabel = in_pcbsosetlabel, | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \ | ||||
cleanup; \ | cleanup; \ | ||||
return (ECONNRESET); \ | return (ECONNRESET); \ | ||||
} \ | } \ | ||||
tp = intotcpcb(inp); \ | tp = intotcpcb(inp); \ | ||||
} while(0) | } while(0) | ||||
#define INP_WLOCK_RECHECK(inp) INP_WLOCK_RECHECK_CLEANUP((inp), /* noop */) | #define INP_WLOCK_RECHECK(inp) INP_WLOCK_RECHECK_CLEANUP((inp), /* noop */) | ||||
int | int | ||||
tcp_ctloutput(struct socket *so, struct sockopt *sopt) | tcp_ctloutput(void *xpcb, struct sockopt *sopt) | ||||
{ | { | ||||
int error; | struct inpcb *inp = (struct inpcb *)xpcb; | ||||
struct inpcb *inp; | |||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
struct tcp_function_block *blk; | struct tcp_function_block *blk; | ||||
struct tcp_function_set fsn; | struct tcp_function_set fsn; | ||||
int error; | |||||
error = 0; | error = 0; | ||||
inp = sotoinpcb(so); | |||||
KASSERT(inp != NULL, ("tcp_ctloutput: inp == NULL")); | |||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (sopt->sopt_level != IPPROTO_TCP) { | if (sopt->sopt_level != IPPROTO_TCP) { | ||||
#ifdef INET6 | #ifdef INET6 | ||||
if (inp->inp_vflag & INP_IPV6PROTO) { | if (inp->inp_vflag & INP_IPV6PROTO) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
error = ip6_ctloutput(so, sopt); | error = ip6_ctloutput(inp, sopt); | ||||
} | } | ||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
#if defined(INET6) && defined(INET) | #if defined(INET6) && defined(INET) | ||||
else | else | ||||
#endif | #endif | ||||
#ifdef INET | #ifdef INET | ||||
{ | { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
error = ip_ctloutput(so, sopt); | error = ip_ctloutput(inp, sopt); | ||||
} | } | ||||
#endif | #endif | ||||
return (error); | return (error); | ||||
} | } | ||||
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (ECONNRESET); | return (ECONNRESET); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | return (error); | ||||
(sopt->sopt_name == TCP_FUNCTION_BLK)) { | (sopt->sopt_name == TCP_FUNCTION_BLK)) { | ||||
strcpy(fsn.function_set_name, tp->t_fb->tfb_tcp_block_name); | strcpy(fsn.function_set_name, tp->t_fb->tfb_tcp_block_name); | ||||
fsn.pcbcnt = tp->t_fb->tfb_refcnt; | fsn.pcbcnt = tp->t_fb->tfb_refcnt; | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
error = sooptcopyout(sopt, &fsn, sizeof fsn); | error = sooptcopyout(sopt, &fsn, sizeof fsn); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* Pass in the INP locked, called must unlock it */ | /* Pass in the INP locked, called must unlock it */ | ||||
return (tp->t_fb->tfb_tcp_ctloutput(so, sopt, inp, tp)); | return (tp->t_fb->tfb_tcp_ctloutput(inp, sopt)); | ||||
} | } | ||||
int | int | ||||
tcp_default_ctloutput(struct socket *so, struct sockopt *sopt, struct inpcb *inp, struct tcpcb *tp) | tcp_default_ctloutput(struct inpcb *inp, struct sockopt *sopt) | ||||
{ | { | ||||
int error, opt, optval; | |||||
u_int ui; | |||||
struct tcp_info ti; | struct tcp_info ti; | ||||
struct cc_algo *algo; | struct cc_algo *algo; | ||||
struct tcpcb *tp = intotcpcb(inp); | |||||
char *pbuf, buf[TCP_CA_NAME_MAX]; | char *pbuf, buf[TCP_CA_NAME_MAX]; | ||||
size_t len; | size_t len; | ||||
int error, opt, optval; | |||||
u_int ui; | |||||
/* | /* | ||||
* For TCP_CCALGOOPT forward the control to CC module, for both | * For TCP_CCALGOOPT forward the control to CC module, for both | ||||
* SOPT_SET and SOPT_GET. | * SOPT_SET and SOPT_GET. | ||||
*/ | */ | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
case TCP_CCALGOOPT: | case TCP_CCALGOOPT: | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
▲ Show 20 Lines • Show All 408 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct inpcb *inp = tp->t_inpcb; | struct inpcb *inp = tp->t_inpcb; | ||||
struct socket *so = inp->inp_socket; | struct socket *so = inp->inp_socket; | ||||
INP_INFO_RLOCK_ASSERT(&V_tcbinfo); | INP_INFO_RLOCK_ASSERT(&V_tcbinfo); | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
/* | /* | ||||
* Neither tcp_close() nor tcp_drop() should return NULL, as the | * For a regular socket neither tcp_close() nor tcp_drop() should | ||||
* socket is still open. | * return NULL, as the socket is still open. | ||||
*/ | */ | ||||
if (tp->t_state < TCPS_ESTABLISHED) { | if (tp->t_state == TCPS_LISTEN) | ||||
tp = tcp_close(tp); | |||||
else if (tp->t_state < TCPS_ESTABLISHED) { | |||||
tp = tcp_close(tp); | tp = tcp_close(tp); | ||||
KASSERT(tp != NULL, | KASSERT(tp != NULL, | ||||
("tcp_disconnect: tcp_close() returned NULL")); | ("tcp_disconnect: tcp_close() returned NULL")); | ||||
} else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { | } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { | ||||
tp = tcp_drop(tp, 0); | tp = tcp_drop(tp, 0); | ||||
KASSERT(tp != NULL, | KASSERT(tp != NULL, | ||||
("tcp_disconnect: tcp_drop() returned NULL")); | ("tcp_disconnect: tcp_drop() returned NULL")); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 383 Lines • Show Last 20 Lines |