📄 tcp_input.pc
字号:
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; /* * Process options if not in LISTEN state, * else do it below (after getting remote address). */ if (tp->t_state != TCPS_LISTEN){ tcp_dooptions(tp, optp, optlen, ti, &topt, tcp_now, tcp_stat); datap = optp; } /* * Header prediction: check for the two common cases * of a uni-directional data xfer. If the packet has * no control flags, is in-sequence, the window didn't * change and we're not retransmitting, it's a * candidate. If the length is zero and the ack moved * forward, we're the sender side of the xfer. Just * free the data acked & wake any higher level process * that was blocked waiting for space. If the length * is non-zero and the ack didn't move, we're the * receiver side. If we're getting packets in-order * (the reassembly queue is empty), add the data to * the socket buffer and note that we need a delayed ack. * Make sure that the hidden state-flags are also off. * Since we check for TCPS_ESTABLISHED above, it can only * be TH_NEEDSYN. */ if (tp->t_state == TCPS_ESTABLISHED && (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) == TH_ACK && ((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) && ((topt.to_flag & TOF_TS) == 0 || TSTMP_GEQ(topt.to_tsval, tp->ts_recent)) && ti->ti_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && tp->snd_nxt == tp->snd_max) { /* * If last ACK falls within this segment's sequence numbers, * record the timestamp. * NOTE that the test is modified according to the latest * proposal of the tcplw@cray.com list (Braden 1993/04/26). */ if ((topt.to_flag & TOF_TS) != 0 && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) { tp->ts_recent_age = tcp_now; tp->ts_recent = topt.to_tsval; } if (ti->ti_len == 0) { if (SEQ_GT(ti->ti_ack, tp->snd_una) && SEQ_LEQ(ti->ti_ack, tp->snd_max) && tp->snd_cwnd >= tp->snd_wnd && tp->t_dupacks < tcprexmtthresh) { /* * this is a pure ack for outstanding data. */ if (tcp_stat) ++tcp_stat->tcps_predack; if ((topt.to_flag & TOF_TS) != 0) tcp_xmit_timer(tp, tcp_now - topt.to_tsecr + 1, tcp_stat); else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) tcp_xmit_timer(tp, tp->t_rtt, tcp_stat); acked = ti->ti_ack - tp->snd_una; if (tcp_stat) { tcp_stat->tcps_rcvackpack++; tcp_stat->tcps_rcvackbyte += acked; } del_buf(node, inp, acked); tp->snd_una = ti->ti_ack; /* * If all outstanding data are acked, stop * retransmit timer, otherwise restart timer * using current (possibly backed-off) value. * If process is waiting for space, * wakeup/selwakeup/signal. If data * are ready to send, let tcp_output * decide between more output or persist. */ if (tp->snd_una == tp->snd_max) tp->t_timer[TCPT_REXMT] = 0; else if (tp->t_timer[TCPT_PERSIST] == 0) tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; if (inp->inp_snd.cc) tcp_output(node, tp, tcp_now, tcp_stat); pc_free(tcp_seg); return; } } else if (ti->ti_ack == tp->snd_una && tp->seg_next == (struct tcpiphdr *)tp && ti->ti_len <= sbspace(inp)) { /* * this is a pure, in-sequence data packet * with nothing on the reassembly queue and * we have enough buffer space to take it. */ tp->rcv_nxt += ti->ti_len; if (tcp_stat) ++tcp_stat->tcps_preddat; /* * submit data to application. */ submit_data(node, inp->app_proto_type, inp->con_id, datap, ti->ti_len, tp); if (TCP_ACK_HACK) { /* * If this is a short packet, then ACK now - with Nagel * congestion avoidance sender won't send more until * he gets an ACK. */ if (tiflags & TH_PUSH) { tp->t_flags |= TF_ACKNOW; tcp_output(node, tp, tcp_now, tcp_stat); } else { tp->t_flags |= TF_DELACK; } } else { tp->t_flags |= TF_DELACK; } pc_free(tcp_seg); return; } } /* * Calculate amount of space in receive window, * and then do TCP input processing. * Receive window is amount of space in rcv queue, * but not less than advertised window. */ { int win; win = sbspace(inp); if (win < 0) win = 0; tp->rcv_wnd = MAX(win, (int)(tp->rcv_adv - tp->rcv_nxt)); } switch (tp->t_state) { /* * If the state is LISTEN then ignore segment if it contains an RST. * If the segment contains an ACK then it is bad and send a RST. * If it does not contain a SYN then it is not interesting; drop it. * Don't bother responding if the destination was a broadcast. * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial * tp->iss, and send a segment: * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. * Fill in remote peer address fields if not previously specified. * Enter SYN_RECEIVED state, and process any other fields of this * segment in this state. */ case TCPS_LISTEN: if (tiflags & TH_RST) goto drop; if (tiflags & TH_ACK) goto dropwithreset; if ((tiflags & TH_SYN) == 0) goto drop; tp->t_template = (struct tcpiphdr *) tcp_template(tp); if (tp->t_template == 0) { tp = (struct tcpcb *)tcp_drop(node, tp, tcp_now, tcp_stat); goto drop; } tcp_dooptions(tp, optp, optlen, ti, &topt, tcp_now, tcp_stat); datap = optp; if (iss) tp->iss = iss; else tp->iss = *tcp_iss; *tcp_iss += TCP_ISSINCR/4; tp->irs = ti->ti_seq; tcp_sendseqinit(tp); tcp_rcvseqinit(tp); /* * Initialization of the tcpcb for transaction; * set SND.WND = SEG.WND, */ tp->snd_wnd = tiwin; /* initial send-window */ /* * do a standard 3-way handshake. */ tp->t_flags |= TF_ACKNOW; tp->t_state = TCPS_SYN_RECEIVED; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; if (tcp_stat) tcp_stat->tcps_accepts++; goto trimthenstep6; /* * If the state is SYN_RECEIVED: * do just the ack and RST checks from SYN_SENT state. * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment * initialize tp->rcv_nxt and tp->irs * if seg contains ack then advance tp->snd_una * if SYN has been acked change to ESTABLISHED else SYN_RCVD state * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_RECEIVED: case TCPS_SYN_SENT: if ((tiflags & TH_ACK) && (SEQ_LEQ(ti->ti_ack, tp->iss) || SEQ_GT(ti->ti_ack, tp->snd_max))) goto dropwithreset; if (tiflags & TH_RST) { if (tiflags & TH_ACK) tp = tcp_drop(node, tp, tcp_now, tcp_stat); goto drop; } if (tp->t_state == TCPS_SYN_RECEIVED) break; if ((tiflags & TH_SYN) == 0) goto drop; tp->snd_wnd = ti->ti_win; /* initial send window */ tp->irs = ti->ti_seq; tcp_rcvseqinit(tp); if (tiflags & TH_ACK) { if (tcp_stat) tcp_stat->tcps_connects++; /* Do window scaling on this connection? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } tp->rcv_adv += tp->rcv_wnd; tp->snd_una++; /* SYN is acked */ /* * If there's data, delay ACK; if there's also a FIN * ACKNOW will be turned on later. */ if (ti->ti_len != 0) tp->t_flags |= TF_DELACK; else tp->t_flags |= TF_ACKNOW; /* * Received <SYN,ACK> in SYN_SENT[*] state. * Transitions: * SYN_SENT --> ESTABLISHED * SYN_SENT* --> FIN_WAIT_1 */ if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; tiflags &= ~TH_SYN; } else { Message *msg; TransportToAppOpenResult *tcpOpenResult; if (inp->usrreq == INPCB_USRREQ_OPEN) flag = TCP_CONN_ACTIVE_OPEN; else flag = TCP_CONN_PASSIVE_OPEN; msg = GLOMO_MsgAlloc(node, GLOMO_APP_LAYER, inp->app_proto_type, MSG_APP_FromTransOpenResult); GLOMO_MsgInfoAlloc(node, msg, sizeof(TransportToAppOpenResult)); tcpOpenResult = (TransportToAppOpenResult *) msg->info; tcpOpenResult->type = flag; tcpOpenResult->localAddr = inp->inp_local_addr; tcpOpenResult->localPort = inp->inp_local_port; tcpOpenResult->remoteAddr = inp->inp_remote_addr; tcpOpenResult->remotePort = inp->inp_remote_port; tcpOpenResult->connectionId = inp->con_id; tcpOpenResult->uniqueId = inp->unique_id; GLOMO_MsgSend(node, msg, TRANSPORT_DELAY); inp->usrreq = INPCB_USRREQ_CONNECTED; tp->t_state = TCPS_ESTABLISHED; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; } } else { /* * Received initial SYN in SYN-SENT[*] state => simul- * taneous open. */ tp->t_flags |= TF_ACKNOW; tp->t_timer[TCPT_REXMT] = 0; tp->t_state = TCPS_SYN_RECEIVED; }trimthenstep6: /* * Advance ti->ti_seq to correspond to first data byte. * If data, trim to stay within window, * dropping FIN if necessary. */ ti->ti_seq++; if (ti->ti_len > tp->rcv_wnd) { todrop = ti->ti_len - tp->rcv_wnd; ti->ti_len = tp->rcv_wnd; tiflags &= ~TH_FIN; if (tcp_stat) { tcp_stat->tcps_rcvpackafterwin++; tcp_stat->tcps_rcvbyteafterwin += todrop; } } tp->snd_wl1 = ti->ti_seq - 1; tp->rcv_up = ti->ti_seq; if (tiflags & TH_ACK) goto process_ACK; goto step6; case TCPS_LAST_ACK: case TCPS_CLOSING: case TCPS_TIME_WAIT: break; /* continue normal processing */ } /* * States other than LISTEN or SYN_SENT. * First check timestamp, if present. * Then check the connection count, if present. * Then check that at least some bytes of segment are within * receive window. If segment begins before rcv_nxt, * drop leading data (and SYN); if nothing left, just ack. * * RFC 1323 PAWS: If we have a timestamp reply on this segment * and it's less than ts_recent, drop it. */ if ((topt.to_flag & TOF_TS) != 0 && (tiflags & TH_RST) == 0 && tp->ts_recent && TSTMP_LT(topt.to_tsval, tp->ts_recent)) { /* Check to see if ts_recent is over 24 days old. */ if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) { /* * Invalidate ts_recent. If this segment updates * ts_recent, the age will be reset later and ts_recent * will get a valid value. If it does not, setting * ts_recent to zero will at least satisfy the * requirement that zero be placed in the timestamp * echo reply when ts_recent isn't valid. The * age isn't reset until we get a valid ts_recent * because we don't want out-of-order segments to be * dropped when ts_recent is old. */ tp->ts_recent = 0; } else { if (tcp_stat) { tcp_stat->tcps_rcvduppack++; tcp_stat->tcps_rcvdupbyte += ti->ti_len; tcp_stat->tcps_pawsdrop++; } goto dropafterack; } } todrop = tp->rcv_nxt - ti->ti_seq; if (todrop > 0) { if (tiflags & TH_SYN) { tiflags &= ~TH_SYN; ti->ti_seq++; if (ti->ti_urp > 1) ti->ti_urp --; else tiflags &= ~TH_URG; todrop--; } /* * Following if statement from Stevens, vol. 2, p. 960. */ if (todrop > ti->ti_len || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) { /* * Any valid FIN must be to the left of the window. * At this point the FIN must be a duplicate or out * of sequence; drop it. */ tiflags &= ~TH_FIN; /* * Send an ACK to resynchronize and drop any data. * But keep on processing for RST or ACK. */ tp->t_flags |= TF_ACKNOW; todrop = ti->ti_len; if (tcp_stat) { tcp_stat->tcps_rcvduppack++; tcp_stat->tcps_rcvdupbyte += todrop; } } else { if (tcp_stat) { tcp_stat->tcps_rcvpartduppack++; tcp_stat->tcps_rcvpartdupbyte += todrop; } } data_adj(ti, todrop); ti->ti_seq += todrop; ti->ti_len -= todrop; if (ti->ti_urp > todrop) ti->ti_urp -= todrop; else { tiflags &= ~TH_URG; ti->ti_urp = 0; } } /* * If new data are received on a connection after the * user processes are gone, then RST the other end. */ if (tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) { tp = tcp_close(node, tp, tcp_stat); if (tcp_stat) tcp_stat->tcps_rcvafterclose++; goto dropwithreset; } /* * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); if (todrop > 0) { if (tcp_stat) tcp_stat->tcps_rcvpackafterwin++; if (todrop >= ti->ti_len) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -