📄 tcp_input.c
字号:
m->m_pkthdr.tdbi = NULL;
}
#endif /* IPSEC */
#ifdef INET6
/*
* Before we do ANYTHING, we have to figure out if it's TCP/IPv6 or
* TCP/IPv4.
*/
is_ipv6 = mtod(m, struct ip *)->ip_v == 6;
#endif /* INET6 */
/*
* Get IP and TCP header together in first mbuf.
* Note: IP leaves IP header in first mbuf.
*/
#ifndef INET6
ti = mtod(m, struct tcpiphdr *);
#else /* INET6 */
if (!is_ipv6)
#endif /* INET6 */
if (iphlen > sizeof (struct ip)) {
#if 0 /*XXX*/
ip_stripoptions(m, (struct mbuf *)0);
#else
#ifdef __ECOS
diag_printf("extension headers are not allowed\n");
#else
printf("extension headers are not allowed\n");
#endif
m_freem(m);
return;
#endif
}
if (m->m_len < iphlen + sizeof(struct tcphdr)) {
if ((m = m_pullup2(m, iphlen + sizeof(struct tcphdr))) == 0) {
tcpstat.tcps_rcvshort++;
return;
}
#ifndef INET6
ti = mtod(m, struct tcpiphdr *);
#endif /* INET6 */
}
tlen = m->m_pkthdr.len - iphlen;
#ifdef INET6
/*
* After that, do initial segment processing which is still very
* dependent on what IP version you're using.
*/
if (is_ipv6) {
#ifdef DIAGNOSTIC
if (iphlen < sizeof(struct ip6_hdr)) {
m_freem(m);
return;
}
#endif /* DIAGNOSTIC */
/* strip off any options */
if (iphlen > sizeof(struct ip6_hdr)) {
#if 0 /*XXX*/
ipv6_stripoptions(m, iphlen);
#else
#ifdef __ECOS
diag_printf("extension headers are not allowed\n");
#else
printf("extension headers are not allowed\n");
#endif
m_freem(m);
return;
#endif
iphlen = sizeof(struct ip6_hdr);
}
ti = NULL;
ipv6 = mtod(m, struct ip6_hdr *);
if (in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), tlen)) {
tcpstat.tcps_rcvbadsum++;
goto drop;
} /* endif in6_cksum */
} else {
ti = mtod(m, struct tcpiphdr *);
#endif /* INET6 */
/*
* Checksum extended TCP header and data.
*/
#ifndef INET6
tlen = ((struct ip *)ti)->ip_len;
#endif /* INET6 */
len = sizeof (struct ip) + tlen;
bzero(ti->ti_x1, sizeof ti->ti_x1);
ti->ti_len = (u_int16_t)tlen;
HTONS(ti->ti_len);
if ((ti->ti_sum = in_cksum(m, len)) != 0) {
tcpstat.tcps_rcvbadsum++;
goto drop;
}
#ifdef INET6
}
#endif /* INET6 */
#endif /* TUBA_INCLUDE */
th = (struct tcphdr *)(mtod(m, caddr_t) + iphlen);
/*
* 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++;
goto drop;
}
tlen -= off;
if (off > sizeof (struct tcphdr)) {
if (m->m_len < iphlen + off) {
if ((m = m_pullup2(m, iphlen + off)) == 0) {
tcpstat.tcps_rcvshort++;
return;
}
#ifdef INET6
if (is_ipv6)
ipv6 = mtod(m, struct ip6_hdr *);
else
#endif /* INET6 */
ti = mtod(m, struct tcpiphdr *);
th = (struct tcphdr *)(mtod(m, caddr_t) + iphlen);
}
optlen = off - sizeof (struct tcphdr);
optp = mtod(m, caddr_t) + iphlen + sizeof(struct tcphdr);
/*
* Do quick retrieval of timestamp options ("options
* prediction?"). If timestamp is the only option and it's
* formatted as recommended in RFC 1323 appendix A, we
* quickly get the values now and not bother calling
* tcp_dooptions(), etc.
*/
if ((optlen == TCPOLEN_TSTAMP_APPA ||
(optlen > TCPOLEN_TSTAMP_APPA &&
optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
*(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
(th->th_flags & TH_SYN) == 0) {
ts_present = 1;
ts_val = ntohl(*(u_int32_t *)(optp + 4));
ts_ecr = ntohl(*(u_int32_t *)(optp + 8));
optp = NULL; /* we've parsed the options */
}
}
tiflags = th->th_flags;
/*
* Convert TCP protocol specific fields to host format.
*/
NTOHL(th->th_seq);
NTOHL(th->th_ack);
NTOHS(th->th_win);
NTOHS(th->th_urp);
/*
* Locate pcb for segment.
*/
findpcb:
#ifdef INET6
if (is_ipv6) {
inp = in6_pcbhashlookup(&tcbtable, &ipv6->ip6_src, th->th_sport,
&ipv6->ip6_dst, th->th_dport);
} else
#endif /* INET6 */
inp = in_pcbhashlookup(&tcbtable, ti->ti_src, ti->ti_sport,
ti->ti_dst, ti->ti_dport);
if (inp == 0) {
++tcpstat.tcps_pcbhashmiss;
#ifdef INET6
if (is_ipv6)
inp = in_pcblookup(&tcbtable, &ipv6->ip6_src,
th->th_sport, &ipv6->ip6_dst, th->th_dport,
INPLOOKUP_WILDCARD | INPLOOKUP_IPV6);
else
#endif /* INET6 */
inp = in_pcblookup(&tcbtable, &ti->ti_src, ti->ti_sport,
&ti->ti_dst, ti->ti_dport, INPLOOKUP_WILDCARD);
/*
* If the state is CLOSED (i.e., TCB does not exist) then
* all data in the incoming segment is discarded.
* If the TCB exists but is in CLOSED state, it is embryonic,
* but should either do a listen or a connect soon.
*/
if (inp == 0) {
++tcpstat.tcps_noport;
goto dropwithreset;
}
}
tp = intotcpcb(inp);
if (tp == 0)
goto dropwithreset;
if (tp->t_state == TCPS_CLOSED)
goto drop;
/* Unscale the window into a 32-bit value. */
if ((tiflags & TH_SYN) == 0)
tiwin = th->th_win << tp->snd_scale;
else
tiwin = th->th_win;
so = inp->inp_socket;
if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {
if (so->so_options & SO_DEBUG) {
ostate = tp->t_state;
#ifdef INET6
if (is_ipv6)
tcp_saveti6 = *(mtod(m, struct tcpipv6hdr *));
else
#endif /* INET6 */
tcp_saveti = *ti;
}
if (so->so_options & SO_ACCEPTCONN) {
struct socket *so1;
so1 = sonewconn(so, 0);
if (so1 == NULL) {
tcpdropoldhalfopen(tp, th->th_dport);
so1 = sonewconn(so, 0);
if (so1 == NULL)
goto drop;
}
so = so1;
/*
* This is ugly, but ....
*
* Mark socket as temporary until we're
* committed to keeping it. The code at
* ``drop'' and ``dropwithreset'' check the
* flag dropsocket to see if the temporary
* socket created here should be discarded.
* We mark the socket as discardable until
* we're committed to it below in TCPS_LISTEN.
*/
dropsocket++;
#ifdef IPSEC
/*
* We need to copy the required security levels
* from the old pcb.
*/
{
struct inpcb *newinp = (struct inpcb *)so->so_pcb;
bcopy(inp->inp_seclevel, newinp->inp_seclevel,
sizeof(inp->inp_seclevel));
newinp->inp_secrequire = inp->inp_secrequire;
}
#endif /* IPSEC */
#ifdef INET6
/*
* inp still has the OLD in_pcb stuff, set the
* v6-related flags on the new guy, too. This is
* done particularly for the case where an AF_INET6
* socket is bound only to a port, and a v4 connection
* comes in on that port.
* we also copy the flowinfo from the original pcb
* to the new one.
*/
{
int flags = inp->inp_flags;
struct inpcb *oldinpcb = inp;
inp = (struct inpcb *)so->so_pcb;
inp->inp_flags |= (flags & (INP_IPV6 | INP_IPV6_UNDEC
| INP_IPV6_MAPPED));
if ((inp->inp_flags & INP_IPV6) &&
!(inp->inp_flags & INP_IPV6_MAPPED)) {
inp->inp_ipv6.ip6_hlim =
oldinpcb->inp_ipv6.ip6_hlim;
inp->inp_ipv6.ip6_flow =
oldinpcb->inp_ipv6.ip6_flow;
}
}
#else /* INET6 */
inp = (struct inpcb *)so->so_pcb;
#endif /* INET6 */
inp->inp_lport = th->th_dport;
#ifdef INET6
if (is_ipv6) {
inp->inp_laddr6 = ipv6->ip6_dst;
inp->inp_fflowinfo = htonl(0x0fffffff) &
ipv6->ip6_flow;
/*inp->inp_options = ip6_srcroute();*/ /* soon. */
/* still need to tweak outbound options
processing to include this mbuf in
the right place and put the correct
NextHdr values in the right places.
XXX rja */
} else {
if (inp->inp_flags & INP_IPV6) {/* v4 to v6 socket */
CREATE_IPV6_MAPPED(inp->inp_laddr6,
ti->ti_dst.s_addr);
} else {
#endif /* INET6 */
inp->inp_laddr = ti->ti_dst;
inp->inp_options = ip_srcroute();
#ifdef INET6
}
}
#endif /* INET6 */
in_pcbrehash(inp);
tp = intotcpcb(inp);
tp->t_state = TCPS_LISTEN;
/* Compute proper scaling value from buffer space
*/
while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat)
tp->request_r_scale++;
}
}
#ifdef IPSEC
/* Check if this socket requires security for incoming packets */
if ((inp->inp_seclevel[SL_AUTH] >= IPSEC_LEVEL_REQUIRE &&
!(m->m_flags & M_AUTH)) ||
(inp->inp_seclevel[SL_ESP_TRANS] >= IPSEC_LEVEL_REQUIRE &&
!(m->m_flags & M_CONF))) {
#ifdef notyet
#ifdef INET6
if (is_ipv6)
icmp6_error(m, ICMPV6_BLAH, ICMPV6_BLAH, 0);
else
#endif /* INET6 */
icmp_error(m, ICMP_BLAH, ICMP_BLAH, 0, 0);
#endif /* notyet */
tcpstat.tcps_rcvnosec++;
goto drop;
}
/* Use tdb_bind_out for this inp's outbound communication */
if (tdb)
tdb_add_inp(tdb, inp);
#endif /*IPSEC */
/*
* Segment received on connection.
* Reset idle time and keep-alive timer.
*/
tp->t_idle = 0;
if (tp->t_state != TCPS_SYN_RECEIVED)
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
#ifdef TCP_SACK
if (!tp->sack_disable)
tcp_del_sackholes(tp, th); /* Delete stale SACK holes */
#endif /* TCP_SACK */
/*
* Process options if not in LISTEN state,
* else do it below (after getting remote address).
*/
if (optp && tp->t_state != TCPS_LISTEN)
tcp_dooptions(tp, optp, optlen, th,
&ts_present, &ts_val, &ts_ecr);
#ifdef TCP_SACK
if (!tp->sack_disable) {
tp->rcv_laststart = th->th_seq; /* last rec'vd segment*/
tp->rcv_lastend = th->th_seq + tlen;
}
#endif /* TCP_SACK */
/*
* Header prediction: check for the two common cases
* of a uni-directional data xfer. If the packet has
* no control flags, is in-sequence, the window didn't
* change and we're not retransmitting, it's a
* candidate. If the length is zero and the ack moved
* forward, we're the sender side of the xfer. Just
* free the data acked & wake any higher level process
* that was blocked waiting for space. If the length
* is non-zero and the ack didn't move, we're the
* receiver side. If we're getting packets in-order
* (the reassembly queue is empty), add the data to
* the socket buffer and note that we need a delayed ack.
*/
if (tp->t_state == TCPS_ESTABLISHED &&
(tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
(!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) &&
th->th_seq == tp->rcv_nxt &&
tiwin && tiwin == tp->snd_wnd &&
tp->snd_nxt == tp->snd_max) {
/*
* If last ACK falls within this segment's sequence numbers,
* record the timestamp.
* Fix from Braden, see Stevens p. 870
*/
if (ts_present && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
tp->ts_recent_age = tcp_now;
tp->ts_recent = ts_val;
}
if (tlen == 0) {
if (SEQ_GT(th->th_ack, tp->snd_una) &&
SEQ_LEQ(th->th_ack, tp->snd_max) &&
tp->snd_cwnd >= tp->snd_wnd &&
tp->t_dupacks == 0) {
/*
* this is a pure ack for outstanding data.
*/
++tcpstat.tcps_predack;
if (ts_present)
tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
else if (tp->t_rtt &&
SEQ_GT(th->th_ack, tp->t_rtseq))
tcp_xmit_timer(tp, tp->t_rtt);
acked = th->th_ack - tp->snd_una;
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
ND6_HINT(tp);
sbdrop(&so->so_snd, acked);
tp->snd_una = th->th_ack;
#if defined(TCP_SACK) || defined(TCP_NEWRENO)
/*
* We want snd_last to track snd_una so
* as to avoid sequence wraparound problems
* for very large transfers.
*/
tp->snd_last = tp->snd_una;
#endif /* TCP_SACK or TCP_NEWRENO */
#if defined(TCP_SACK) && defined(TCP_FACK)
tp->snd_fack = tp->snd_una;
tp->retran_data = 0;
#endif /* TCP_FACK */
m_freem(m);
/*
* If all outstanding data are acked, stop
* retransmit timer, otherwise restart timer
* using current (possibly backed-off) value.
* If process is waiting for space,
* wakeup/selwakeup/signal. If data
* are ready to send, let tcp_output
* decide between more output or persist.
*/
if (tp->snd_una == tp->snd_max)
tp->t_timer[TCPT_REXMT] = 0;
else if (tp->t_timer[TCPT_PERSIST] == 0)
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (sb_notify(&so->so_snd))
sowwakeup(so);
if (so->so_snd.sb_cc)
(void) tcp_output(tp);
return;
}
} else if (th->th_ack == tp->snd_una &&
tp->segq.lh_first == NULL &&
tlen <= sbspace(&so->so_rcv)) {
/*
* This is a pure, in-sequence data packet
* with nothing on the reassembly queue and
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -