extractedLnx/linux/net/inet/tcp.c_tcp_rcv.c
int
tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
unsigned long daddr, unsigned short len,
unsigned long saddr, int redo, struct inet_protocol * protocol)
{
struct tcphdr *th;
struct sock *sk;
if (!skb)
{
return(0);
}
if (!dev)
{
return(0);
}
tcp_statistics.TcpInSegs++;
if(skb->pkt_type!=PACKET_HOST)
{
kfree_skb(skb,FREE_READ);
return(0);
}
th = skb->h.th;
/*
* Find the socket.
*/
sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
/*
* If this socket has got a reset its to all intents and purposes
* really dead
*/
if (sk!=NULL && sk->zapped)
sk=NULL;
if (!redo)
{
if (tcp_check(th, len, saddr, daddr ))
{
skb->sk = NULL;
kfree_skb(skb,FREE_READ);
/*
* We don't release the socket because it was
* never marked in use.
*/
return(0);
}
th->seq = ntohl(th->seq);
/* See if we know about the socket. */
if (sk == NULL)
{
if (!th->rst)
tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
return(0);
}
skb->len = len;
skb->acked = 0;
skb->used = 0;
skb->free = 0;
skb->saddr = daddr;
skb->daddr = saddr;
/* We may need to add it to the backlog here. */
cli();
if (sk->inuse)
{
skb_queue_tail(&sk->back_log, skb);
sti();
return(0);
}
sk->inuse = 1;
sti();
}
else
{
if (!sk)
{
return(0);
}
}
if (!sk->prot)
{
return(0);
}
/*
* Charge the memory to the socket.
*/
if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
skb->sk=sk;
sk->rmem_alloc += skb->mem_len;
#ifdef TCP_FASTPATH
/*
* Incoming data stream fastpath.
*
* We try to optimise two things.
* 1) Spot general data arriving without funny options and skip extra checks and the switch.
* 2) Spot the common case in raw data receive streams of a packet that has no funny options,
* fits exactly on the end of the current queue and may or may not have the ack bit set.
*
* Case two especially is done inline in this routine so there are no long jumps causing heavy
* cache thrashing, no function call overhead (except for the ack sending if needed) and for
* speed although further optimizing here is possible.
*/
/* I'm trusting gcc to optimise this sensibly... might need judicious application of a software mallet */
if(!(sk->shutdown & RCV_SHUTDOWN) && sk->state==TCP_ESTABLISHED && !th->urg && !th->syn && !th->fin && !th->rst)
{
/* Packets in order. Fits window */
if(th->seq == sk->acked_seq+1 && sk->window && tcp_clean_end(sk))
{
/* Ack is harder */
if(th->ack && !tcp_ack(sk, th, saddr, len))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return 0;
}
/*
* Set up variables
*/
skb->len -= (th->doff *4);
sk->bytes_rcv += skb->len;
tcp_rx_hit2++;
if(skb->len)
{
skb_queue_tail(&sk->receive_queue,skb); /* We already know where to put it */
if(sk->window >= skb->len)
sk->window-=skb->len; /* We know its effect on the window */
else
sk->window=0;
sk->acked_seq = th->seq+skb->len; /* Easy */
skb->acked=1; /* Guaranteed true */
if(!sk->delay_acks || sk->ack_backlog >= sk->max_ack_backlog ||
sk->bytes_rcv > sk->max_unacked)
{
tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th , saddr);
}
else
{
sk->ack_backlog++;
reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
}
if(!sk->dead)
sk->data_ready(sk,0);
release_sock(sk);
return 0;
}
}
/*
* More generic case of arriving data stream in ESTABLISHED
*/
tcp_rx_hit1++;
if(!tcp_sequence(sk, th, len, opt, saddr, dev))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return 0;
}
if(th->ack && !tcp_ack(sk, th, saddr, len))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return 0;
}
if(tcp_data(skb, sk, saddr, len))
kfree_skb(skb, FREE_READ);
release_sock(sk);
return 0;
}
tcp_rx_miss++;
#endif
/*
* Now deal with all cases.
*/
switch(sk->state)
{
/*
* This should close the system down if it's waiting
* for an ack that is never going to be sent.
*/
case TCP_LAST_ACK:
if (th->rst)
{
sk->zapped=1;
sk->err = ECONNRESET;
tcp_set_state(sk,TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead)
{
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
case TCP_TIME_WAIT:
/*
* is it a good packet?
*/
if (!tcp_sequence(sk, th, len, opt, saddr,dev))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (th->rst)
{
if(sk->state!=TCP_TIME_WAIT) /* RFC 1337 recommendation re RST in time wait */
{
tcp_statistics.TcpEstabResets++;
sk->zapped=1;
/* This means the thing should really be closed. */
sk->err = ECONNRESET;
if (sk->state == TCP_CLOSE_WAIT)
{
sk->err = EPIPE;
}
tcp_set_state(sk,TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead)
{
sk->state_change(sk);
}
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (th->syn)
{
long seq=sk->write_seq;
int st=sk->state;
tcp_statistics.TcpEstabResets++;
sk->err = ECONNRESET;
tcp_set_state(sk,TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
if(sk->debug)
printk("Socket %p reset by SYN while established.\n", sk);
if (!sk->dead) {
sk->state_change(sk);
}
/*
* The BSD port reuse protocol violation.
* I do sometimes wonder how the *bsd people
* have the nerve to talk about 'standards'.
*
* If seq > last used on connection then
* open a new connection and use 128000+seq of
* old connection.
*
*/
if(st==TCP_TIME_WAIT && th->seq > sk->acked_seq && sk->dead)
{
struct sock *psk=sk;
/*
* Find the listening socket.
*/
sk=get_sock(&tcp_prot, th->source, daddr, th->dest, saddr);
if(sk && sk->state==TCP_LISTEN)
{
sk->inuse=1;
tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+128000);
release_sock(psk);
/* Fall through in case people are
also using the piggy backed SYN + data
protocol violation */
}
else
{
tcp_reset(daddr, saddr, th, psk->prot, opt,dev, psk->ip_tos,psk->ip_ttl);
release_sock(psk);
kfree_skb(skb, FREE_READ);
return 0;
}
}
else
{
tcp_reset(daddr, saddr, th, sk->prot, opt,dev, sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
}
if (th->ack && !tcp_ack(sk, th, saddr, len)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (tcp_urg(sk, th, saddr, len)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (tcp_data(skb, sk, saddr, len)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (th->fin && tcp_fin(skb, sk, th, saddr, dev)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
release_sock(sk);
return(0);
case TCP_CLOSE:
if (sk->dead || sk->daddr) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (!th->rst) {
if (!th->ack)
th->ack_seq = 0;
if(sk->debug) printk("Reset on closed socket %d.\n",sk->blog);
tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
case TCP_LISTEN:
if (th->rst) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (th->ack) {
if(sk->debug) printk("Reset on listening socket %d.\n",sk->blog);
tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (th->syn)
{
/*
* Now we just put the whole thing including
* the header and saddr, and protocol pointer
* into the buffer. We can't respond until the
* user tells us to accept the connection.
*/
tcp_conn_request(sk, skb, daddr, saddr, opt, dev, tcp_init_seq());
release_sock(sk);
return(0);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
case TCP_SYN_RECV:
if (th->syn) {
/* Probably a retransmitted syn */
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
default:
if (!tcp_sequence(sk, th, len, opt, saddr,dev))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
case TCP_SYN_SENT:
if (th->rst)
{
tcp_statistics.TcpAttemptFails++;
sk->err = ECONNREFUSED;
tcp_set_state(sk,TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
sk->zapped = 1;
if (!sk->dead)
{
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (!th->ack)
{
if (th->syn)
{
/* Crossed SYN's are fine - but talking to
yourself is right out... */
if(sk->saddr==saddr && sk->daddr==daddr &&
sk->dummy_th.source==th->source &&
sk->dummy_th.dest==th->dest)
{
tcp_statistics.TcpAttemptFails++;
sk->err = ECONNREFUSED;
tcp_set_state(sk,TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
sk->zapped = 1;
if (!sk->dead)
{
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
tcp_set_state(sk,TCP_SYN_RECV);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
switch(sk->state)
{
case TCP_SYN_SENT:
if (!tcp_ack(sk, th, saddr, len))
{
tcp_statistics.TcpAttemptFails++;
tcp_reset(daddr, saddr, th,
sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
/*
* If the syn bit is also set, switch to
* tcp_syn_recv, and then to established.
*/
if (!th->syn)
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
/* Ack the syn and fall through. */
sk->acked_seq = th->seq+1;
sk->fin_seq = th->seq;
tcp_send_ack(sk->sent_seq, th->seq+1,
sk, th, sk->daddr);
case TCP_SYN_RECV:
if (!tcp_ack(sk, th, saddr, len))
{
tcp_statistics.TcpAttemptFails++;
tcp_reset(daddr, saddr, th,
sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
tcp_set_state(sk,TCP_ESTABLISHED);
/*
* Now we need to finish filling out
* some of the tcp header.
*
* We need to check for mtu info.
*/
tcp_options(sk, th);
sk->dummy_th.dest = th->source;
sk->copied_seq = sk->acked_seq-1;
if (!sk->dead)
{
sk->state_change(sk);
}
/*
* We've already processed his first
* ack. In just about all cases that
* will have set max_window. This is
* to protect us against the possibility
* that the initial window he sent was 0.
* This must occur after tcp_options, which
* sets sk->mtu.
*/
if (sk->max_window == 0)
{
sk->max_window = 32;
sk->mss = min(sk->max_window, sk->mtu);
}
/*
* Now process the rest like we were
* already in the established state.
*/
if (th->urg)
{
if (tcp_urg(sk, th, saddr, len))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
}
if (tcp_data(skb, sk, saddr, len))
kfree_skb(skb, FREE_READ);
if (th->fin)
tcp_fin(skb, sk, th, saddr, dev);
release_sock(sk);
return(0);
}
if (th->urg)
{
if (tcp_urg(sk, th, saddr, len))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
}
if (tcp_data(skb, sk, saddr, len))
{
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (!th->fin)
{
release_sock(sk);
return(0);
}
tcp_fin(skb, sk, th, saddr, dev);
release_sock(sk);
return(0);
}
}
Generated by GNU enscript 1.6.4.