📄 tcpsock.c
字号:
svp->sv_initretrim = tcp_initial_rex_delay; svp->sv_minretrim = tcp_min_rex_delay; svp->sv_maxretrim = tcp_max_rex_delay; svp->sv_ackdelay = MAX_ACK_DELAY; svp->sv_ackEveryN = 2;#ifdef TCP_TIMESTAMPS svp->sv_flags2 &= ~SV_PROVIDE_TIMESTAMPS; /* Socket option default is off */ svp->sv_flags2 &= ~SV_TIMESTAMPS_ENABLED; /* This will be set if the timestamps option is succesfully negotiated. */ svp->sv_recent_rx_tmstmp = 0;#endif#ifdef TCP_SELACK_ENHANCEMENTS /* Selective acknowldegemnt processing and generation are by default disabled */ svp->sv_flags2 &= ~(SV_PERMIT_IN_SACKS | SV_OUT_SACKS_ALLOWED | SV_OUT_SACKS_ENABLED); svp->sv_num_out_sack_blocks = 0;#endif#ifdef TCP_SS_CA_FRETR_FREC svp->sv_flags2 |= SV_SLOW_START_CA; svp->sv_flags2 &= ~SV_FAST_RETR_RECOV;#endif /* TCP_SS_CA_FRETR_FREC */ svp->sv_flags2 &= ~SV_NAGLE_DISABLED; #ifdef TCP_TRANSACTION_TCP#ifdef T_TCP_TRANS_DEFAULT_ON svp->sv_t_tcp_flags = SV_TRANSACTION; #else svp->sv_t_tcp_flags = 0; #endif#endif /* TCP_TRANSACTION_TCP */ sop->so_hsize += SIZEOF_TCPH_T; /* Linger is OFF by default. But this does NOT imply that the close done on a TCP socket won't be graceful. It only won't be graceful if linger is ON, AND, if the linger time is zero. */ SET_SOCKET_OPTION_OFF(sop,SO_LINGER); sop->so_poptions = DEFAULT_LINGER_TIME; /* insert the new state vector into the tcp state vector queue */ (void) q_in(&tcp_q, &svp->sv_q); sv_detach(svp, tcpattach, true); return OK;} /* tcpattach *//*******************************************************************/export int tcp_bind (so_t * sop, saddr * to, int tolen){ fast tcpsv_t * svp; fast int err; fast ipa * ipap; fast u16 port; auto so_addr addr; struct in_sockaddr; so_t * nsop; use_critical; trace0(tcp_trace, "tcp_bind:\n"); trace2(tcp_tbind, "tcp_bind: [%d], sop->so_prp = %x\n", sop->so_index, sop->so_prp); err = 0; critical; if ((svp = sop->so_svp) == (tcpsv_t *)0) { debug0(tcp_debug, "tcp_bind: no state vector\n"); normal; return FNS_EINVAL; /* no state vector */ } sv_bind(svp, tcp_bind, true); normal; if (svp->sv_state != CLOSED) { /* not possible to bind in any other state */ debug1(tcp_debug, "tcp_bind: non-closed state, (%s)\n", tcp_st(svp->sv_state)); err = FNS_EINVAL; goto out; } if ((err = ip_inaddr(to, tolen, (so_addr *)&addr)) != 0) { debug1(tcp_debug, "tcp_bind: ip_inaddr returned %d\n", err); goto out; } /* Make sure not bound already by checking svp->sv_src */ if( (svp->sv_src.ip_port != 0) || (svp->sv_src.ip_nethost != 0) ) { err = FNS_EINVAL; goto out; } ipap = (ipa *)&addr.a_ipa; if ((port = ntohs(ipap->ip_port)) != (u16)0) { fast tcpsv_t * nsvp; /* look through the TCP sockets for a port collision. */ critical; for (nsvp = (tcpsv_t *)tcp_q.q_next; nsvp != (tcpsv_t *)&tcp_q; nsvp = (tcpsv_t *)nsvp->sv_q.q_next) { if (nsvp == svp) continue; /* If this state vector is not associated with a socket at present, of if its local port number is different than the port which is the object of this bind, no collision */ if ( ((nsop = sv_valid_sop(nsvp, nsop)) == (so_t *)0) || (ntohs(nsvp->sv_src.ip_port) != port) ) { continue; }#ifndef SOCKET_USE_OLD_SOCKOPT_FUNCTIONS /* The below logic implements the discussion of SO_REUSEPORT and SO_REUSEADDR in Stevens: Unix Network Programming Volume 1, second edition. */ /* The socket we are looking at has the same port number as the socket that is attempting to bind. If both sockets have the SO_REUSEPORT socket option in effect, then there is no need to look further -- complete port/address matches are allowed. */ if ( (GET_SOCKET_OPTION_STATUS(nsop,SO_REUSEPORT)) && (GET_SOCKET_OPTION_STATUS(sop,SO_REUSEPORT)) ) { continue; }#else /* NOTE: Does this need to be fixed for the case where we don't support the' SO_REUSEPORT socket option??? -- MBM */#endif /* No SO_REUSEPORT here, so cannot have a complete port/address match. But we can allow a port match if the SO_REUSEADDR socket option is effect, as long as the local IP address doesn't match. */ if ( ( (GET_SOCKET_OPTION_STATUS(nsop,SO_REUSEADDR)) == 0) || (nsvp->sv_src.ip_nethost == addr.a_ipa.ip_nethost) ) { normal; err = FNS_EADDRINUSE; goto out; } } /* for */ normal; } else { /* port 0 special case */ /* pick a new port number */ ipap->ip_port = htons((u16)(TCP_MIN_FREE + (tcp_port|(sop->so_index<<8)))); tcp_port++; tcp_port &= 0xFF; } /* otherwise, bind to 0.0.0.0, 0 */ trace2(tcp_tbind, "tcp_bind: binding [%d] to (%s)\n", sop->so_index, ipa2str(ipap, (char *)0)); stass(svp->sv_src, addr.a_ipa); /* grab it */out: sv_detach(svp, tcp_bind, true); return err;} /* tcp_bind *//*******************************************************************//* ULP abort */export int tcp_cabort (fast so_t * sop){ fast tcpsv_t * svp; use_critical; trace0(tcp_trace, "tcp_cabort:\n"); critical; if ((svp = sop->so_svp) == (tcpsv_t *)0) { /* no state vector */ debug0(tcp_debug, "tcp_cabort: no state vector\n"); normal; return FNS_ENOTCONN; } sv_bind(svp, tcp_cabort, true); normal; if (svp->sv_state != CLOSED) tcpabort(svp, FNS_ECONNABORTED); sv_detach(svp, tcp_cabort, true); return 0;}/*******************************************************************//* ULP close event */export int tcpclose (fast so_t * sop){ fast tcpsv_t * svp; int err = 0; use_critical; critical; trace0(tcp_trace, "tcpclose:\n"); if ((svp = sop->so_svp) == (tcpsv_t *)0) { /* state vector is already gone. Should not be an error */ goto out; } trace1(tcp_tstate, "tcpclose: close(%s)\n", ipa2str(&svp->sv_src, (char *)0)); sv_bind(svp, tcpclose, true); trace2(tcp_tclose, "tcpclose: [%d] state = %s\n", sop->so_index, tcp_st(svp->sv_state)); switch (svp->sv_state) { case CLOSED:kludge_o:; /* fake a non-closed state, so the close can occur */ set_state("tcpclose", svp, LISTEN); err = FNS_ENOTCONN; goto rslf; case LISTEN: case SYN_SENT: err = AB_UC;rslf: tcp_rslf(svp, err); /* Reset self changes state to CLOSED */ break; case ESTAB:#ifdef TCP_KEEP_ALIVE /* we are closing and don't need Keep-Alive any more */ if (so_keepalive(sop)) { /* if the socket is with Keep-Alive option set */ tcp_end_ka(svp); }#endif case SYN_RECVD: case CLOSE_WAIT:#ifndef TCP_TRANSACTION_TCP tcp_sfin(svp); /* state changes in 'tcp_sfin' */#else tcp_sfin(svp,true); /* state changes in 'tcp_sfin' */#endif /* TCP_TRANSACTION_TCP */ if ( svp->sv_state == CLOSED ) goto kludge_o; fallthru; case CLOSING: /* Connection closing */ case FIN1_WAIT: /* " " */ case FIN2_WAIT: /* " " */ case LAST_ACK: /* " " */ case TIME_WAIT: /* " " */ /* state is FIN1_WAIT, LAST_ACK, FIN2_WAIT, CLOSING or TIME_WAIT */ sop = sv_valid_sop(svp, sop); assert(sop != (so_t *)0, "tcpclose: 0 sop\n"); if (sop->so_rq.gq_cnt == 0 && q_empty(&sop->so_hq.gq_q)) {#ifdef TCP_TRANSACTION_TCPkludge_oo:;#endif gq_smoke(&sop->so_hq); /* shutdown the receive queues */ gq_smoke(&sop->so_rq); so_notify(sop, RSHUTDOWN_NOTIFY); } else {#ifdef TCP_TRANSACTION_TCP /* If this is a transaction TCP session and we are in the TIME_WAIT state, we don't want to do an abort (causing a "reset" to be sent), even though there is still something in the receive queue, because: 1) We observe that FreeBSD's t/tcp client doesn't send a reset under this circumstance, and; 2) Applications don't normally close before they know they've read all of the incoming data -- they are not robust applications if they behave this way (i.e., the lack of a reset perhaps leading the peer application to think the transaction completed is an application-layer bug, not a TCP protocol violation). */ if ( (svp->sv_t_tcp_flags & SV_TRANSACTION) && (svp->sv_state == TIME_WAIT) ) { goto kludge_oo; }#endif tcpabort(svp, AB_UC); /* abort if receive-data exists -- this causes a TCP reset to be sent */ } /* state is FIN1_WAIT, LAST_ACK, FIN2_WAIT, CLOSING, * TIME_WAIT or CLOSED */ sop = sv_valid_sop(svp, sop); if (sop != (so_t *)0) /* the socket and state vector part company */ sv_so_detach(svp);/* Notes on the conditions of the various states after 'closing' * FIN1_WAIT : Normally we can receive data in this state, but since we * now no-longer have a socket, we must abort if any new data * is received. We are expecting the ACK of our FIN as well as * the FIN of the remote TCP. We must time-out and abort if * our FIN is not ACKED within the normal timeout period. * FIN2_WAIT : We were in the FIN1_WAIT state, except that our FIN has * been ACKED. Since we cannot accept new data in this state, * nor are we going to generate any new data packets or FINs, * we must time-out after a reasonable amount of time, so that * this state vector does not hang about forever if the remote * side fails to send the expected FIN. * CLOSING : We were in the FIN1_WAIT state, and we have just received * the FIN from the remote TCP, and ACKED that FIN. We are still * expecting the ACK of our FIN, and as in the FIN1_WAIT state, * we must time-out and abort if our FIN is not ACKED within the * normal timeout period. * TIME_WAIT : Our FIN has been ACKED, and we have ACKED the remote * TCP's FIN. We are waiting in this state for two maximum * segment lifetimes to pick up any straggler-packets, or a * retransmission of the remote TCP's FIN in case our ACK was lost. * LAST_ACK : We were in the CLOSE_WAIT state, and have already seen * and ACKED the remote TCP's FIN, and sent off our FIN. We * are waiting for the ACK of our FIN before going on to the * CLOSED state. We must time-out and abort if our FIN is not * ACKED within the normal timeout period. */ if (svp->sv_state == FIN2_WAIT) { /* see note above */ svp->sv_abort = 1; /* start FIN2_WAIT timeout */ (void) t_start(svp->sv_rextcb, (u32)TWOMSL); } break; } sv_detach(svp, tcpclose, true);out: normal; return err;}/*******************************************************************//* "inner connect" processing. Called for either explicit connects *//* or implied (via tcp_send) connects *//* Was originally inline with tcp_connect. */export int tcp_connect_inner(so_t *sop, saddr *to, int tolen, tcpsv_t **svpp){ tcpsv_t *svp; int err; auto so_addr addr; use_critical; trace0(tcp_trace, "tcp_connect_inner:\n");#ifdef TCP_QUIET /* If TCP is in its quiet period after systm bootup, don't allow the connection */ if (tcp_quiet) { return(FNS_ENETDOWN); }#endif critical; if ((svp = sop->so_svp) == (tcpsv_t *) 0) { /* no state vector */ debug0(tcp_debug, "tcp_connect: no state vector\n"); normal; return FNS_ENOTCONN; } /* if */ *svpp = svp; sv_bind(svp, tcp_connect_inner, true); normal; trace3(tcp_trace, "tcp_connect state %x ref_cnt %x abort %x\n", svp->sv_state, svp->sv_refcnt, svp->sv_abort); if ( (err = svp->sv_err) != 0) { /* Connect failed (when sv_err is set, sv_state is set to CLOSED) */ goto i_out; } else { switch (svp->sv_state) { case CLOSED: break; /* This is the normal case */ case ESTAB: err = FNS_EALREADY; goto i_out; case LISTEN: err = FNS_EINVAL; goto i_out; default: /* Still trying to connect. Let the user know about it */ if (give_bs(sop->so_flags) == F_NONBLOCKING) { err = FNS_EALREADY; } else { /* MBM note: I don't think this case can be hit because
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -