/* * 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. * * 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) { struct tcpcb *tp; INP_INFO_WLOCK_ASSERT(&V_tcbinfo); INP_WLOCK_ASSERT(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_flags & 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_flags & INP_DROPPED) { KASSERT(tp == NULL, ("tcp_detach: INP_TIMEWAIT && " "INP_DROPPED && tp != NULL")); in_pcbdetach(inp); in_pcbfree(inp); } else { in_pcbdetach(inp); INP_WUNLOCK(inp); } } else { /* * 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? */ if (inp->inp_flags & INP_DROPPED || tp->t_state < TCPS_SYN_SENT) { tcp_discardcb(tp); in_pcbdetach(inp); in_pcbfree(inp); } else in_pcbdetach(inp); } }
/* * Move a TCP connection into TIME_WAIT state. * tcbinfo is locked. * inp is locked, and is unlocked before returning. */ void tcp_twstart(struct tcpcb *tp) { struct tcptw *tw; struct inpcb *inp = tp->t_inpcb; int acknow; struct socket *so; #ifdef INET6 //ScenSim-Port// int isipv6 = inp->inp_inc.inc_flags & INC_ISIPV6; #endif //ScenSim-Port// INP_INFO_WLOCK_ASSERT(&V_tcbinfo); /* tcp_tw_2msl_reset(). */ //ScenSim-Port// INP_WLOCK_ASSERT(inp); //ScenSim-Port// if (V_nolocaltimewait) { //ScenSim-Port// int error = 0; //ScenSim-Port//#ifdef INET6 //ScenSim-Port// if (isipv6) //ScenSim-Port// error = in6_localaddr(&inp->in6p_faddr); //ScenSim-Port//#endif //ScenSim-Port//#if defined(INET6) && defined(INET) //ScenSim-Port// else //ScenSim-Port//#endif //ScenSim-Port//#ifdef INET //ScenSim-Port// error = in_localip(inp->inp_faddr); //ScenSim-Port//#endif //ScenSim-Port// if (error) { //ScenSim-Port// tp = tcp_close(tp); //ScenSim-Port// if (tp != NULL) //ScenSim-Port// INP_WUNLOCK(inp); //ScenSim-Port// return; //ScenSim-Port// } //ScenSim-Port// } //ScenSim-Port// tw = uma_zalloc(V_tcptw_zone, M_NOWAIT); tw = (struct tcptw *)uma_zalloc(V_tcptw_zone, M_NOWAIT); //ScenSim-Port// if (tw == NULL) { tw = tcp_tw_2msl_scan(1); if (tw == NULL) { tp = tcp_close(tp); //ScenSim-Port// if (tp != NULL) //ScenSim-Port// INP_WUNLOCK(inp); return; } } tw->tw_inpcb = inp; /* * Recover last window size sent. */ //ScenSim-Port// KASSERT(SEQ_GEQ(tp->rcv_adv, tp->rcv_nxt), //ScenSim-Port// ("tcp_twstart negative window: tp %p rcv_nxt %u rcv_adv %u", tp, //ScenSim-Port// tp->rcv_nxt, tp->rcv_adv)); tw->last_win = (tp->rcv_adv - tp->rcv_nxt) >> tp->rcv_scale; /* * Set t_recent if timestamps are used on the connection. */ if ((tp->t_flags & (TF_REQ_TSTMP|TF_RCVD_TSTMP|TF_NOOPT)) == (TF_REQ_TSTMP|TF_RCVD_TSTMP)) { tw->t_recent = tp->ts_recent; tw->ts_offset = tp->ts_offset; } else { tw->t_recent = 0; tw->ts_offset = 0; } tw->snd_nxt = tp->snd_nxt; tw->rcv_nxt = tp->rcv_nxt; tw->iss = tp->iss; tw->irs = tp->irs; tw->t_starttime = tp->t_starttime; tw->tw_time = 0; /* XXX * If this code will * be used for fin-wait-2 state also, then we may need * a ts_recent from the last segment. */ acknow = tp->t_flags & TF_ACKNOW; /* * First, discard tcpcb state, which includes stopping its timers and * freeing it. tcp_discardcb() used to also release the inpcb, but * that work is now done in the caller. * * Note: soisdisconnected() call used to be made in tcp_discardcb(), * and might not be needed here any longer. */ tcp_discardcb(tp); so = inp->inp_socket; soisdisconnected(so); //ScenSim-Port// tw->tw_cred = crhold(so->so_cred); //ScenSim-Port// SOCK_LOCK(so); tw->tw_so_options = so->so_options; //ScenSim-Port// SOCK_UNLOCK(so); if (acknow) tcp_twrespond(tw, TH_ACK); inp->inp_ppcb = tw; inp->inp_flags |= INP_TIMEWAIT; tcp_tw_2msl_reset(tw, 0); /* * If the inpcb owns the sole reference to the socket, then we can * detach and free the socket as it is not needed in time wait. */ if (inp->inp_flags & INP_SOCKREF) { //ScenSim-Port// KASSERT(so->so_state & SS_PROTOREF, //ScenSim-Port// ("tcp_twstart: !SS_PROTOREF")); inp->inp_flags &= ~INP_SOCKREF; //ScenSim-Port// INP_WUNLOCK(inp); //ScenSim-Port// ACCEPT_LOCK(); //ScenSim-Port// SOCK_LOCK(so); so->so_state &= ~SS_PROTOREF; sofree(so); } else //ScenSim-Port// INP_WUNLOCK(inp); ; //ScenSim-Port// }