tcp_subr.c

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

C
1,397
字号
	if (cmd == PRC_QUENCH)		notify = tcp_quench;	else if (icmp_may_rst && (cmd == PRC_UNREACH_ADMIN_PROHIB ||		cmd == PRC_UNREACH_PORT) && ip)		notify = tcp_drop_syn_sent;	else if (cmd == PRC_MSGSIZE)		notify = tcp_mtudisc;	else if (PRC_IS_REDIRECT(cmd)) {		ip = 0;		notify = in_rtchange;	} else if (cmd == PRC_HOSTDEAD)		ip = 0;	else if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)		return;	if (ip) {		s = splnet();		th = (struct tcphdr *)((caddr_t)ip 				       + (IP_VHL_HL(ip->ip_vhl) << 2));		inp = in_pcblookup_hash(&tcbinfo, faddr, th->th_dport,		    ip->ip_src, th->th_sport, 0, NULL);		if (inp != NULL && inp->inp_socket != NULL) {			icmp_seq = htonl(th->th_seq);			tp = intotcpcb(inp);			if (SEQ_GEQ(icmp_seq, tp->snd_una) &&			    SEQ_LT(icmp_seq, tp->snd_max))				(*notify)(inp, inetctlerrmap[cmd]);		}		splx(s);	} else		in_pcbnotifyall(&tcb, faddr, inetctlerrmap[cmd], notify);}#ifdef INET6voidtcp6_ctlinput(cmd, sa, d)	int cmd;	struct sockaddr *sa;	void *d;{	struct tcphdr th;	void (*notify) __P((struct inpcb *, int)) = tcp_notify;	struct ip6_hdr *ip6;	struct mbuf *m;	struct ip6ctlparam *ip6cp = NULL;	const struct sockaddr_in6 *sa6_src = NULL;	int off;	struct tcp_portonly {		u_int16_t th_sport;		u_int16_t th_dport;	} *thp;	if (sa->sa_family != AF_INET6 ||	    sa->sa_len != sizeof(struct sockaddr_in6))		return;	if (cmd == PRC_QUENCH)		notify = tcp_quench;	else if (cmd == PRC_MSGSIZE)		notify = tcp_mtudisc;	else if (!PRC_IS_REDIRECT(cmd) &&		 ((unsigned)cmd > PRC_NCMDS || inet6ctlerrmap[cmd] == 0))		return;	/* if the parameter is from icmp6, decode it. */	if (d != NULL) {		ip6cp = (struct ip6ctlparam *)d;		m = ip6cp->ip6c_m;		ip6 = ip6cp->ip6c_ip6;		off = ip6cp->ip6c_off;		sa6_src = ip6cp->ip6c_src;	} else {		m = NULL;		ip6 = NULL;		off = 0;	/* fool gcc */		sa6_src = &sa6_any;	}	if (ip6) {		/*		 * XXX: We assume that when IPV6 is non NULL,		 * M and OFF are valid.		 */		/* check if we can safely examine src and dst ports */		if (m->m_pkthdr.len < off + sizeof(*thp))			return;		bzero(&th, sizeof(th));		m_copydata(m, off, sizeof(*thp), (caddr_t)&th);		in6_pcbnotify(&tcb, sa, th.th_dport,		    (struct sockaddr *)ip6cp->ip6c_src,		    th.th_sport, cmd, NULL, notify);	} else		in6_pcbnotify(&tcb, sa, 0, (struct sockaddr *)sa6_src,			      0, cmd, NULL, notify);}#endif /* INET6 *//* * Following is where TCP initial sequence number generation occurs. * * There are two places where we must use initial sequence numbers: * 1.  In SYN-ACK packets. * 2.  In SYN packets. * * The ISNs in SYN-ACK packets have no monotonicity requirement,  * and should be as unpredictable as possible to avoid the possibility * of spoofing and/or connection hijacking.  To satisfy this * requirement, SYN-ACK ISNs are generated via the arc4random() * function.  If exact RFC 1948 compliance is requested via sysctl, * these ISNs will be generated just like those in SYN packets. * * The ISNs in SYN packets must be monotonic; TIME_WAIT recycling * depends on this property.  In addition, these ISNs should be * unguessable so as to prevent connection hijacking.  To satisfy * the requirements of this situation, the algorithm outlined in * RFC 1948 is used to generate sequence numbers. * * For more information on the theory of operation, please see * RFC 1948. * * Implementation details: * * Time is based off the system timer, and is corrected so that it * increases by one megabyte per second.  This allows for proper * recycling on high speed LANs while still leaving over an hour * before rollover. * * Two sysctls control the generation of ISNs: * * net.inet.tcp.isn_reseed_interval controls the number of seconds * between seeding of isn_secret.  This is normally set to zero, * as reseeding should not be necessary. * * net.inet.tcp.strict_rfc1948 controls whether RFC 1948 is followed * strictly.  When strict compliance is requested, reseeding is * disabled and SYN-ACKs will be generated in the same manner as * SYNs.  Strict mode is disabled by default. * */#define ISN_BYTES_PER_SECOND 1048576u_char isn_secret[32];int isn_last_reseed;MD5_CTX isn_ctx;tcp_seqtcp_new_isn(tp)	struct tcpcb *tp;{	u_int32_t md5_buffer[4];	tcp_seq new_isn;	/* Use arc4random for SYN-ACKs when not in exact RFC1948 mode. */	if (((tp->t_state == TCPS_LISTEN) || (tp->t_state == TCPS_TIME_WAIT))	   && tcp_strict_rfc1948 == 0)		return arc4random();	/* Seed if this is the first use, reseed if requested. */	if ((isn_last_reseed == 0) ||	    ((tcp_strict_rfc1948 == 0) && (tcp_isn_reseed_interval > 0) &&	     (((u_int)isn_last_reseed + (u_int)tcp_isn_reseed_interval*hz)		< (u_int)ticks))) {		read_random_unlimited(&isn_secret, sizeof(isn_secret));		isn_last_reseed = ticks;	}			/* Compute the md5 hash and return the ISN. */	MD5Init(&isn_ctx);	MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_fport, sizeof(u_short));	MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_lport, sizeof(u_short));#ifdef INET6	if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) {		MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_faddr,			  sizeof(struct in6_addr));		MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_laddr,			  sizeof(struct in6_addr));	} else#endif	{		MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_faddr,			  sizeof(struct in_addr));		MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_laddr,			  sizeof(struct in_addr));	}	MD5Update(&isn_ctx, (u_char *) &isn_secret, sizeof(isn_secret));	MD5Final((u_char *) &md5_buffer, &isn_ctx);	new_isn = (tcp_seq) md5_buffer[0];	new_isn += ticks * (ISN_BYTES_PER_SECOND / hz);	return new_isn;}/* * When a source quench is received, close congestion window * to one segment.  We will gradually open it again as we proceed. */voidtcp_quench(inp, _errno)	struct inpcb *inp;	int _errno;{	struct tcpcb *tp = intotcpcb(inp);	if (tp)		tp->snd_cwnd = tp->t_maxseg;}/* * When a specific ICMP unreachable message is received and the * connection state is SYN-SENT, drop the connection.  This behavior * is controlled by the icmp_may_rst sysctl. */voidtcp_drop_syn_sent(inp, _errno)	struct inpcb *inp;	int _errno;{	struct tcpcb *tp = intotcpcb(inp);	if (tp && tp->t_state == TCPS_SYN_SENT)		tcp_drop(tp, _errno);}/* * When `need fragmentation' ICMP is received, update our idea of the MSS * based on the new value in the route.  Also nudge TCP to send something, * since we know the packet we just sent was dropped. * This duplicates some code in the tcp_mss() function in tcp_input.c. */voidtcp_mtudisc(inp, _errno)	struct inpcb *inp;	int _errno;{	struct tcpcb *tp = intotcpcb(inp);	struct rtentry *rt;	struct rmxp_tao *taop;	struct socket *so = inp->inp_socket;	int offered;	int mss;#ifdef INET6	int isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0;#endif /* INET6 */	if (tp) {#ifdef INET6		if (isipv6)			rt = tcp_rtlookup6(inp);		else#endif /* INET6 */		rt = tcp_rtlookup(inp);		if (!rt || !rt->rt_rmx.rmx_mtu) {			tp->t_maxopd = tp->t_maxseg =#ifdef INET6				isipv6 ? tcp_v6mssdflt :#endif /* INET6 */				tcp_mssdflt;			return;		}		taop = rmx_taop(rt->rt_rmx);		offered = taop->tao_mssopt;		mss = rt->rt_rmx.rmx_mtu -#ifdef INET6			(isipv6 ?			 sizeof(struct ip6_hdr) + sizeof(struct tcphdr) :#endif /* INET6 */			 sizeof(struct tcpiphdr)#ifdef INET6			 )#endif /* INET6 */			;		if (offered)			mss = min(mss, offered);		/*		 * XXX - The above conditional probably violates the TCP		 * spec.  The problem is that, since we don't know the		 * other end's MSS, we are supposed to use a conservative		 * default.  But, if we do that, then MTU discovery will		 * never actually take place, because the conservative		 * default is much less than the MTUs typically seen		 * on the Internet today.  For the moment, we'll sweep		 * this under the carpet.		 *		 * The conservative default might not actually be a problem		 * if the only case this occurs is when sending an initial		 * SYN with options and data to a host we've never talked		 * to before.  Then, they will reply with an MSS value which		 * will get recorded and the new parameters should get		 * recomputed.  For Further Study.		 */		if (tp->t_maxopd <= mss)			return;		tp->t_maxopd = mss;		if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&		    (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)			mss -= TCPOLEN_TSTAMP_APPA;		if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC &&		    (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC)			mss -= TCPOLEN_CC_APPA;#if	(MCLBYTES & (MCLBYTES - 1)) == 0		if (mss > MCLBYTES)			mss &= ~(MCLBYTES-1);#else		if (mss > MCLBYTES)			mss = mss / MCLBYTES * MCLBYTES;#endif		if (so->so_snd.sb_hiwat < mss)			mss = so->so_snd.sb_hiwat;		tp->t_maxseg = mss;		tcpstat.tcps_mturesent++;		tp->t_rtttime = 0;		tp->snd_nxt = tp->snd_una;		tcp_output(tp);	}}/* * Look-up the routing entry to the peer of this inpcb.  If no route * is found and it cannot be allocated the return NULL.  This routine * is called by TCP routines that access the rmx structure and by tcp_mss * to get the interface MTU. */struct rtentry *tcp_rtlookup(inp)	struct inpcb *inp;{	struct route *ro;	struct rtentry *rt;	ro = &inp->inp_route;	rt = ro->ro_rt;	if (rt == NULL || !(rt->rt_flags & RTF_UP)) {		/* No route yet, so try to acquire one */		if (inp->inp_faddr.s_addr != INADDR_ANY) {			ro->ro_dst.sa_family = AF_INET;			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =				inp->inp_faddr;			rtalloc(ro);			rt = ro->ro_rt;		}	}	return rt;}#ifdef INET6struct rtentry *tcp_rtlookup6(inp)	struct inpcb *inp;{#ifdef NEW_STRUCT_ROUTE	struct route *ro6;#else	struct route_in6 *ro6;#endif	struct rtentry *rt;	ro6 = &inp->in6p_route;	rt = ro6->ro_rt;	if (rt == NULL || !(rt->rt_flags & RTF_UP)) {		/* No route yet, so try to acquire one */		if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {			struct sockaddr_in6 *dst6;			dst6 = (struct sockaddr_in6 *)&ro6->ro_dst;			dst6->sin6_family = AF_INET6;			dst6->sin6_len = sizeof(*dst6);			dst6->sin6_addr = inp->in6p_faddr;			rtalloc((struct route *)ro6);			rt = ro6->ro_rt;		}	}	return rt;}#endif /* INET6 */#ifdef IPSEC/* compute ESP/AH header size for TCP, including outer IP header. */size_tipsec_hdrsiz_tcp(tp)	struct tcpcb *tp;{	struct inpcb *inp;	struct mbuf *m;	size_t hdrsiz;	struct ip *ip;#ifdef INET6	struct ip6_hdr *ip6;#endif /* INET6 */	struct tcphdr *th;	if ((tp == NULL) || ((inp = tp->t_inpcb) == NULL))		return 0;	MGETHDR(m, M_DONTWAIT, MT_DATA);	if (!m)		return 0;#ifdef INET6	if ((inp->inp_vflag & INP_IPV6) != 0) {		ip6 = mtod(m, struct ip6_hdr *);		th = (struct tcphdr *)(ip6 + 1);		m->m_pkthdr.len = m->m_len =			sizeof(struct ip6_hdr) + sizeof(struct tcphdr);		tcp_fillheaders(tp, ip6, th);		hdrsiz = ipsec6_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);	} else#endif /* INET6 */      {	ip = mtod(m, struct ip *);	th = (struct tcphdr *)(ip + 1);	m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr);	tcp_fillheaders(tp, ip, th);	hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);      }	m_free(m);	return hdrsiz;}#endif /*IPSEC*//* * Return a pointer to the cached information about the remote host. * The cached information is stored in the protocol specific part of * the route metrics. */struct rmxp_tao *tcp_gettaocache(inp)	struct inpcb *inp;{	struct rtentry *rt;#ifdef INET6	if ((inp->inp_vflag & INP_IPV6) != 0)		rt = tcp_rtlookup6(inp);	else#endif /* INET6 */	rt = tcp_rtlookup(inp);	/* Make sure this is a host route and is up. */	if (rt == NULL ||	    (rt->rt_flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST))		return NULL;	return rmx_taop(rt->rt_rmx);}/* * Clear all the TAO cache entries, called from tcp_init. * * XXX * This routine is just an empty one, because we assume that the routing * routing tables are initialized at the same time when TCP, so there is * nothing in the cache left over. */static voidtcp_cleartaocache(){}

⌨️ 快捷键说明

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