⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.c

📁 基于linux1.0内核的linux源码
💻 C
📖 第 1 页 / 共 5 页
字号:
   * 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 + -