diff options
Diffstat (limited to 'sys/netinet')
| -rw-r--r-- | sys/netinet/raw_ip.c | 49 | ||||
| -rw-r--r-- | sys/netinet/tcp_subr.c | 49 | ||||
| -rw-r--r-- | sys/netinet/tcp_timewait.c | 49 | ||||
| -rw-r--r-- | sys/netinet/tcp_usrreq.c | 147 | ||||
| -rw-r--r-- | sys/netinet/udp_usrreq.c | 32 |
5 files changed, 178 insertions, 148 deletions
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index f635c8959c34..dfe1c6831a36 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -622,12 +622,17 @@ rip_attach(struct socket *so, int proto, struct thread *td) } static void -rip_pcbdetach(struct socket *so, struct inpcb *inp) +rip_detach(struct socket *so) { + struct inpcb *inp; - INP_INFO_WLOCK_ASSERT(&ripcbinfo); - INP_LOCK_ASSERT(inp); + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("rip_detach: inp == NULL")); + KASSERT(inp->inp_faddr.s_addr == INADDR_ANY, + ("rip_detach: not closed")); + INP_INFO_WLOCK(&ripcbinfo); + INP_LOCK(inp); if (so == ip_mrouter && ip_mrouter_done) ip_mrouter_done(); if (ip_rsvp_force_done) @@ -636,32 +641,48 @@ rip_pcbdetach(struct socket *so, struct inpcb *inp) ip_rsvp_done(); in_pcbdetach(inp); in_pcbfree(inp); + INP_INFO_WUNLOCK(&ripcbinfo); } static void -rip_detach(struct socket *so) +rip_dodisconnect(struct socket *so, struct inpcb *inp) +{ + + INP_LOCK_ASSERT(inp); + + inp->inp_faddr.s_addr = INADDR_ANY; + SOCK_LOCK(so); + so->so_state &= ~SS_ISCONNECTED; + SOCK_UNLOCK(so); +} + +static void +rip_abort(struct socket *so) { struct inpcb *inp; inp = sotoinpcb(so); - KASSERT(inp != NULL, ("rip_detach: inp == NULL")); + KASSERT(inp != NULL, ("rip_abort: inp == NULL")); + INP_INFO_WLOCK(&ripcbinfo); INP_LOCK(inp); - rip_pcbdetach(so, inp); + rip_dodisconnect(so, inp); + INP_UNLOCK(inp); INP_INFO_WUNLOCK(&ripcbinfo); } static void -rip_abort(struct socket *so) +rip_close(struct socket *so) { struct inpcb *inp; inp = sotoinpcb(so); - KASSERT(inp != NULL, ("rip_abort: inp == NULL")); + KASSERT(inp != NULL, ("rip_close: inp == NULL")); + INP_INFO_WLOCK(&ripcbinfo); INP_LOCK(inp); - soisdisconnected(so); - rip_pcbdetach(so, inp); + rip_dodisconnect(so, inp); + INP_UNLOCK(inp); INP_INFO_WUNLOCK(&ripcbinfo); } @@ -677,10 +698,7 @@ rip_disconnect(struct socket *so) KASSERT(inp != NULL, ("rip_disconnect: inp == NULL")); INP_INFO_WLOCK(&ripcbinfo); INP_LOCK(inp); - inp->inp_faddr.s_addr = INADDR_ANY; - SOCK_LOCK(so); - so->so_state &= ~SS_ISCONNECTED; - SOCK_UNLOCK(so); + rip_dodisconnect(so, inp); INP_UNLOCK(inp); INP_INFO_WUNLOCK(&ripcbinfo); return (0); @@ -912,5 +930,6 @@ struct pr_usrreqs rip_usrreqs = { .pru_send = rip_send, .pru_shutdown = rip_shutdown, .pru_sockaddr = rip_sockaddr, - .pru_sosetlabel = in_pcbsosetlabel + .pru_sosetlabel = in_pcbsosetlabel, + .pru_close = rip_close, }; diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 676b22e15d94..64574cf9f1f7 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -808,18 +808,7 @@ tcp_close(struct tcpcb *tp) KASSERT(so->so_state & SS_PROTOREF, ("tcp_close: !SS_PROTOREF")); inp->inp_vflag &= ~INP_SOCKREF; - tcp_discardcb(tp); -#ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) { - in6_pcbdetach(inp); - in6_pcbfree(inp); - } else { -#endif - in_pcbdetach(inp); - in_pcbfree(inp); -#ifdef INET6 - } -#endif + INP_UNLOCK(inp); ACCEPT_LOCK(); SOCK_LOCK(so); so->so_state &= ~SS_PROTOREF; @@ -1789,12 +1778,6 @@ tcp_twstart(struct tcpcb *tp) KASSERT(so->so_state & SS_PROTOREF, ("tcp_twstart: !SS_PROTOREF")); inp->inp_vflag &= ~INP_SOCKREF; -#ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) - in6_pcbdetach(inp); - else -#endif - in_pcbdetach(inp); INP_UNLOCK(inp); ACCEPT_LOCK(); SOCK_LOCK(so); @@ -1847,12 +1830,11 @@ tcp_twclose(struct tcptw *tw, int reuse) /* * At this point, we are in one of two situations: * - * (1) We have no socket, just an inpcb<->twtcp pair. Release it all - * after validating. + * (1) We have no socket, just an inpcb<->twtcp pair. We can free + * all state. * - * (2) We have a socket, which we may or may now own the reference - * for. If we own the reference, release all the state after - * validating. If not, leave it for the socket close to clean up. + * (2) We have a socket -- if we own a reference, release it and + * notify the socket layer. */ inp = tw->tw_inpcb; KASSERT((inp->inp_vflag & INP_TIMEWAIT), ("tcp_twclose: !timewait")); @@ -1867,22 +1849,15 @@ tcp_twclose(struct tcptw *tw, int reuse) so = inp->inp_socket; if (so != NULL) { + /* + * If there's a socket, handle two cases: first, we own a + * strong reference, which we will now release, or we don't + * in which case another reference exists (XXXRW: think + * about this more), and we don't need to take action. + */ if (inp->inp_vflag & INP_SOCKREF) { - /* - * If a socket is present, and we own the only - * reference, we need to tear down the socket and the - * inpcb. - */ inp->inp_vflag &= ~INP_SOCKREF; -#ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) { - in6_pcbdetach(inp); - in6_pcbfree(inp); - } else { - in_pcbdetach(inp); - in_pcbfree(inp); - } -#endif + INP_UNLOCK(inp); ACCEPT_LOCK(); SOCK_LOCK(so); KASSERT(so->so_state & SS_PROTOREF, diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index 676b22e15d94..64574cf9f1f7 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -808,18 +808,7 @@ tcp_close(struct tcpcb *tp) KASSERT(so->so_state & SS_PROTOREF, ("tcp_close: !SS_PROTOREF")); inp->inp_vflag &= ~INP_SOCKREF; - tcp_discardcb(tp); -#ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) { - in6_pcbdetach(inp); - in6_pcbfree(inp); - } else { -#endif - in_pcbdetach(inp); - in_pcbfree(inp); -#ifdef INET6 - } -#endif + INP_UNLOCK(inp); ACCEPT_LOCK(); SOCK_LOCK(so); so->so_state &= ~SS_PROTOREF; @@ -1789,12 +1778,6 @@ tcp_twstart(struct tcpcb *tp) KASSERT(so->so_state & SS_PROTOREF, ("tcp_twstart: !SS_PROTOREF")); inp->inp_vflag &= ~INP_SOCKREF; -#ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) - in6_pcbdetach(inp); - else -#endif - in_pcbdetach(inp); INP_UNLOCK(inp); ACCEPT_LOCK(); SOCK_LOCK(so); @@ -1847,12 +1830,11 @@ tcp_twclose(struct tcptw *tw, int reuse) /* * At this point, we are in one of two situations: * - * (1) We have no socket, just an inpcb<->twtcp pair. Release it all - * after validating. + * (1) We have no socket, just an inpcb<->twtcp pair. We can free + * all state. * - * (2) We have a socket, which we may or may now own the reference - * for. If we own the reference, release all the state after - * validating. If not, leave it for the socket close to clean up. + * (2) We have a socket -- if we own a reference, release it and + * notify the socket layer. */ inp = tw->tw_inpcb; KASSERT((inp->inp_vflag & INP_TIMEWAIT), ("tcp_twclose: !timewait")); @@ -1867,22 +1849,15 @@ tcp_twclose(struct tcptw *tw, int reuse) so = inp->inp_socket; if (so != NULL) { + /* + * If there's a socket, handle two cases: first, we own a + * strong reference, which we will now release, or we don't + * in which case another reference exists (XXXRW: think + * about this more), and we don't need to take action. + */ if (inp->inp_vflag & INP_SOCKREF) { - /* - * If a socket is present, and we own the only - * reference, we need to tear down the socket and the - * inpcb. - */ inp->inp_vflag &= ~INP_SOCKREF; -#ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) { - in6_pcbdetach(inp); - in6_pcbfree(inp); - } else { - in_pcbdetach(inp); - in_pcbfree(inp); - } -#endif + INP_UNLOCK(inp); ACCEPT_LOCK(); SOCK_LOCK(so); KASSERT(so->so_state & SS_PROTOREF, diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 111a9830f809..549627bf7992 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -137,12 +137,13 @@ out: } /* - * tcp_detach() releases any protocol state that can be reasonably released - * when a socket shutdown is requested, and is a shared code path for - * tcp_usr_detach() and tcp_usr_abort(), the two socket close entry points. + * tcp_detach is called when the socket layer loses its final reference + * to the socket, be it a file descriptor reference, a reference from TCP, + * etc. At this point, there is only one case in which we will keep around + * inpcb state: time wait. * - * Accepts pcbinfo, inpcb locked, will unlock the inpcb (if needed) on - * return. + * This function can probably be re-absorbed back into tcp_usr_detach() now + * that there is a single detach path. */ static void tcp_detach(struct socket *so, struct inpcb *inp) @@ -158,19 +159,24 @@ tcp_detach(struct socket *so, struct inpcb *inp) KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp")); KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so")); + tp = intotcpcb(inp); + if (inp->inp_vflag & INP_TIMEWAIT) { + /* + * There are two cases to handle: one in which the time wait + * state is being discarded (INP_DROPPED), and one in which + * this connection will remain in timewait. In the former, + * it is time to discard all state (except tcptw, which has + * already been discarded by the timewait close code, which + * should be further up the call stack somewhere). In the + * latter case, we detach from the socket, but leave the pcb + * present until timewait ends. + * + * XXXRW: Would it be cleaner to free the tcptw here? + */ if (inp->inp_vflag & INP_DROPPED) { - /* - * Connection was in time wait and has been dropped; - * the calling path is either via tcp_twclose(), or - * as a result of an eventual soclose() after - * tcp_twclose() has been called. In either case, - * tcp_twclose() has detached the tcptw from the - * inpcb, so we just detach and free the inpcb. - * - * XXXRW: Would it be cleaner to free the tcptw - * here? - */ + KASSERT(tp == NULL, ("tcp_detach: INP_TIMEWAIT && " + "INP_DROPPED && tp != NULL")); #ifdef INET6 if (isipv6) { in6_pcbdetach(inp); @@ -183,11 +189,6 @@ tcp_detach(struct socket *so, struct inpcb *inp) } #endif } else { - /* - * Connection is in time wait and has not yet been - * dropped; allow the socket to be discarded, but - * need to keep inpcb until end of time wait. - */ #ifdef INET6 if (isipv6) in6_pcbdetach(inp); @@ -198,20 +199,21 @@ tcp_detach(struct socket *so, struct inpcb *inp) } } else { /* - * If not in timewait, there are two possible paths. First, - * the TCP connection is either embryonic or done, in which - * case we tear down all state. Second, it may still be - * active, in which case we acquire a reference to the socket - * and will free it later when TCP is done. + * If the connection is not in timewait, we consider two + * two conditions: one in which no further processing is + * necessary (dropped || embryonic), and one in which TCP is + * not yet done, but no longer requires the socket, so the + * pcb will persist for the time being. + * + * XXXRW: Does the second case still occur? */ - tp = intotcpcb(inp); if (inp->inp_vflag & INP_DROPPED || tp->t_state < TCPS_SYN_SENT) { tcp_discardcb(tp); #ifdef INET6 if (isipv6) { - in_pcbdetach(inp); - in_pcbfree(inp); + in6_pcbdetach(inp); + in6_pcbfree(inp); } else { #endif in_pcbdetach(inp); @@ -220,11 +222,12 @@ tcp_detach(struct socket *so, struct inpcb *inp) } #endif } else { - SOCK_LOCK(so); - so->so_state |= SS_PROTOREF; - SOCK_UNLOCK(so); - inp->inp_vflag |= INP_SOCKREF; - INP_UNLOCK(inp); +#ifdef INET6 + if (isipv6) + in6_pcbdetach(inp); + else +#endif + in_pcbdetach(inp); } } } @@ -251,15 +254,6 @@ tcp_usr_detach(struct socket *so) ("tcp_usr_detach: inp_socket == NULL")); TCPDEBUG1(); - /* - * First, if we still have full TCP state, and we're not dropped, - * initiate a disconnect. - */ - if (!(inp->inp_vflag & INP_TIMEWAIT) && - !(inp->inp_vflag & INP_DROPPED)) { - tp = intotcpcb(inp); - tcp_disconnect(tp); - } tcp_detach(so, inp); tp = NULL; TCPDEBUG2(PRU_DETACH); @@ -926,15 +920,13 @@ out: } /* - * Abort the TCP. - * - * First, drop the connection. Then collect state if possible. + * Abort the TCP. Drop the connection abruptly. */ static void tcp_usr_abort(struct socket *so) { struct inpcb *inp; - struct tcpcb *tp; + struct tcpcb *tp = NULL; TCPDEBUG0; inp = sotoinpcb(so); @@ -944,20 +936,63 @@ tcp_usr_abort(struct socket *so) INP_LOCK(inp); KASSERT(inp->inp_socket != NULL, ("tcp_usr_abort: inp_socket == NULL")); - TCPDEBUG1(); /* - * First, if we still have full TCP state, and we're not dropped, - * drop. + * If we still have full TCP state, and we're not dropped, drop. */ if (!(inp->inp_vflag & INP_TIMEWAIT) && !(inp->inp_vflag & INP_DROPPED)) { tp = intotcpcb(inp); + TCPDEBUG1(); tcp_drop(tp, ECONNABORTED); + TCPDEBUG2(PRU_ABORT); } - tcp_detach(so, inp); - tp = NULL; - TCPDEBUG2(PRU_DETACH); + if (!(inp->inp_vflag & INP_DROPPED)) { + SOCK_LOCK(so); + so->so_state |= SS_PROTOREF; + SOCK_UNLOCK(so); + inp->inp_vflag |= INP_SOCKREF; + } + INP_UNLOCK(inp); + INP_INFO_WUNLOCK(&tcbinfo); +} + +/* + * TCP socket is closed. Start friendly disconnect. + */ +static void +tcp_usr_close(struct socket *so) +{ + struct inpcb *inp; + struct tcpcb *tp = NULL; + TCPDEBUG0; + + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL")); + + INP_INFO_WLOCK(&tcbinfo); + INP_LOCK(inp); + KASSERT(inp->inp_socket != NULL, + ("tcp_usr_close: inp_socket == NULL")); + + /* + * If we still have full TCP state, and we're not dropped, initiate + * a disconnect. + */ + if (!(inp->inp_vflag & INP_TIMEWAIT) && + !(inp->inp_vflag & INP_DROPPED)) { + tp = intotcpcb(inp); + TCPDEBUG1(); + tcp_disconnect(tp); + TCPDEBUG2(PRU_CLOSE); + } + if (!(inp->inp_vflag & INP_DROPPED)) { + SOCK_LOCK(so); + so->so_state |= SS_PROTOREF; + SOCK_UNLOCK(so); + inp->inp_vflag |= INP_SOCKREF; + } + INP_UNLOCK(inp); INP_INFO_WUNLOCK(&tcbinfo); } @@ -1019,7 +1054,8 @@ struct pr_usrreqs tcp_usrreqs = { .pru_send = tcp_usr_send, .pru_shutdown = tcp_usr_shutdown, .pru_sockaddr = tcp_sockaddr, - .pru_sosetlabel = in_pcbsosetlabel + .pru_sosetlabel = in_pcbsosetlabel, + .pru_close = tcp_usr_close, }; #ifdef INET6 @@ -1039,7 +1075,8 @@ struct pr_usrreqs tcp6_usrreqs = { .pru_send = tcp_usr_send, .pru_shutdown = tcp_usr_shutdown, .pru_sockaddr = in6_mapped_sockaddr, - .pru_sosetlabel = in_pcbsosetlabel + .pru_sosetlabel = in_pcbsosetlabel, + .pru_close = tcp_usr_close, }; #endif /* INET6 */ diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 7fff2b62507e..695a7e1569de 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -959,9 +959,12 @@ udp_abort(struct socket *so) KASSERT(inp != NULL, ("udp_abort: inp == NULL")); INP_INFO_WLOCK(&udbinfo); INP_LOCK(inp); - soisdisconnected(so); - in_pcbdetach(inp); - in_pcbfree(inp); + if (inp->inp_faddr.s_addr != INADDR_ANY) { + in_pcbdisconnect(inp); + inp->inp_laddr.s_addr = INADDR_ANY; + soisdisconnected(so); + } + INP_UNLOCK(inp); INP_INFO_WUNLOCK(&udbinfo); } @@ -1007,6 +1010,24 @@ udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td) return error; } +static void +udp_close(struct socket *so) +{ + struct inpcb *inp; + + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("udp_close: inp == NULL")); + INP_INFO_WLOCK(&udbinfo); + INP_LOCK(inp); + if (inp->inp_faddr.s_addr != INADDR_ANY) { + in_pcbdisconnect(inp); + inp->inp_laddr.s_addr = INADDR_ANY; + soisdisconnected(so); + } + INP_UNLOCK(inp); + INP_INFO_WUNLOCK(&udbinfo); +} + static int udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td) { @@ -1041,6 +1062,8 @@ udp_detach(struct socket *so) inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp_detach: inp == NULL")); + KASSERT(inp->inp_faddr.s_addr == INADDR_ANY, + ("udp_detach: not disconnected")); INP_INFO_WLOCK(&udbinfo); INP_LOCK(inp); in_pcbdetach(inp); @@ -1131,5 +1154,6 @@ struct pr_usrreqs udp_usrreqs = { .pru_sosend = sosend_dgram, .pru_shutdown = udp_shutdown, .pru_sockaddr = udp_sockaddr, - .pru_sosetlabel = in_pcbsosetlabel + .pru_sosetlabel = in_pcbsosetlabel, + .pru_close = udp_close, }; |
