📄 tcp.c
字号:
* We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent.
* FIXME:
* Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
* Most of this is guesswork, so maybe it will work...
*/
/* If we've already sent a FIN, return. */
if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) return;
if (!(how & SEND_SHUTDOWN)) return;
sk->inuse = 1;
/* Clear out any half completed packets. */
if (sk->partial)
tcp_send_partial(sk);
prot =(struct proto *)sk->prot;
th =(struct tcphdr *)&sk->dummy_th;
release_sock(sk); /* incase the malloc sleeps. */
buff = prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
if (buff == NULL) return;
sk->inuse = 1;
DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff));
buff->mem_addr = buff;
buff->mem_len = MAX_RESET_SIZE;
buff->sk = sk;
buff->len = sizeof(*t1);
t1 =(struct tcphdr *) buff->data;
/* Put in the IP header and routing stuff. */
tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, sk->opt,
sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
buff->free=1;
prot->wfree(sk,buff->mem_addr, buff->mem_len);
release_sock(sk);
DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
return;
}
t1 =(struct tcphdr *)((char *)t1 +tmp);
buff->len += tmp;
buff->dev = dev;
memcpy(t1, th, sizeof(*t1));
t1->seq = ntohl(sk->write_seq);
sk->write_seq++;
buff->h.seq = sk->write_seq;
t1->ack = 1;
t1->ack_seq = ntohl(sk->acked_seq);
t1->window = ntohs(sk->window=tcp_select_window(sk)/*sk->prot->rspace(sk)*/);
t1->fin = 1;
t1->rst = 0;
t1->doff = sizeof(*t1)/4;
tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
/*
* Can't just queue this up.
* It should go at the end of the write queue.
*/
if (sk->wback != NULL) {
buff->free=0;
buff->next = NULL;
sk->wback->next = buff;
sk->wback = buff;
buff->magic = TCP_WRITE_QUEUE_MAGIC;
} else {
sk->sent_seq = sk->write_seq;
sk->prot->queue_xmit(sk, dev, buff, 0);
}
if (sk->state == TCP_ESTABLISHED) sk->state = TCP_FIN_WAIT1;
else sk->state = TCP_FIN_WAIT2;
release_sock(sk);
}
static int
tcp_recvfrom(struct sock *sk, unsigned char *to,
int to_len, int nonblock, unsigned flags,
struct sockaddr_in *addr, int *addr_len)
{
struct sockaddr_in sin;
int len;
int err;
int result;
/* Have to check these first unlike the old code. If
we check them after we lose data on an error
which is wrong */
err = verify_area(VERIFY_WRITE,addr_len,sizeof(long));
if(err)
return err;
len = get_fs_long(addr_len);
if(len > sizeof(sin))
len = sizeof(sin);
err=verify_area(VERIFY_WRITE, addr, len);
if(err)
return err;
result=tcp_read(sk, to, to_len, nonblock, flags);
if (result < 0) return(result);
sin.sin_family = AF_INET;
sin.sin_port = sk->dummy_th.dest;
sin.sin_addr.s_addr = sk->daddr;
memcpy_tofs(addr, &sin, len);
put_fs_long(len, addr_len);
return(result);
}
/* This routine will send an RST to the other tcp. */
static void
tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl)
{
struct sk_buff *buff;
struct tcphdr *t1;
int tmp;
/*
* We need to grab some memory, and put together an RST,
* and then put it into the queue to be sent.
*/
buff = prot->wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC);
if (buff == NULL)
return;
DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff));
buff->mem_addr = buff;
buff->mem_len = MAX_RESET_SIZE;
buff->len = sizeof(*t1);
buff->sk = NULL;
buff->dev = dev;
t1 =(struct tcphdr *) buff->data;
/* Put in the IP header and routing stuff. */
tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt,
sizeof(struct tcphdr),tos,ttl);
if (tmp < 0) {
buff->free = 1;
prot->wfree(NULL, buff->mem_addr, buff->mem_len);
return;
}
t1 =(struct tcphdr *)((char *)t1 +tmp);
buff->len += tmp;
memcpy(t1, th, sizeof(*t1));
/* Swap the send and the receive. */
t1->dest = th->source;
t1->source = th->dest;
t1->rst = 1;
t1->window = 0;
if(th->ack)
{
t1->ack = 0;
t1->seq = th->ack_seq;
t1->ack_seq = 0;
}
else
{
t1->ack = 1;
if(!th->syn)
t1->ack_seq=htonl(th->seq);
else
t1->ack_seq=htonl(th->seq+1);
t1->seq=0;
}
t1->syn = 0;
t1->urg = 0;
t1->fin = 0;
t1->psh = 0;
t1->doff = sizeof(*t1)/4;
tcp_send_check(t1, saddr, daddr, sizeof(*t1), NULL);
prot->queue_xmit(NULL, dev, buff, 1);
}
/*
* Look for tcp options. Parses everything but only knows about MSS.
* This routine is always called with the packet containing the SYN.
* However it may also be called with the ack to the SYN. So you
* can't assume this is always the SYN. It's always called after
* we have set up sk->mtu to our own MTU.
*/
static void
tcp_options(struct sock *sk, struct tcphdr *th)
{
unsigned char *ptr;
int length=(th->doff*4)-sizeof(struct tcphdr);
int mss_seen = 0;
ptr = (unsigned char *)(th + 1);
while(length>0)
{
int opcode=*ptr++;
int opsize=*ptr++;
switch(opcode)
{
case TCPOPT_EOL:
return;
case TCPOPT_NOP:
length-=2;
continue;
default:
if(opsize<=2) /* Avoid silly options looping forever */
return;
switch(opcode)
{
case TCPOPT_MSS:
if(opsize==4 && th->syn)
{
sk->mtu=min(sk->mtu,ntohs(*(unsigned short *)ptr));
mss_seen = 1;
}
break;
/* Add other options here as people feel the urge to implement stuff like large windows */
}
ptr+=opsize-2;
length-=opsize;
}
}
if (th->syn) {
if (! mss_seen)
sk->mtu=min(sk->mtu, 536); /* default MSS if none sent */
}
sk->mss = min(sk->max_window, sk->mtu);
}
static inline unsigned long default_mask(unsigned long dst)
{
dst = ntohl(dst);
if (IN_CLASSA(dst))
return htonl(IN_CLASSA_NET);
if (IN_CLASSB(dst))
return htonl(IN_CLASSB_NET);
return htonl(IN_CLASSC_NET);
}
/*
* This routine handles a connection request.
* It should make sure we haven't already responded.
* Because of the way BSD works, we have to send a syn/ack now.
* This also means it will be harder to close a socket which is
* listening.
*/
static void
tcp_conn_request(struct sock *sk, struct sk_buff *skb,
unsigned long daddr, unsigned long saddr,
struct options *opt, struct device *dev)
{
struct sk_buff *buff;
struct tcphdr *t1;
unsigned char *ptr;
struct sock *newsk;
struct tcphdr *th;
int tmp;
DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n"
" opt = %X, dev = %X)\n",
sk, skb, daddr, saddr, opt, dev));
th = skb->h.th;
/* If the socket is dead, don't accept the connection. */
if (!sk->dead) {
sk->data_ready(sk,0);
} else {
DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n"));
tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
return;
}
/*
* Make sure we can accept more. This will prevent a
* flurry of syns from eating up all our memory.
*/
if (sk->ack_backlog >= sk->max_ack_backlog) {
kfree_skb(skb, FREE_READ);
return;
}
/*
* We need to build a new sock struct.
* It is sort of bad to have a socket without an inode attached
* to it, but the wake_up's will just wake up the listening socket,
* and if the listening socket is destroyed before this is taken
* off of the queue, this will take care of it.
*/
newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
if (newsk == NULL) {
/* just ignore the syn. It will get retransmitted. */
kfree_skb(skb, FREE_READ);
return;
}
DPRINTF((DBG_TCP, "newsk = %X\n", newsk));
memcpy((void *)newsk,(void *)sk, sizeof(*newsk));
newsk->wback = NULL;
newsk->wfront = NULL;
newsk->rqueue = NULL;
newsk->send_head = NULL;
newsk->send_tail = NULL;
newsk->back_log = NULL;
newsk->rtt = TCP_CONNECT_TIME << 3;
newsk->rto = TCP_CONNECT_TIME;
newsk->mdev = 0;
newsk->max_window = 0;
newsk->cong_window = 1;
newsk->cong_count = 0;
newsk->ssthresh = 0;
newsk->backoff = 0;
newsk->blog = 0;
newsk->intr = 0;
newsk->proc = 0;
newsk->done = 0;
newsk->partial = NULL;
newsk->pair = NULL;
newsk->wmem_alloc = 0;
newsk->rmem_alloc = 0;
newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
newsk->err = 0;
newsk->shutdown = 0;
newsk->ack_backlog = 0;
newsk->acked_seq = skb->h.th->seq+1;
newsk->fin_seq = skb->h.th->seq;
newsk->copied_seq = skb->h.th->seq;
newsk->state = TCP_SYN_RECV;
newsk->timeout = 0;
newsk->write_seq = jiffies * SEQ_TICK - seq_offset;
newsk->window_seq = newsk->write_seq;
newsk->rcv_ack_seq = newsk->write_seq;
newsk->urg_data = 0;
newsk->retransmits = 0;
newsk->destroy = 0;
newsk->timer.data = (unsigned long)newsk;
newsk->timer.function = &net_timer;
newsk->dummy_th.source = skb->h.th->dest;
newsk->dummy_th.dest = skb->h.th->source;
/* Swap these two, they are from our point of view. */
newsk->daddr = saddr;
newsk->saddr = daddr;
put_sock(newsk->num,newsk);
newsk->dummy_th.res1 = 0;
newsk->dummy_th.doff = 6;
newsk->dummy_th.fin = 0;
newsk->dummy_th.syn = 0;
newsk->dummy_th.rst = 0;
newsk->dummy_th.psh = 0;
newsk->dummy_th.ack = 0;
newsk->dummy_th.urg = 0;
newsk->dummy_th.res2 = 0;
newsk->acked_seq = skb->h.th->seq + 1;
newsk->copied_seq = skb->h.th->seq;
/* Grab the ttl and tos values and use them */
newsk->ip_ttl=sk->ip_ttl;
newsk->ip_tos=skb->ip_hdr->tos;
/* use 512 or whatever user asked for */
/* note use of sk->user_mss, since user has no direct access to newsk */
if (sk->user_mss)
newsk->mtu = sk->user_mss;
else {
#ifdef SUBNETSARELOCAL
if ((saddr ^ daddr) & default_mask(saddr))
#else
if ((saddr ^ daddr) & dev->pa_mask)
#endif
newsk->mtu = 576 - HEADER_SIZE;
else
newsk->mtu = MAX_WINDOW;
}
/* but not bigger than device MTU */
newsk->mtu = min(newsk->mtu, dev->mtu - HEADER_SIZE);
/* this will min with what arrived in the packet */
tcp_options(newsk,skb->h.th);
buff = newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
if (buff == NULL) {
sk->err = -ENOMEM;
newsk->dead = 1;
release_sock(newsk);
kfree_skb(skb, FREE_READ);
return;
}
buff->mem_addr = buff;
buff->mem_len = MAX_SYN_SIZE;
buff->len = sizeof(struct tcphdr)+4;
buff->sk = newsk;
t1 =(struct tcphdr *) buff->data;
/* Put in the IP header and routing stuff. */
tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev,
IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
/* Something went wrong. */
if (tmp < 0) {
sk->err = tmp;
buff->free=1;
kfree_skb(buff,FREE_WRITE);
newsk->dead = 1;
release_sock(newsk);
skb->sk = sk;
kfree_skb(skb, FREE_READ);
return;
}
buff->len += tmp;
t1 =(struct tcphdr *)((char *)t1 +tmp);
memcpy(t1, skb->h.th, sizeof(*t1));
buff->h.seq = newsk->write_seq;
/* Swap the send and the receive. */
t1->dest = skb->h.th->source;
t1->source = newsk->dummy_th.source;
t1->seq = ntohl(newsk->write_seq++);
t1->ack = 1;
newsk->window = tcp_select_window(newsk);/*newsk->prot->rspace(newsk);*/
newsk->sent_seq = newsk->write_seq;
t1->window = ntohs(newsk->window);
t1->res1 = 0;
t1->res2 = 0;
t1->rst = 0;
t1->urg = 0;
t1->psh = 0;
t1->syn = 1;
t1->ack_seq = ntohl(skb->h.th->seq+1);
t1->doff = sizeof(*t1)/4+1;
ptr =(unsigned char *)(t1+1);
ptr[0] = 2;
ptr[1] = 4;
ptr[2] = ((newsk->mtu) >> 8) & 0xff;
ptr[3] =(newsk->mtu) & 0xff;
tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk);
newsk->prot->queue_xmit(newsk, dev, buff, 0);
reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME);
skb->sk = newsk;
/* Charge the sock_buff to newsk. */
sk->rmem_alloc -= skb->mem_len;
newsk->rmem_alloc += skb->mem_len;
skb_queue_tail(&sk->rqueue,skb);
sk->ack_backlog++;
release_sock(newsk);
}
static void
tcp_close(struct sock *sk, int timeout)
{
struct sk_buff *buff;
int need_reset = 0;
struct tcphdr *t1, *th;
struct proto *prot;
struct device *dev=NULL;
int tmp;
/*
* We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -