📄 tcp_input.c
字号:
/* * If no out of band data is expected, * pull receive urgent pointer along * with the receive window. */ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) tp->rcv_up = tp->rcv_nxt;dodata: /* XXX */ /* * Process the segment text, merging it into the TCP sequencing queue, * and arranging for acknowledgment of receipt if necessary. * This process logically involves adjusting tp->rcv_wnd as data * is presented to the user (this happens in tcp_usrreq.c, * case PRU_RCVD). If a FIN has already been received on this * connection then we just ignore the text. */ if ((tlen || (tiflags & TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { if (th->th_seq == tp->rcv_nxt && tp->segq.lh_first == NULL && tp->t_state == TCPS_ESTABLISHED) { if (th->th_flags & TH_PUSH) tp->t_flags |= TF_ACKNOW; else tp->t_flags |= TF_DELACK; tp->rcv_nxt += tlen; tiflags = th->th_flags & TH_FIN; tcpstat.tcps_rcvpack++; tcpstat.tcps_rcvbyte += tlen; ND6_HINT(tp); m_adj(m, hdroptlen); sbappend(&so->so_rcv, m); sorwakeup(so); } else { m_adj(m, hdroptlen); tiflags = tcp_reass(tp, th, m, &tlen); tp->t_flags |= TF_ACKNOW; }#ifdef TCP_SACK if (!tp->sack_disable) tcp_update_sack_list(tp); #endif /* * variable len never referenced again in modern BSD, * so why bother computing it ?? */#if 0 /* * Note the amount of data that peer has sent into * our window, in order to estimate the sender's * buffer size. */ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);#endif /* 0 */ } else { m_freem(m); tiflags &= ~TH_FIN; } /* * If FIN is received ACK the FIN and let the user know * that the connection is closing. Ignore a FIN received before * the connection is fully established. */ if ((tiflags & TH_FIN) && TCPS_HAVEESTABLISHED(tp->t_state)) { if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { socantrcvmore(so); tp->t_flags |= TF_ACKNOW; tp->rcv_nxt++; } switch (tp->t_state) { /* * In ESTABLISHED STATE enter the CLOSE_WAIT state. */ case TCPS_ESTABLISHED: tp->t_state = TCPS_CLOSE_WAIT; break; /* * If still in FIN_WAIT_1 STATE FIN has not been acked so * enter the CLOSING state. */ case TCPS_FIN_WAIT_1: tp->t_state = TCPS_CLOSING; break; /* * In FIN_WAIT_2 state enter the TIME_WAIT state, * starting the time-wait timer, turning off the other * standard timers. */ case TCPS_FIN_WAIT_2: tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; soisdisconnected(so); break; /* * In TIME_WAIT state restart the 2 MSL time_wait timer. */ case TCPS_TIME_WAIT: tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; break; } } if (so->so_options & SO_DEBUG) {#ifdef INET6 if (tp->pf == PF_INET6) tcp_trace(TA_INPUT, ostate, tp, (caddr_t) &tcp_saveti6, 0, tlen); else#endif /* INET6 */ tcp_trace(TA_INPUT, ostate, tp, (caddr_t) &tcp_saveti, 0, tlen); } /* * Return any desired output. */ if (needoutput || (tp->t_flags & TF_ACKNOW)) { (void) tcp_output(tp); } return;dropafterack: /* * Generate an ACK dropping incoming segment if it occupies * sequence space, where the ACK reflects our state. */ if (tiflags & TH_RST) goto drop; m_freem(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return;dropwithreset: /* * Generate a RST, dropping incoming segment. * Make ACK acceptable to originator of segment. * Don't bother to respond if destination was broadcast/multicast. */ if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST)) goto drop;#ifdef INET6 if (is_ipv6) { /* For following calls to tcp_respond */ ti = mtod(m, struct tcpiphdr *); if (IN6_IS_ADDR_MULTICAST(&ipv6->ip6_dst)) goto drop; } else {#endif /* INET6 */ if (IN_MULTICAST(ti->ti_dst.s_addr)) goto drop;#ifdef INET6 }#endif /* INET6 */ if (tiflags & TH_ACK) tcp_respond(tp, (caddr_t) ti, m, (tcp_seq)0, th->th_ack, TH_RST); else { if (tiflags & TH_SYN) tlen++; tcp_respond(tp, (caddr_t) ti, m, th->th_seq+tlen, (tcp_seq)0, TH_RST|TH_ACK); } /* destroy temporarily created socket */ if (dropsocket) (void) soabort(so); return;drop: /* * Drop space held by incoming segment and return. */ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) {#ifdef INET6 if (tp->pf == PF_INET6) tcp_trace(TA_DROP, ostate, tp, (caddr_t) &tcp_saveti6, 0, tlen); else#endif /* INET6 */ tcp_trace(TA_DROP, ostate, tp, (caddr_t) &tcp_saveti, 0, tlen); } m_freem(m); /* destroy temporarily created socket */ if (dropsocket) (void) soabort(so); return;#ifndef TUBA_INCLUDE}voidtcp_dooptions(tp, cp, cnt, th, ts_present, ts_val, ts_ecr) struct tcpcb *tp; u_char *cp; int cnt; struct tcphdr *th; int *ts_present; u_int32_t *ts_val, *ts_ecr;{ u_int16_t mss = 0; int opt, optlen; for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; if (opt == TCPOPT_EOL) break; if (opt == TCPOPT_NOP) optlen = 1; else { optlen = cp[1]; if (optlen <= 0) break; } switch (opt) { default: continue; case TCPOPT_MAXSEG: if (optlen != TCPOLEN_MAXSEG) continue; if (!(th->th_flags & TH_SYN)) continue; bcopy((char *) cp + 2, (char *) &mss, sizeof(mss)); NTOHS(mss); break; case TCPOPT_WINDOW: if (optlen != TCPOLEN_WINDOW) continue; if (!(th->th_flags & TH_SYN)) continue; tp->t_flags |= TF_RCVD_SCALE; tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT); break; case TCPOPT_TIMESTAMP: if (optlen != TCPOLEN_TIMESTAMP) continue; *ts_present = 1; bcopy((char *)cp + 2, (char *) ts_val, sizeof(*ts_val)); NTOHL(*ts_val); bcopy((char *)cp + 6, (char *) ts_ecr, sizeof(*ts_ecr)); NTOHL(*ts_ecr); /* * A timestamp received in a SYN makes * it ok to send timestamp requests and replies. */ if (th->th_flags & TH_SYN) { tp->t_flags |= TF_RCVD_TSTMP; tp->ts_recent = *ts_val; tp->ts_recent_age = tcp_now; } break; #ifdef TCP_SACK case TCPOPT_SACK_PERMITTED: if (tp->sack_disable || optlen!=TCPOLEN_SACK_PERMITTED) continue; if (th->th_flags & TH_SYN) /* MUST only be set on SYN */ tp->t_flags |= TF_SACK_PERMIT; break; case TCPOPT_SACK: if (tcp_sack_option(tp, th, cp, optlen)) continue; break;#endif } } /* Update t_maxopd and t_maxseg after all options are processed */ if (th->th_flags & TH_SYN) (void) tcp_mss(tp, mss); /* sets t_maxseg */}#if defined(TCP_SACK)u_long tcp_seq_subtract(a, b) u_long a, b;{ return ((long)(a - b)); }#endif#ifdef TCP_SACK /* * This function is called upon receipt of new valid data (while not in header * prediction mode), and it updates the ordered list of sacks. */void tcp_update_sack_list(tp) struct tcpcb *tp; { /* * First reported block MUST be the most recent one. Subsequent * blocks SHOULD be in the order in which they arrived at the * receiver. These two conditions make the implementation fully * compliant with RFC 2018. */ int i, j = 0, count = 0, lastpos = -1; struct sackblk sack, firstsack, temp[MAX_SACK_BLKS]; /* First clean up current list of sacks */ for (i = 0; i < tp->rcv_numsacks; i++) { sack = tp->sackblks[i]; if (sack.start == 0 && sack.end == 0) { count++; /* count = number of blocks to be discarded */ continue; } if (SEQ_LEQ(sack.end, tp->rcv_nxt)) { tp->sackblks[i].start = tp->sackblks[i].end = 0; count++; } else { temp[j].start = tp->sackblks[i].start; temp[j++].end = tp->sackblks[i].end; } } tp->rcv_numsacks -= count; if (tp->rcv_numsacks == 0) { /* no sack blocks currently (fast path) */ tcp_clean_sackreport(tp); if (SEQ_LT(tp->rcv_nxt, tp->rcv_laststart)) { /* ==> need first sack block */ tp->sackblks[0].start = tp->rcv_laststart; tp->sackblks[0].end = tp->rcv_lastend; tp->rcv_numsacks = 1; } return; } /* Otherwise, sack blocks are already present. */ for (i = 0; i < tp->rcv_numsacks; i++) tp->sackblks[i] = temp[i]; /* first copy back sack list */ if (SEQ_GEQ(tp->rcv_nxt, tp->rcv_lastend)) return; /* sack list remains unchanged */ /* * From here, segment just received should be (part of) the 1st sack. * Go through list, possibly coalescing sack block entries. */ firstsack.start = tp->rcv_laststart; firstsack.end = tp->rcv_lastend; for (i = 0; i < tp->rcv_numsacks; i++) { sack = tp->sackblks[i]; if (SEQ_LT(sack.end, firstsack.start) || SEQ_GT(sack.start, firstsack.end)) continue; /* no overlap */ if (sack.start == firstsack.start && sack.end == firstsack.end){ /* * identical block; delete it here since we will * move it to the front of the list. */ tp->sackblks[i].start = tp->sackblks[i].end = 0; lastpos = i; /* last posn with a zero entry */ continue; } if (SEQ_LEQ(sack.start, firstsack.start)) firstsack.start = sack.start; /* merge blocks */ if (SEQ_GEQ(sack.end, firstsack.end)) firstsack.end = sack.end; /* merge blocks */ tp->sackblks[i].start = tp->sackblks[i].end = 0; lastpos = i; /* last posn with a zero entry */ } if (lastpos != -1) { /* at least one merge */ for (i = 0, j = 1; i < tp->rcv_numsacks; i++) { sack = tp->sackblks[i]; if (sack.start == 0 && sack.end == 0) continue; temp[j++] = sack; } tp->rcv_numsacks = j; /* including first blk (added later) */ for (i = 1; i < tp->rcv_numsacks; i++) /* now copy back */ tp->sackblks[i] = temp[i]; } else { /* no merges -- shift sacks by 1 */ if (tp->rcv_numsacks < MAX_SACK_BLKS) tp->rcv_numsacks++; for (i = tp->rcv_numsacks-1; i > 0; i--) tp->sackblks[i] = tp->sackblks[i-1]; } tp->sackblks[0] = firstsack; return;} /* * Process the TCP SACK option. Returns 1 if tcp_dooptions() should continue, * and 0 otherwise, if the option was fine. tp->snd_holes is an ordered list * of holes (oldest to newest, in terms of the sequence space). */ inttcp_sack_option(tp, th, cp, optlen) struct tcpcb *tp; struct tcphdr *th; u_char *cp; int optlen;{ int tmp_olen; u_char *tmp_cp; struct sackhole *cur, *p, *temp; if (tp->sack_disable) return 1; /* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */ if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0) return 1; tmp_cp = cp + 2; tmp_olen = optlen - 2; if (tp->snd_numholes < 0) tp->snd_numholes = 0; if (tp->t_maxseg == 0) panic("tcp_sack_option"); /* Should never happen */ while (tmp_olen > 0) { struct sackblk sack; bcopy((char *) tmp_cp, (char *) &(sack.start), sizeof(tcp_seq)); NTOHL(sack.start); bcopy((char *) tmp_cp + sizeof(tcp_seq), (char *) &(sack.end), sizeof(tcp_seq)); NTOHL(sack.end); tmp_olen -= TCPOLEN_SACK; tmp_cp += TCPOLEN_SACK; if (SEQ_LEQ(sack.end, sack.start)) continue; /* bad SACK fields */ if (SEQ_LEQ(sack.end, tp->snd_una)) continue; /* old block */#if defined(TCP_SACK) && defined(TCP_FACK) /* Updates snd_fack. */ if (SEQ_GEQ(sack.end, tp->snd_fack)) tp->snd_fack = sack.end;#endif /* TCP_FACK */ if (SEQ_GT(th->th_ack, tp->snd_una)) { if (SEQ_LT(sack.start, th->th_ack)) continue; } else { if (SEQ_LT(sack.start, tp->snd_una)) continue; } if (SEQ_GT(sack.end, tp->snd_max)) continue; if (tp->snd_holes == 0) { /* first hole */ tp->snd_holes = (struct sackhole *) malloc(sizeof(struct sackhole), M_PCB, M_NOWAIT); if (tp->snd_holes == NULL) { /* ENOBUFS, so ignore SACKed block for now*/ continue; } cur = tp->snd_holes; cur->start = th->th_ack; cur->end = sack.start; cur->rxmit = cur->start; cur->next = 0; tp->snd_numholes = 1; tp->rcv_lastsack = sack.end; /* * dups is at least one. If more data has been * SACKed, it can be greater than one. */ cur->dups = min(tcprexmtthresh, ((sack.end - cur->end)/tp->t_maxseg)); if (cur->dups < 1) cur->dups = 1; continue; /* with next sack block */ } /* Go thru list of holes: p = previous, cur = current */ p = cur = tp->snd_holes; while (cur) { if (SEQ_LEQ(sack.end, cur->start)) /* SACKs data before the current hole */ break; /* no use going through more holes */ if (SEQ_GEQ(sack.start, cur->end)) { /* SACKs data beyond the current hole */ cur->dups++; if (
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -