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 + -
显示快捷键?