tcp_input.c
来自「eCos操作系统源码」· C语言 代码 · 共 2,368 行 · 第 1/5 页
C
2,368 行
* random urgent pointers, we'll crash in * soreceive. It's hard to imagine someone * actually wanting to send this much urgent data. */ if (th->th_urp + so->so_rcv.sb_cc > sb_max) { th->th_urp = 0; /* XXX */ tiflags &= ~TH_URG; /* XXX */ goto dodata; /* XXX */ } /* * If this segment advances the known urgent pointer, * then mark the data stream. This should not happen * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since * a FIN has been received from the remote side. * In these states we ignore the URG. * * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section as the original * spec states (in one of two places). */ if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) { tp->rcv_up = th->th_seq + th->th_urp; so->so_oobmark = so->so_rcv.sb_cc + (tp->rcv_up - tp->rcv_nxt) - 1; if (so->so_oobmark == 0) so->so_state |= SS_RCVATMARK; sohasoutofband(so); tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA); } /* * Remove out of band data so doesn't get presented to user. * This can happen independent of advancing the URG pointer, * but if two URG's are pending at once, some out-of-band * data may creep in... ick. */ if (th->th_urp <= (u_int16_t) tlen#ifdef SO_OOBINLINE && (so->so_options & SO_OOBINLINE) == 0#endif ) tcp_pulloutofband(so, th->th_urp, m, hdroptlen); } else /* * 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; } }#ifdef TCPDEBUG 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); }#endif /* TCPDEBUG */ /* * 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. */#ifdef TCPDEBUG 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); }#endif /* TCPDEBUG */ 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) || defined(TCP_NEWRENO)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
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?