📄 tcp_input.c
字号:
//==========================================================================
//
// sys/netinet/tcp_input.c
//
//
//
//==========================================================================
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD or other sources,
// and are covered by the appropriate copyright disclaimers included herein.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-01-10
// Purpose:
// Description:
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
/* $OpenBSD: tcp_input.c,v 1.54 1999/12/15 16:37:20 provos Exp $ */
/* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
* 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.5 (Berkeley) 4/10/94
*/
/*
%%% portions-copyright-nrl-95
Portions of this software are Copyright 1995-1998 by Randall Atkinson,
Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
Reserved. All rights under this copyright have been assigned to the US
Naval Research Laboratory (NRL). The NRL Copyright Notice and License
Agreement Version 1.1 (January 17, 1995) applies to these portions of the
software.
You should have received a copy of the license with this software. If you
didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
*/
#ifndef TUBA_INCLUDE
#include <sys/param.h>
#ifndef __ECOS
#include <sys/systm.h>
#endif
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_debug.h>
#ifndef __ECOS
#include <dev/rndvar.h>
#endif
#include <machine/stdarg.h>
#ifndef __ECOS
#include <sys/md5k.h>
#endif
#ifdef IPSEC
#include <netinet/ip_ipsp.h>
#endif /* IPSEC */
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
#endif
#include <sys/domain.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/tcpipv6.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#ifndef CREATE_IPV6_MAPPED
#define CREATE_IPV6_MAPPED(a6, a4) \
do { \
bzero(&(a6), sizeof(a6)); \
(a6).s6_addr[10] = (a6).s6_addr[11] = 0xff; \
*(u_int32_t *)&(a6).s6_addr[12] = (a4); \
} while (0)
#endif
struct tcpiphdr tcp_saveti;
struct tcpipv6hdr tcp_saveti6;
/* for the packet header length in the mbuf */
#define M_PH_LEN(m) (((struct mbuf *)(m))->m_pkthdr.len)
#define M_V6_LEN(m) (M_PH_LEN(m) - sizeof(struct ip6_hdr))
#define M_V4_LEN(m) (M_PH_LEN(m) - sizeof(struct ip))
#endif /* INET6 */
int tcprexmtthresh = 3;
struct tcpiphdr tcp_saveti;
int tcptv_keep_init = TCPTV_KEEP_INIT;
extern u_long sb_max;
#endif /* TUBA_INCLUDE */
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
/* for modulo comparisons of timestamps */
#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
/*
* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint.
*/
#ifdef INET6
#define ND6_HINT(tp) \
do { \
if (tp && tp->t_inpcb && (tp->t_inpcb->inp_flags & INP_IPV6) \
&& !(tp->t_inpcb->inp_flags & INP_IPV6_MAPPED) \
&& tp->t_inpcb->inp_route6.ro_rt) { \
nd6_nud_hint(tp->t_inpcb->inp_route6.ro_rt, NULL); \
} \
} while (0)
#else
#define ND6_HINT(tp)
#endif
/*
* Insert segment ti into reassembly queue of tcp with
* control block tp. Return TH_FIN if reassembly now includes
* a segment with FIN. The macro form does the common case inline
* (segment is the next to be received on an established connection,
* and the queue is empty), avoiding linkage into and removal
* from the queue and repetition of various conversions.
* Set DELACK for segments received in order, but ack immediately
* when segments are out of order (so fast retransmit can work).
*/
#ifndef TUBA_INCLUDE
int
tcp_reass(tp, th, m, tlen)
register struct tcpcb *tp;
register struct tcphdr *th;
struct mbuf *m;
int *tlen;
{
register struct ipqent *p, *q, *nq, *tiqe;
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, before we throw away any data.
* If we can't, just drop the packet. XXX
*/
MALLOC(tiqe, struct ipqent *, sizeof (struct ipqent), M_IPQ, M_NOWAIT);
if (tiqe == NULL) {
tcpstat.tcps_rcvmemdrop++;
m_freem(m);
return (0);
}
/*
* Find a segment which begins after this one does.
*/
for (p = NULL, q = tp->segq.lh_first; q != NULL;
p = q, q = q->ipqe_q.le_next)
if (SEQ_GT(q->ipqe_tcp->th_seq, th->th_seq))
break;
/*
* 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 struct tcphdr *phdr = p->ipqe_tcp;
register int i;
/* conversion to int (in i) handles seq wraparound */
i = phdr->th_seq + phdr->th_reseqlen - th->th_seq;
if (i > 0) {
if (i >= *tlen) {
tcpstat.tcps_rcvduppack++;
tcpstat.tcps_rcvdupbyte += *tlen;
m_freem(m);
FREE(tiqe, M_IPQ);
return (0);
}
m_adj(m, i);
*tlen -= i;
th->th_seq += i;
}
}
tcpstat.tcps_rcvoopack++;
tcpstat.tcps_rcvoobyte += *tlen;
/*
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
*/
for (; q != NULL; q = nq) {
register struct tcphdr *qhdr = q->ipqe_tcp;
register int i = (th->th_seq + *tlen) - qhdr->th_seq;
if (i <= 0)
break;
if (i < qhdr->th_reseqlen) {
qhdr->th_seq += i;
qhdr->th_reseqlen -= i;
m_adj(q->ipqe_m, i);
break;
}
nq = q->ipqe_q.le_next;
m_freem(q->ipqe_m);
LIST_REMOVE(q, ipqe_q);
FREE(q, M_IPQ);
}
/* Insert the new fragment queue entry into place. */
tiqe->ipqe_m = m;
th->th_reseqlen = *tlen;
tiqe->ipqe_tcp = th;
if (p == NULL) {
LIST_INSERT_HEAD(&tp->segq, tiqe, ipqe_q);
} else {
LIST_INSERT_AFTER(p, tiqe, ipqe_q);
}
present:
/*
* Present data to user, advancing rcv_nxt through
* completed sequence space.
*/
if (TCPS_HAVEESTABLISHED(tp->t_state) == 0)
return (0);
q = tp->segq.lh_first;
if (q == NULL || q->ipqe_tcp->th_seq != tp->rcv_nxt)
return (0);
if (tp->t_state == TCPS_SYN_RECEIVED && q->ipqe_tcp->th_reseqlen)
return (0);
do {
tp->rcv_nxt += q->ipqe_tcp->th_reseqlen;
flags = q->ipqe_tcp->th_flags & TH_FIN;
nq = q->ipqe_q.le_next;
LIST_REMOVE(q, ipqe_q);
ND6_HINT(tp);
if (so->so_state & SS_CANTRCVMORE)
m_freem(q->ipqe_m);
else
sbappend(&so->so_rcv, q->ipqe_m);
FREE(q, M_IPQ);
q = nq;
} while (q != NULL && q->ipqe_tcp->th_seq == tp->rcv_nxt);
sorwakeup(so);
return (flags);
}
/*
* First check for a port-specific bomb. We do not want to drop half-opens
* for other ports if this is the only port being bombed. We only check
* the bottom 40 half open connections, to avoid wasting too much time.
*
* Or, otherwise it is more likely a generic syn bomb, so delete the oldest
* half-open connection.
*/
void
tcpdropoldhalfopen(avoidtp, port)
struct tcpcb *avoidtp;
u_int16_t port;
{
register struct inpcb *inp;
register struct tcpcb *tp;
int ncheck = 40;
int s;
s = splnet();
inp = tcbtable.inpt_queue.cqh_first;
if (inp) /* XXX */
for (; inp != (struct inpcb *)&tcbtable.inpt_queue && --ncheck;
inp = inp->inp_queue.cqe_prev) {
if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
tp != avoidtp &&
tp->t_state == TCPS_SYN_RECEIVED &&
port == inp->inp_lport) {
tcp_close(tp);
goto done;
}
}
inp = tcbtable.inpt_queue.cqh_first;
if (inp) /* XXX */
for (; inp != (struct inpcb *)&tcbtable.inpt_queue;
inp = inp->inp_queue.cqe_prev) {
if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
tp != avoidtp &&
tp->t_state == TCPS_SYN_RECEIVED) {
tcp_close(tp);
goto done;
}
}
done:
splx(s);
}
#if defined(INET6) && !defined(TCP6)
int
tcp6_input(mp, offp, proto)
struct mbuf **mp;
int *offp, proto;
{
struct mbuf *m = *mp;
#if defined(NFAITH) && 0 < NFAITH
if (m->m_pkthdr.rcvif) {
if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
/* XXX send icmp6 host/port unreach? */
m_freem(m);
return IPPROTO_DONE;
}
}
#endif
/*
* draft-itojun-ipv6-tcp-to-anycast
* better place to put this in?
*/
if (m->m_flags & M_ANYCAST6) {
if (m->m_len >= sizeof(struct ip6_hdr)) {
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR,
(caddr_t)&ip6->ip6_dst - (caddr_t)ip6);
} else
m_freem(m);
return IPPROTO_DONE;
}
tcp_input(m, *offp, proto);
return IPPROTO_DONE;
}
#endif
/*
* TCP input routine, follows pages 65-76 of the
* protocol specification dated September, 1981 very closely.
*/
void
#if __STDC__
tcp_input(struct mbuf *m, ...)
#else
tcp_input(m, va_alist)
register struct mbuf *m;
#endif
{
register struct tcpiphdr *ti;
register struct inpcb *inp;
caddr_t optp = NULL;
int optlen = 0;
int len, tlen, off;
register struct tcpcb *tp = 0;
register int tiflags;
struct socket *so = NULL;
int todrop, acked, ourfinisacked, needoutput = 0;
int hdroptlen = 0;
short ostate = 0;
struct in_addr laddr;
int dropsocket = 0;
int iss = 0;
u_long tiwin;
u_int32_t ts_val, ts_ecr;
int ts_present = 0;
int iphlen;
va_list ap;
register struct tcphdr *th;
#ifdef IPSEC
struct tdb *tdb = NULL;
#endif /* IPSEC */
#ifdef INET6
struct in6_addr laddr6;
unsigned short is_ipv6; /* Type of incoming datagram. */
struct ip6_hdr *ipv6 = NULL;
#endif /* INET6 */
va_start(ap, m);
iphlen = va_arg(ap, int);
va_end(ap);
tcpstat.tcps_rcvtotal++;
#ifdef IPSEC
/* Save the last SA which was used to process the mbuf */
if ((m->m_flags & (M_CONF|M_AUTH)) && m->m_pkthdr.tdbi) {
struct tdb_ident *tdbi = m->m_pkthdr.tdbi;
/* XXX gettdb() should really be called at spltdb(). */
/* XXX this is splsoftnet(), currently they are the same. */
tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
free(m->m_pkthdr.tdbi, M_TEMP);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -