?? tcpin.c
字號:
/* Process incoming TCP segments. Page number references are to ARPA RFC-793,
* the TCP specification.
*
*/
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"
#include "icmp.h"
#include "iface.h"
#include "ip.h"
static void update(struct tcb *tcb,struct tcp *seg,uint16 length);
static void proc_syn(struct tcb *tcb,uint8 tos,struct tcp *seg);
static void add_reseq(struct tcb *tcb,uint8 tos,struct tcp *seg,
struct mbuf **bp,uint16 length);
static void get_reseq(struct tcb *tcb,uint8 *tos,struct tcp *seq,
struct mbuf **bp,uint16 *length);
static int trim(struct tcb *tcb,struct tcp *seg,struct mbuf **bpp,
uint16 *length);
static int in_window(struct tcb *tcb,int32 seq);
/* This function is called from IP with the IP header in machine byte order,
* along with a mbuf chain pointing to the TCP header.
*/
void
tcp_input(
struct iface *iface, /* Incoming interface (ignored) */
struct ip *ip, /* IP header */
struct mbuf **bpp, /* Data field, if any */
int rxbroadcast, /* Incoming broadcast - discard if true */
int32 said /* Authenticated packet */
){
struct tcb *ntcb;
register struct tcb *tcb; /* TCP Protocol control block */
struct tcp seg; /* Local copy of segment header */
struct connection conn; /* Local copy of addresses */
struct pseudo_header ph; /* Pseudo-header for checksumming */
int hdrlen; /* Length of TCP header */
uint16 length;
int32 t;
if(bpp == NULL || *bpp == NULL)
return;
tcpInSegs++;
if(rxbroadcast){
/* Any TCP packet arriving as a broadcast is
* to be completely IGNORED!!
*/
free_p(bpp);
return;
}
length = ip->length - IPLEN - ip->optlen;
ph.source = ip->source;
ph.dest = ip->dest;
ph.protocol = ip->protocol;
ph.length = length;
if(cksum(&ph,*bpp,length) != 0){
/* Checksum failed, ignore segment completely */
tcpInErrs++;
free_p(bpp);
return;
}
/* Form local copy of TCP header in host byte order */
if((hdrlen = ntohtcp(&seg,bpp)) < 0){
/* TCP header is too small */
free_p(bpp);
return;
}
length -= hdrlen;
/* Fill in connection structure and find TCB */
conn.local.address = ip->dest;
conn.local.port = seg.dest;
conn.remote.address = ip->source;
conn.remote.port = seg.source;
if((tcb = lookup_tcb(&conn)) == NULL){
/* If this segment doesn't carry a SYN, reject it */
if(!seg.flags.syn){
free_p(bpp);
reset(ip,&seg);
return;
}
/* See if there's a TCP_LISTEN on this socket with
* unspecified remote address and port
*/
conn.remote.address = 0;
conn.remote.port = 0;
if((tcb = lookup_tcb(&conn)) == NULL){
/* Nope, try unspecified local address too */
conn.local.address = 0;
if((tcb = lookup_tcb(&conn)) == NULL){
/* No LISTENs, so reject */
free_p(bpp);
reset(ip,&seg);
return;
}
}
/* We've found an server listen socket, so clone the TCB */
if(tcb->flags.clone){
ntcb = (struct tcb *)mallocw(sizeof (struct tcb));
ASSIGN(*ntcb,*tcb);
tcb = ntcb;
tcb->timer.arg = tcb;
/* Put on list */
tcb->next = Tcbs;
Tcbs = tcb;
}
/* Put all the socket info into the TCB */
tcb->conn.local.address = ip->dest;
tcb->conn.remote.address = ip->source;
tcb->conn.remote.port = seg.source;
}
tcb->flags.congest = ip->flags.congest;
/* Do unsynchronized-state processing (p. 65-68) */
switch(tcb->state){
case TCP_CLOSED:
free_p(bpp);
reset(ip,&seg);
return;
case TCP_LISTEN:
if(seg.flags.rst){
free_p(bpp);
return;
}
if(seg.flags.ack){
free_p(bpp);
reset(ip,&seg);
return;
}
if(seg.flags.syn){
/* (Security check is bypassed) */
/* page 66 */
proc_syn(tcb,ip->tos,&seg);
send_syn(tcb);
settcpstate(tcb,TCP_SYN_RECEIVED);
if(length != 0 || seg.flags.fin) {
/* Continue processing if there's more */
break;
}
tcp_output(tcb);
}
free_p(bpp); /* Unlikely to get here directly */
return;
case TCP_SYN_SENT:
if(seg.flags.ack){
if(!seq_within(seg.ack,tcb->iss+1,tcb->snd.nxt)){
free_p(bpp);
reset(ip,&seg);
return;
}
}
if(seg.flags.rst){ /* p 67 */
if(seg.flags.ack){
/* The ack must be acceptable since we just checked it.
* This is how the remote side refuses connect requests.
*/
close_self(tcb,RESET);
}
free_p(bpp);
return;
}
/* (Security check skipped here) */
#ifdef PREC_CHECK /* Turned off for compatibility with BSD */
/* Check incoming precedence; it must match if there's an ACK */
if(seg.flags.ack && PREC(ip->tos) != PREC(tcb->tos)){
free_p(bpp);
reset(ip,&seg);
return;
}
#endif
if(seg.flags.syn){
proc_syn(tcb,ip->tos,&seg);
if(seg.flags.ack){
/* Our SYN has been acked, otherwise the ACK
* wouldn't have been valid.
*/
update(tcb,&seg,length);
settcpstate(tcb,TCP_ESTABLISHED);
} else {
settcpstate(tcb,TCP_SYN_RECEIVED);
}
if(length != 0 || seg.flags.fin) {
break; /* Continue processing if there's more */
}
tcp_output(tcb);
} else {
free_p(bpp); /* Ignore if neither SYN or RST is set */
}
return;
}
/* We reach this point directly in any synchronized state. Note that
* if we fell through from LISTEN or SYN_SENT processing because of a
* data-bearing SYN, window trimming and sequence testing "cannot fail".
*
* Begin by trimming segment to fit receive window.
*/
if(trim(tcb,&seg,bpp,&length) == -1){
/* Segment is unacceptable */
if(!seg.flags.rst){ /* NEVER answer RSTs */
/* In SYN_RECEIVED state, answer a retransmitted SYN
* with a retransmitted SYN/ACK.
*/
if(tcb->state == TCP_SYN_RECEIVED)
tcb->snd.ptr = tcb->snd.una;
tcb->flags.force = 1;
tcp_output(tcb);
}
return;
}
/* If segment isn't the next one expected, and there's data
* or flags associated with it, put it on the resequencing
* queue and remind ourselves to ACK it. Then strip off
* the SYN/data/FIN and continue to process the ACK (or RST)
*/
if(seg.seq != tcb->rcv.nxt
&& (length != 0 || seg.flags.syn || seg.flags.fin)){
add_reseq(tcb,ip->tos,&seg,bpp,length);
if(seg.flags.ack && !seg.flags.rst)
tcb->flags.force = 1;
seg.flags.syn = seg.flags.fin = 0;
length = 0;
}
/* This loop first processes the current segment, and then
* repeats if it can process the resequencing queue.
*/
for(;;){
/* We reach this point with an acceptable segment; data and flags
* (if any) are in the window, and if there's data, syn or fin,
* the starting sequence number equals rcv.nxt
* (p. 70)
*/
if(seg.flags.rst){
if(tcb->state == TCP_SYN_RECEIVED
&& !tcb->flags.clone && !tcb->flags.active){
/* Go back to listen state only if this was
* not a cloned or active server TCB
*/
settcpstate(tcb,TCP_LISTEN);
} else {
close_self(tcb,RESET);
}
free_p(bpp);
return;
}
/* (Security check skipped here) p. 71 */
#ifdef PREC_CHECK
/* Check for precedence mismatch */
if(PREC(ip->tos) != PREC(tcb->tos)){
free_p(bpp);
reset(ip,&seg);
return;
}
#endif
/* Check for erroneous extra SYN */
if(seg.flags.syn){
free_p(bpp);
reset(ip,&seg);
return;
}
/* Update timestamp field */
if(seg.flags.tstamp
&& seq_within(tcb->last_ack_sent,seg.seq,seg.seq+length))
tcb->ts_recent = seg.tsval;
/* Check ack field p. 72 */
if(!seg.flags.ack){
free_p(bpp); /* All segments after synchronization must have ACK */
return;
}
/* Process ACK */
switch(tcb->state){
case TCP_SYN_RECEIVED:
if(seq_within(seg.ack,tcb->snd.una+1,tcb->snd.nxt)){
update(tcb,&seg,length);
settcpstate(tcb,TCP_ESTABLISHED);
} else {
free_p(bpp);
reset(ip,&seg);
return;
}
break;
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
case TCP_FINWAIT2:
update(tcb,&seg,length);
break;
case TCP_FINWAIT1: /* p. 73 */
update(tcb,&seg,length);
if(tcb->sndcnt == 0){
/* Our FIN is acknowledged */
settcpstate(tcb,TCP_FINWAIT2);
}
break;
case TCP_CLOSING:
update(tcb,&seg,length);
if(tcb->sndcnt == 0){
/* Our FIN is acknowledged */
settcpstate(tcb,TCP_TIME_WAIT);
set_timer(&tcb->timer,MSL2*1000L);
start_timer(&tcb->timer);
}
break;
case TCP_LAST_ACK:
update(tcb,&seg,length);
if(tcb->sndcnt == 0){
/* Our FIN is acknowledged, close connection */
close_self(tcb,NORMAL);
return;
}
break;
case TCP_TIME_WAIT:
start_timer(&tcb->timer);
break;
}
/* (URGent bit processing skipped here) */
/* Process the segment text, if any, beginning at rcv.nxt (p. 74) */
if(length != 0){
switch(tcb->state){
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_FINWAIT1:
case TCP_FINWAIT2:
/* Place on receive queue */
t = msclock();
if(t > tcb->lastrx){
tcb->rxbw = 1000L*length / (t - tcb->lastrx);
tcb->lastrx = t;
}
append(&tcb->rcvq,bpp);
tcb->rcvcnt += length;
tcb->rcv.nxt += length;
tcb->rcv.wnd -= length;
tcb->flags.force = 1;
/* Notify user */
if(tcb->r_upcall)
(*tcb->r_upcall)(tcb,tcb->rcvcnt);
break;
default:
/* Ignore segment text */
free_p(bpp);
break;
}
}
/* process FIN bit (p 75) */
if(seg.flags.fin){
tcb->flags.force = 1; /* Always respond with an ACK */
switch(tcb->state){
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
tcb->rcv.nxt++;
settcpstate(tcb,TCP_CLOSE_WAIT);
break;
case TCP_FINWAIT1:
tcb->rcv.nxt++;
if(tcb->sndcnt == 0){
/* Our FIN has been acked; bypass TCP_CLOSING state */
settcpstate(tcb,TCP_TIME_WAIT);
set_timer(&tcb->timer,MSL2*1000L);
start_timer(&tcb->timer);
} else {
settcpstate(tcb,TCP_CLOSING);
}
break;
case TCP_FINWAIT2:
tcb->rcv.nxt++;
settcpstate(tcb,TCP_TIME_WAIT);
set_timer(&tcb->timer,MSL2*1000L);
start_timer(&tcb->timer);
break;
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_LAST_ACK:
break; /* Ignore */
case TCP_TIME_WAIT: /* p 76 */
start_timer(&tcb->timer);
break;
}
/* Call the client again so he can see EOF */
if(tcb->r_upcall)
(*tcb->r_upcall)(tcb,tcb->rcvcnt);
}
/* Scan the resequencing queue, looking for a segment we can handle,
* and freeing all those that are now obsolete.
*/
while(tcb->reseq != NULL && seq_ge(tcb->rcv.nxt,tcb->reseq->seg.seq)){
get_reseq(tcb,&ip->tos,&seg,bpp,&length);
if(trim(tcb,&seg,bpp,&length) == 0)
goto gotone;
/* Segment is an old one; trim has freed it */
}
break;
gotone: ;
}
tcp_output(tcb); /* Send any necessary ack */
}
/* Process an incoming ICMP response */
void
tcp_icmp(
int32 icsource, /* Sender of ICMP message (not used) */
int32 source, /* Original IP datagram source (i.e. us) */
int32 dest, /* Original IP datagram dest (i.e., them) */
uint8 type, /* ICMP error codes */
uint8 code,
struct mbuf **bpp /* First 8 bytes of TCP header */
){
struct tcp seg;
struct connection conn;
register struct tcb *tcb;
/* Extract the socket info from the returned TCP header fragment
* Note that since this is a datagram we sent, the source fields
* refer to the local side.
*/
ntohtcp(&seg,bpp);
conn.local.port = seg.source;
conn.remote.port = seg.dest;
conn.local.address = source;
conn.remote.address = dest;
if((tcb = lookup_tcb(&conn)) == NULL)
return; /* Unknown connection, ignore */
/* Verify that the sequence number in the returned segment corresponds
* to something currently unacknowledged. If not, it can safely
* be ignored.
*/
if(!seq_within(seg.seq,tcb->snd.una,tcb->snd.nxt))
return;
/* Destination Unreachable and Time Exceeded messages never kill a
* connection; the info is merely saved for future reference.
*/
switch(type){
case ICMP_DEST_UNREACH:
case ICMP_TIME_EXCEED:
tcb->type = type;
tcb->code = code;
tcb->unreach++;
break;
case ICMP_QUENCH:
/* Source quench; reduce slowstart threshold to half
* current window and restart slowstart
*/
tcb->ssthresh = tcb->cwind / 2;
tcb->ssthresh = max(tcb->ssthresh,tcb->mss);
/* Shrink congestion window to 1 packet */
tcb->cwind = tcb->mss;
tcb->quench++;
break;
}
}
/* Send an acceptable reset (RST) response for this segment
* The RST reply is composed in place on the input segment
*/
void
reset(ip,seg)
struct ip *ip; /* Offending IP header */
register struct tcp *seg; /* Offending TCP header */
{
struct mbuf *hbp;
uint16 tmp;
if(seg->flags.rst)
return; /* Never send an RST in response to an RST */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -