📄 tcp_input.c
字号:
//==========================================================================
//
// 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/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;
static int log_in_vain = 0;
static int blackhole = 0;
int tcp_delack_enabled = 1;
int tcp_lq_overflow = 1;
#ifdef TCP_DROP_SYNFIN
static int drop_synfin = 0;
#endif
struct 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 int
tcp_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 INET6
int
tcp6_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;
}
#endif
void
tcp_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
}
/*
* Check that TCP offset makes sense,
* pull out TCP options and adjust length. XXX
*/
off = th->th_off << 2;
if (off < sizeof (struct tcphdr) || off > tlen) {
tcpstat.tcps_rcvbadoff++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -