tcp_input.c

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

C
2,238
字号
//==========================================================================////      src/sys/netinet/tcp_input.c////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD, // FreeBSD or other sources, and are covered by the appropriate// copyright disclaimers included herein.//// Portions created by Red Hat are// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================/* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 *	The Regents of the University of California.  All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	@(#)tcp_input.c	8.12 (Berkeley) 5/24/95 * $FreeBSD: src/sys/netinet/tcp_input.c,v 1.107.2.16 2001/08/22 00:59:12 silby Exp $ */#include <sys/param.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/sysctl.h>#include <sys/socketvar.h>#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#include <netinet/ip_icmp.h>	/* for ICMP_BANDLIM		*/#include <netinet/in_var.h>#include <netinet/icmp_var.h>	/* for ICMP_BANDLIM		*/#include <netinet/in_pcb.h>#include <netinet/ip_var.h>#ifdef INET6#include <netinet/ip6.h>#include <netinet/icmp6.h>#include <netinet6/nd6.h>#include <netinet6/ip6_var.h>#include <netinet6/in6_pcb.h>#endif#include <netinet/tcp.h>#include <netinet/tcp_fsm.h>#include <netinet/tcp_seq.h>#include <netinet/tcp_timer.h>#include <netinet/tcp_var.h>#ifdef INET6#include <netinet6/tcp6_var.h>#endif#include <netinet/tcpip.h>#ifdef TCPDEBUG#include <netinet/tcp_debug.h>u_char tcp_saveipgen[40]; /* the size must be of max ip header, now IPv6 */struct tcphdr tcp_savetcp;#endif /* TCPDEBUG */#ifdef IPSEC#include <netinet6/ipsec.h>#include <netkey/key.h>#endif /*IPSEC*/static int	tcprexmtthresh = 3;tcp_cc	tcp_ccgen;struct	tcpstat tcpstat;SYSCTL_STRUCT(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RD,     &tcpstat , tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)");static int log_in_vain = 0;SYSCTL_INT(_net_inet_tcp, OID_AUTO, log_in_vain, CTLFLAG_RW,     &log_in_vain, 0, "Log all incoming TCP connections");static int blackhole = 0;SYSCTL_INT(_net_inet_tcp, OID_AUTO, blackhole, CTLFLAG_RW,	&blackhole, 0, "Do not send RST when dropping refused connections");int tcp_delack_enabled = 1;SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW,     &tcp_delack_enabled, 0,     "Delay ACK to try and piggyback it onto a data packet");int tcp_lq_overflow = 1;SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_lq_overflow, CTLFLAG_RW,    &tcp_lq_overflow, 0,     "Listen Queue Overflow");#ifdef TCP_DROP_SYNFINstatic int drop_synfin = 0;SYSCTL_INT(_net_inet_tcp, OID_AUTO, drop_synfin, CTLFLAG_RW,    &drop_synfin, 0, "Drop TCP packets with SYN+FIN set");#endifstruct inpcbhead tcb;#define	tcb6	tcb  /* for KAME src sync over BSD*'s */struct inpcbinfo tcbinfo;static void	 tcp_dooptions __P((struct tcpcb *,	    u_char *, int, struct tcphdr *, struct tcpopt *));static void	 tcp_pulloutofband __P((struct socket *,	    struct tcphdr *, struct mbuf *, int));static int	 tcp_reass __P((struct tcpcb *, struct tcphdr *, int *,				struct mbuf *));static void	 tcp_xmit_timer __P((struct tcpcb *, int));static int	 tcp_newreno __P((struct tcpcb *, struct tcphdr *));/* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */#ifdef INET6#define ND6_HINT(tp) \do { \	if ((tp) && (tp)->t_inpcb && \	    ((tp)->t_inpcb->inp_vflag & INP_IPV6) != 0 && \	    (tp)->t_inpcb->in6p_route.ro_rt) \		nd6_nud_hint((tp)->t_inpcb->in6p_route.ro_rt, NULL, 0); \} while (0)#else#define ND6_HINT(tp)#endif/* * Indicate whether this ack should be delayed. */#define DELAY_ACK(tp) \	(tcp_delack_enabled && !callout_pending(tp->tt_delack))static inttcp_reass(tp, th, tlenp, m)	register struct tcpcb *tp;	register struct tcphdr *th;	int *tlenp;	struct mbuf *m;{	struct tseg_qent *q;	struct tseg_qent *p = NULL;	struct tseg_qent *nq;	struct tseg_qent *te;	struct socket *so = tp->t_inpcb->inp_socket;	int flags;	/*	 * Call with th==0 after become established to	 * force pre-ESTABLISHED data up to user socket.	 */	if (th == 0)		goto present;	/* Allocate a new queue entry. If we can't, just drop the pkt. XXX */	MALLOC(te, struct tseg_qent *, sizeof (struct tseg_qent), M_TSEGQ,	       M_NOWAIT);	if (te == NULL) {		tcpstat.tcps_rcvmemdrop++;		m_freem(m);		return (0);	}	/*	 * Find a segment which begins after this one does.	 */	LIST_FOREACH(q, &tp->t_segq, tqe_q) {		if (SEQ_GT(q->tqe_th->th_seq, th->th_seq))			break;		p = q;	}	/*	 * If there is a preceding segment, it may provide some of	 * our data already.  If so, drop the data from the incoming	 * segment.  If it provides all of our data, drop us.	 */	if (p != NULL) {		register int i;		/* conversion to int (in i) handles seq wraparound */		i = p->tqe_th->th_seq + p->tqe_len - th->th_seq;		if (i > 0) {			if (i >= *tlenp) {				tcpstat.tcps_rcvduppack++;				tcpstat.tcps_rcvdupbyte += *tlenp;				m_freem(m);				FREE(te, M_TSEGQ);				/*				 * Try to present any queued data				 * at the left window edge to the user.				 * This is needed after the 3-WHS				 * completes.				 */				goto present;	/* ??? */			}			m_adj(m, i);			*tlenp -= i;			th->th_seq += i;		}	}	tcpstat.tcps_rcvoopack++;	tcpstat.tcps_rcvoobyte += *tlenp;	/*	 * While we overlap succeeding segments trim them or,	 * if they are completely covered, dequeue them.	 */	while (q) {		register int i = (th->th_seq + *tlenp) - q->tqe_th->th_seq;		if (i <= 0)			break;		if (i < q->tqe_len) {			q->tqe_th->th_seq += i;			q->tqe_len -= i;			m_adj(q->tqe_m, i);			break;		}		nq = LIST_NEXT(q, tqe_q);		LIST_REMOVE(q, tqe_q);		m_freem(q->tqe_m);		FREE(q, M_TSEGQ);		q = nq;	}	/* Insert the new segment queue entry into place. */	te->tqe_m = m;	te->tqe_th = th;	te->tqe_len = *tlenp;	if (p == NULL) {		LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q);	} else {		LIST_INSERT_AFTER(p, te, tqe_q);	}present:	/*	 * Present data to user, advancing rcv_nxt through	 * completed sequence space.	 */	if (!TCPS_HAVEESTABLISHED(tp->t_state))		return (0);	q = LIST_FIRST(&tp->t_segq);	if (!q || q->tqe_th->th_seq != tp->rcv_nxt)		return (0);	do {		tp->rcv_nxt += q->tqe_len;		flags = q->tqe_th->th_flags & TH_FIN;		nq = LIST_NEXT(q, tqe_q);		LIST_REMOVE(q, tqe_q);		if (so->so_state & SS_CANTRCVMORE)			m_freem(q->tqe_m);		else			sbappend(&so->so_rcv, q->tqe_m);		FREE(q, M_TSEGQ);		q = nq;	} while (q && q->tqe_th->th_seq == tp->rcv_nxt);	ND6_HINT(tp);	sorwakeup(so);	return (flags);}/* * TCP input routine, follows pages 65-76 of the * protocol specification dated September, 1981 very closely. */#ifdef INET6inttcp6_input(mp, offp, proto)	struct mbuf **mp;	int *offp, proto;{	register struct mbuf *m = *mp;	struct in6_ifaddr *ia6;	IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE);	/*	 * draft-itojun-ipv6-tcp-to-anycast	 * better place to put this in?	 */	ia6 = ip6_getdstifaddr(m);	if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) {				struct ip6_hdr *ip6;		ip6 = mtod(m, struct ip6_hdr *);		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR,			    (caddr_t)&ip6->ip6_dst - (caddr_t)ip6);		return IPPROTO_DONE;	}	tcp_input(m, *offp);	return IPPROTO_DONE;}#endifvoidtcp_input(m, off0)	register struct mbuf *m;	int off0;{	register struct tcphdr *th;	register struct ip *ip = NULL;	register struct ipovly *ipov;	register struct inpcb *inp;	u_char *optp = NULL;	int optlen = 0;	int len, tlen, off;	int drop_hdrlen;	register struct tcpcb *tp = 0;	register int thflags;	struct socket *so = 0;	int todrop, acked, ourfinisacked, needoutput = 0;	struct in_addr laddr;#ifdef INET6	struct in6_addr laddr6;#endif	int dropsocket = 0;	int iss = 0;	u_long tiwin;	struct tcpopt to;		/* options in this segment */	struct rmxp_tao *taop;		/* pointer to our TAO cache entry */	struct rmxp_tao	tao_noncached;	/* in case there's no cached entry */#ifdef TCPDEBUG	short ostate = 0;#endif#ifdef INET6	struct ip6_hdr *ip6 = NULL;	int isipv6;#endif /* INET6 */	int rstreason; /* For badport_bandlim accounting purposes */#ifdef INET6	isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;#endif	bzero((char *)&to, sizeof(to));	tcpstat.tcps_rcvtotal++;#ifdef INET6	if (isipv6) {		/* IP6_EXTHDR_CHECK() is already done at tcp6_input() */		ip6 = mtod(m, struct ip6_hdr *);		tlen = sizeof(*ip6) + ntohs(ip6->ip6_plen) - off0;		if (in6_cksum(m, IPPROTO_TCP, off0, tlen)) {			tcpstat.tcps_rcvbadsum++;			goto drop;		}		th = (struct tcphdr *)((caddr_t)ip6 + off0);		/*		 * Be proactive about unspecified IPv6 address in source.		 * As we use all-zero to indicate unbounded/unconnected pcb,		 * unspecified IPv6 address can be used to confuse us.		 *		 * Note that packets with unspecified IPv6 destination is		 * already dropped in ip6_input.		 */		if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {			/* XXX stat */			goto drop;		}	} else#endif /* INET6 */      {	/*	 * Get IP and TCP header together in first mbuf.	 * Note: IP leaves IP header in first mbuf.	 */	if (off0 > sizeof (struct ip)) {		ip_stripoptions(m, (struct mbuf *)0);		off0 = sizeof(struct ip);	}	if (m->m_len < sizeof (struct tcpiphdr)) {		if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) {			tcpstat.tcps_rcvshort++;			return;		}	}	ip = mtod(m, struct ip *);	ipov = (struct ipovly *)ip;	th = (struct tcphdr *)((caddr_t)ip + off0);	tlen = ip->ip_len;	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {		if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)                	th->th_sum = m->m_pkthdr.csum_data;		else	                th->th_sum = in_pseudo(ip->ip_src.s_addr,			    ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data +			    ip->ip_len + IPPROTO_TCP));		th->th_sum ^= 0xffff;	} else {		/*		 * Checksum extended TCP header and data.		 */		len = sizeof (struct ip) + tlen;		bzero(ipov->ih_x1, sizeof(ipov->ih_x1));		ipov->ih_len = (u_short)tlen;		HTONS(ipov->ih_len);		th->th_sum = in_cksum(m, len);	}	if (th->th_sum) {		tcpstat.tcps_rcvbadsum++;		goto drop;	}#ifdef INET6	/* Re-initialization for later version check */	ip->ip_v = IPVERSION;#endif      }

⌨️ 快捷键说明

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