tcp_output.c

来自「eCos操作系统源码」· C语言 代码 · 共 949 行 · 第 1/2 页

C
949
字号
		 */		case TH_SYN:			opt[optlen++] = TCPOPT_NOP;			opt[optlen++] = TCPOPT_NOP;			opt[optlen++] = tp->t_flags & TF_SENDCCNEW ?						TCPOPT_CCNEW : TCPOPT_CC;			opt[optlen++] = TCPOLEN_CC;			*(u_int32_t *)&opt[optlen] = htonl(tp->cc_send); 			optlen += 4;			break;		/*		 * This is a SYN,ACK; send CC and CC.echo if we received		 * CC from our peer.		 */		case (TH_SYN|TH_ACK):			if (tp->t_flags & TF_RCVD_CC) {				opt[optlen++] = TCPOPT_NOP;				opt[optlen++] = TCPOPT_NOP;				opt[optlen++] = TCPOPT_CC;				opt[optlen++] = TCPOLEN_CC;				*(u_int32_t *)&opt[optlen] =					htonl(tp->cc_send);				optlen += 4;				opt[optlen++] = TCPOPT_NOP;				opt[optlen++] = TCPOPT_NOP;				opt[optlen++] = TCPOPT_CCECHO;				opt[optlen++] = TCPOLEN_CC;				*(u_int32_t *)&opt[optlen] =					htonl(tp->cc_recv);				optlen += 4;			}			break;		} 	} 	hdrlen += optlen;#ifdef INET6	if (isipv6)		ipoptlen = ip6_optlen(tp->t_inpcb);	else#endif      {	if (tp->t_inpcb->inp_options) {		ipoptlen = tp->t_inpcb->inp_options->m_len -				offsetof(struct ipoption, ipopt_list);	} else {		ipoptlen = 0;	}      }#ifdef IPSEC	ipoptlen += ipsec_hdrsiz_tcp(tp);#endif	/*	 * Adjust data length if insertion of options will	 * bump the packet length beyond the t_maxopd length.	 * Clear the FIN bit because we cut off the tail of	 * the segment.	 */	if (len + optlen + ipoptlen > tp->t_maxopd) {		/*		 * If there is still more to send, don't close the connection.		 */		flags &= ~TH_FIN;		len = tp->t_maxopd - optlen - ipoptlen;		sendalot = 1;	}/*#ifdef DIAGNOSTIC*/#ifdef INET6 	if (max_linkhdr + hdrlen > MCLBYTES)		panic("tcphdr too big");#else 	if (max_linkhdr + hdrlen > MHLEN)		panic("tcphdr too big");#endif/*#endif*/	/*	 * Grab a header mbuf, attaching a copy of data to	 * be transmitted, and initialize the header from	 * the template for sends on this connection.	 */	if (len) {		if (tp->t_force && len == 1)			tcpstat.tcps_sndprobe++;		else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {			tcpstat.tcps_sndrexmitpack++;			tcpstat.tcps_sndrexmitbyte += len;		} else {			tcpstat.tcps_sndpack++;			tcpstat.tcps_sndbyte += len;		}#ifdef notyet		if ((m = m_copypack(so->so_snd.sb_mb, off,		    (int)len, max_linkhdr + hdrlen)) == 0) {			error = ENOBUFS;			goto out;		}		/*		 * m_copypack left space for our hdr; use it.		 */		m->m_len += hdrlen;		m->m_data -= hdrlen;#else		MGETHDR(m, M_DONTWAIT, MT_HEADER);		if (m == NULL) {			error = ENOBUFS;			goto out;		}#ifdef INET6		if (MHLEN < hdrlen + max_linkhdr) {			MCLGET(m, M_DONTWAIT);			if ((m->m_flags & M_EXT) == 0) {				m_freem(m);				error = ENOBUFS;				goto out;			}		}#endif		m->m_data += max_linkhdr;		m->m_len = hdrlen;		if (len <= MHLEN - hdrlen - max_linkhdr) {			m_copydata(so->so_snd.sb_mb, off, (int) len,			    mtod(m, caddr_t) + hdrlen);			m->m_len += len;		} else {			m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);			if (m->m_next == 0) {				(void) m_free(m);				error = ENOBUFS;				goto out;			}		}#endif		/*		 * If we're sending everything we've got, set PUSH.		 * (This will keep happy those implementations which only		 * give data to the user when a buffer fills or		 * a PUSH comes in.)		 */		if (off + len == so->so_snd.sb_cc)			flags |= TH_PUSH;	} else {		if (tp->t_flags & TF_ACKNOW)			tcpstat.tcps_sndacks++;		else if (flags & (TH_SYN|TH_FIN|TH_RST))			tcpstat.tcps_sndctrl++;		else if (SEQ_GT(tp->snd_up, tp->snd_una))			tcpstat.tcps_sndurg++;		else			tcpstat.tcps_sndwinup++;		MGETHDR(m, M_DONTWAIT, MT_HEADER);		if (m == NULL) {			error = ENOBUFS;			goto out;		}#ifdef INET6		if (isipv6 && (MHLEN < hdrlen + max_linkhdr) &&		    MHLEN >= hdrlen) {			MH_ALIGN(m, hdrlen);		} else#endif		m->m_data += max_linkhdr;		m->m_len = hdrlen;	}	m->m_pkthdr.rcvif = (struct ifnet *)0;#ifdef INET6	if (isipv6) {		ip6 = mtod(m, struct ip6_hdr *);		th = (struct tcphdr *)(ip6 + 1);		tcp_fillheaders(tp, ip6, th);	} else#endif /* INET6 */      {	ip = mtod(m, struct ip *);	ipov = (struct ipovly *)ip;	th = (struct tcphdr *)(ip + 1);	/* this picks up the pseudo header (w/o the length) */	tcp_fillheaders(tp, ip, th);      }	/*	 * Fill in fields, remembering maximum advertised	 * window for use in delaying messages about window sizes.	 * If resending a FIN, be sure not to use a new sequence number.	 */	if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&	    tp->snd_nxt == tp->snd_max)		tp->snd_nxt--;	/*	 * If we are doing retransmissions, then snd_nxt will	 * not reflect the first unsent octet.  For ACK only	 * packets, we do not want the sequence number of the	 * retransmitted packet, we want the sequence number	 * of the next unsent octet.  So, if there is no data	 * (and no SYN or FIN), use snd_max instead of snd_nxt	 * when filling in ti_seq.  But if we are in persist	 * state, snd_max might reflect one byte beyond the	 * right edge of the window, so use snd_nxt in that	 * case, since we know we aren't doing a retransmission.	 * (retransmit and persist are mutually exclusive...)	 */	if (len || (flags & (TH_SYN|TH_FIN)) 	    || callout_active(tp->tt_persist))		th->th_seq = htonl(tp->snd_nxt);	else		th->th_seq = htonl(tp->snd_max);	th->th_ack = htonl(tp->rcv_nxt);	if (optlen) {		bcopy(opt, th + 1, optlen);		th->th_off = (sizeof (struct tcphdr) + optlen) >> 2;	}	th->th_flags = flags;	/*	 * Calculate receive window.  Don't shrink window,	 * but avoid silly window syndrome.	 */	if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)		win = 0;	if (win < (long)(tp->rcv_adv - tp->rcv_nxt))		win = (long)(tp->rcv_adv - tp->rcv_nxt);	if (win > (long)TCP_MAXWIN << tp->rcv_scale)		win = (long)TCP_MAXWIN << tp->rcv_scale;	th->th_win = htons((u_short) (win>>tp->rcv_scale));	if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {		th->th_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));		th->th_flags |= TH_URG;	} else		/*		 * If no urgent pointer to send, then we pull		 * the urgent pointer to the left edge of the send window		 * so that it doesn't drift into the send window on sequence		 * number wraparound.		 */		tp->snd_up = tp->snd_una;		/* drag it along */	/*	 * Put TCP length in extended header, and then	 * checksum extended header and data.	 */	m->m_pkthdr.len = hdrlen + len; /* in6_cksum() need this */#ifdef INET6	if (isipv6)		/*		 * ip6_plen is not need to be filled now, and will be filled		 * in ip6_output.		 */		th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),				       sizeof(struct tcphdr) + optlen + len);	else#endif /* INET6 */      {	m->m_pkthdr.csum_flags = CSUM_TCP;	m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);	if (len + optlen)		th->th_sum = in_addword(th->th_sum, 		    htons((u_short)(optlen + len)));	/* IP version must be set here for ipv4/ipv6 checking later */      }	/*	 * In transmit state, time the transmission and arrange for	 * the retransmit.  In persist state, just set snd_max.	 */	if (tp->t_force == 0 || !callout_active(tp->tt_persist)) {		tcp_seq startseq = tp->snd_nxt;		/*		 * Advance snd_nxt over sequence space of this segment.		 */		if (flags & (TH_SYN|TH_FIN)) {			if (flags & TH_SYN)				tp->snd_nxt++;			if (flags & TH_FIN) {				tp->snd_nxt++;				tp->t_flags |= TF_SENTFIN;			}		}		tp->snd_nxt += len;		if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {			tp->snd_max = tp->snd_nxt;			/*			 * Time this transmission if not a retransmission and			 * not currently timing anything.			 */			if (tp->t_rtttime == 0) {				tp->t_rtttime = ticks;				tp->t_rtseq = startseq;				tcpstat.tcps_segstimed++;			}		}		/*		 * Set retransmit timer if not currently set,		 * and not doing an ack or a keep-alive probe.		 * Initial value for retransmit timer is smoothed		 * round-trip time + 2 * round-trip time variance.		 * Initialize shift counter which is used for backoff		 * of retransmit time.		 */		if (!callout_active(tp->tt_rexmt) &&		    tp->snd_nxt != tp->snd_una) {			if (callout_active(tp->tt_persist)) {				callout_stop(tp->tt_persist);				tp->t_rxtshift = 0;			}			callout_reset(tp->tt_rexmt, tp->t_rxtcur,				      tcp_timer_rexmt, tp);		}	} else		if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))			tp->snd_max = tp->snd_nxt + len;#ifdef TCPDEBUG	/*	 * Trace.	 */	if (so->so_options & SO_DEBUG)		tcp_trace(TA_OUTPUT, tp->t_state, tp, mtod(m, void *), th, 0);#endif	/*	 * Fill in IP length and desired time to live and	 * send to IP level.  There should be a better way	 * to handle ttl and tos; we could keep them in	 * the template, but need a way to checksum without them.	 */	/*	 * m->m_pkthdr.len should have been set before cksum calcuration,	 * because in6_cksum() need it.	 */#ifdef INET6	if (isipv6) {		/*		 * we separately set hoplimit for every segment, since the		 * user might want to change the value via setsockopt.		 * Also, desired default hop limit might be changed via		 * Neighbor Discovery.		 */		ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb,					       tp->t_inpcb->in6p_route.ro_rt ?					       tp->t_inpcb->in6p_route.ro_rt->rt_ifp					       : NULL);		/* TODO: IPv6 IP6TOS_ECT bit on */#ifdef IPSEC		if (ipsec_setsocket(m, so) != 0) {			m_freem(m);			error = ENOBUFS;			goto out;		}#endif /*IPSEC*/		error = ip6_output(m,			    tp->t_inpcb->in6p_outputopts,			    &tp->t_inpcb->in6p_route,			    (so->so_options & SO_DONTROUTE), NULL, NULL);	} else#endif /* INET6 */    {	struct rtentry *rt;	ip->ip_len = m->m_pkthdr.len;#ifdef INET6 	if (INP_CHECK_SOCKAF(so, AF_INET6)) 		ip->ip_ttl = in6_selecthlim(tp->t_inpcb, 					    tp->t_inpcb->in6p_route.ro_rt ? 					    tp->t_inpcb->in6p_route.ro_rt->rt_ifp 					    : NULL); 	else#endif /* INET6 */	ip->ip_ttl = tp->t_inpcb->inp_ip_ttl;	/* XXX */	ip->ip_tos = tp->t_inpcb->inp_ip_tos;	/* XXX */	/*	 * See if we should do MTU discovery.  We do it only if the following	 * are true:	 *	1) we have a valid route to the destination	 *	2) the MTU is not locked (if it is, then discovery has been	 *	   disabled)	 */	if (path_mtu_discovery	    && (rt = tp->t_inpcb->inp_route.ro_rt)	    && rt->rt_flags & RTF_UP	    && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {		ip->ip_off |= IP_DF;	}#ifdef IPSEC 	ipsec_setsocket(m, so);#endif /*IPSEC*/	error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,	    (so->so_options & SO_DONTROUTE), 0);    }	if (error) {		/*		 * We know that the packet was lost, so back out the		 * sequence number advance, if any.		 */		if (tp->t_force == 0 || !callout_active(tp->tt_persist)) {			/*			 * No need to check for TH_FIN here because			 * the TF_SENTFIN flag handles that case.			 */			if ((flags & TH_SYN) == 0)				tp->snd_nxt -= len;		}out:		if (error == ENOBUFS) {	                if (!callout_active(tp->tt_rexmt) &&                            !callout_active(tp->tt_persist))	                        callout_reset(tp->tt_rexmt, tp->t_rxtcur,                                      tcp_timer_rexmt, tp);			tcp_quench(tp->t_inpcb, 0);			return (0);		}		if (error == EMSGSIZE) {			/*			 * ip_output() will have already fixed the route			 * for us.  tcp_mtudisc() will, as its last action,			 * initiate retransmission, so it is important to			 * not do so here.			 */			tcp_mtudisc(tp->t_inpcb, 0);			return 0;		}		if ((error == EHOSTUNREACH || error == ENETDOWN)		    && TCPS_HAVERCVDSYN(tp->t_state)) {			tp->t_softerror = error;			return (0);		}		return (error);	}	tcpstat.tcps_sndtotal++;	/*	 * Data sent (as far as we can tell).	 * If this advertises a larger window than any other segment,	 * then remember the size of the advertised window.	 * Any pending ACK has now been sent.	 */	if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))		tp->rcv_adv = tp->rcv_nxt + win;	tp->last_ack_sent = tp->rcv_nxt;	tp->t_flags &= ~TF_ACKNOW;	if (tcp_delack_enabled)		callout_stop(tp->tt_delack);	if (sendalot && (!tcp_do_newreno || --maxburst))		goto again;	return (0);}voidtcp_setpersist(tp)	register struct tcpcb *tp;{	int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;	int tt;	if (callout_active(tp->tt_rexmt))		panic("tcp_setpersist: retransmit pending");	/*	 * Start/restart persistance timer.	 */	TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift],		      TCPTV_PERSMIN, TCPTV_PERSMAX);	callout_reset(tp->tt_persist, tt, tcp_timer_persist, tp);	if (tp->t_rxtshift < tcp_rexmit_shift_max)		tp->t_rxtshift++;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?