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

📄 tcp.c

📁 基于linux1.0内核的linux源码
💻 C
📖 第 1 页 / 共 5 页
字号:
			if(err)
				return err;
			put_fs_long(amount,(unsigned long *)arg);
			return(0);
		}
	case SIOCATMARK:
		{
			int answ = sk->urg_data && sk->urg_seq == sk->copied_seq+1;

			err = verify_area(VERIFY_WRITE,(void *) arg,
						  sizeof(unsigned long));
			if (err)
				return err;
			put_fs_long(answ,(int *) arg);
			return(0);
		}
	case TIOCOUTQ:
		{
			unsigned long amount;

			if (sk->state == TCP_LISTEN) return(-EINVAL);
			amount = sk->prot->wspace(sk);
			err=verify_area(VERIFY_WRITE,(void *)arg,
						   sizeof(unsigned long));
			if(err)
				return err;
			put_fs_long(amount,(unsigned long *)arg);
			return(0);
		}
	default:
		return(-EINVAL);
  }
}


/* This routine computes a TCP checksum. */
unsigned short
tcp_check(struct tcphdr *th, int len,
	  unsigned long saddr, unsigned long daddr)
{     
  unsigned long sum;
   
  if (saddr == 0) saddr = my_addr();
  print_th(th);
  __asm__("\t addl %%ecx,%%ebx\n"
	  "\t adcl %%edx,%%ebx\n"
	  "\t adcl $0, %%ebx\n"
	  : "=b"(sum)
	  : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_TCP*256)
	  : "cx","bx","dx" );
   
  if (len > 3) {
	__asm__("\tclc\n"
		"1:\n"
		"\t lodsl\n"
		"\t adcl %%eax, %%ebx\n"
		"\t loop 1b\n"
		"\t adcl $0, %%ebx\n"
		: "=b"(sum) , "=S"(th)
		: "0"(sum), "c"(len/4) ,"1"(th)
		: "ax", "cx", "bx", "si" );
  }
   
  /* Convert from 32 bits to 16 bits. */
  __asm__("\t movl %%ebx, %%ecx\n"
	  "\t shrl $16,%%ecx\n"
	  "\t addw %%cx, %%bx\n"
	  "\t adcw $0, %%bx\n"
	  : "=b"(sum)
	  : "0"(sum)
	  : "bx", "cx");
   
  /* Check for an extra word. */
  if ((len & 2) != 0) {
	__asm__("\t lodsw\n"
		"\t addw %%ax,%%bx\n"
		"\t adcw $0, %%bx\n"
		: "=b"(sum), "=S"(th)
		: "0"(sum) ,"1"(th)
		: "si", "ax", "bx");
  }
   
  /* Now check for the extra byte. */
  if ((len & 1) != 0) {
	__asm__("\t lodsb\n"
		"\t movb $0,%%ah\n"
		"\t addw %%ax,%%bx\n"
		"\t adcw $0, %%bx\n"
		: "=b"(sum)
		: "0"(sum) ,"S"(th)
		: "si", "ax", "bx");
  }
   
  /* We only want the bottom 16 bits, but we never cleared the top 16. */
  return((~sum) & 0xffff);
}


void tcp_send_check(struct tcphdr *th, unsigned long saddr, 
		unsigned long daddr, int len, struct sock *sk)
{
	th->check = 0;
	th->check = tcp_check(th, len, saddr, daddr);
	return;
}

static void tcp_send_skb(struct sock *sk, struct sk_buff *skb)
{
	int size;
	struct tcphdr * th = skb->h.th;

	/* length of packet (not counting length of pre-tcp headers) */
	size = skb->len - ((unsigned char *) th - skb->data);

	/* sanity check it.. */
	if (size < sizeof(struct tcphdr) || size > skb->len) {
		printk("tcp_send_skb: bad skb (skb = %p, data = %p, th = %p, len = %lu)\n",
			skb, skb->data, th, skb->len);
		kfree_skb(skb, FREE_WRITE);
		return;
	}

	/* If we have queued a header size packet.. */
	if (size == sizeof(struct tcphdr)) {
		/* If its got a syn or fin its notionally included in the size..*/
		if(!th->syn && !th->fin) {
			printk("tcp_send_skb: attempt to queue a bogon.\n");
			kfree_skb(skb,FREE_WRITE);
			return;
		}
	}
  
	/* We need to complete and send the packet. */
	tcp_send_check(th, sk->saddr, sk->daddr, size, sk);

	skb->h.seq = ntohl(th->seq) + size - 4*th->doff;
	if (after(skb->h.seq, sk->window_seq) ||
	    (sk->retransmits && sk->timeout == TIME_WRITE) ||
	     sk->packets_out >= sk->cong_window) {
		DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n",
					sk->cong_window, sk->packets_out));
		DPRINTF((DBG_TCP, "sk->write_seq = %d, sk->window_seq = %d\n",
					sk->write_seq, sk->window_seq));
		skb->next = NULL;
		skb->magic = TCP_WRITE_QUEUE_MAGIC;
		if (sk->wback == NULL) {
			sk->wfront = skb;
		} else {
			sk->wback->next = skb;
		}
		sk->wback = skb;
		if (before(sk->window_seq, sk->wfront->h.seq) &&
		    sk->send_head == NULL &&
		    sk->ack_backlog == 0)
		  reset_timer(sk, TIME_PROBE0, sk->rto);
	} else {
		sk->sent_seq = sk->write_seq;
		sk->prot->queue_xmit(sk, skb->dev, skb, 0);
	}
}

struct sk_buff * tcp_dequeue_partial(struct sock * sk)
{
	struct sk_buff * skb;
	unsigned long flags;

	save_flags(flags);
	cli();
	skb = sk->partial;
	if (skb) {
		sk->partial = NULL;
		del_timer(&sk->partial_timer);
	}
	restore_flags(flags);
	return skb;
}

static void tcp_send_partial(struct sock *sk)
{
	struct sk_buff *skb;

	if (sk == NULL)
		return;
	while ((skb = tcp_dequeue_partial(sk)) != NULL)
		tcp_send_skb(sk, skb);
}

void tcp_enqueue_partial(struct sk_buff * skb, struct sock * sk)
{
	struct sk_buff * tmp;
	unsigned long flags;

	save_flags(flags);
	cli();
	tmp = sk->partial;
	if (tmp)
		del_timer(&sk->partial_timer);
	sk->partial = skb;
	sk->partial_timer.expires = HZ;
	sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
	sk->partial_timer.data = (unsigned long) sk;
	add_timer(&sk->partial_timer);
	restore_flags(flags);
	if (tmp)
		tcp_send_skb(sk, tmp);
}


/* This routine sends an ack and also updates the window. */
static void
tcp_send_ack(unsigned long sequence, unsigned long ack,
	     struct sock *sk,
	     struct tcphdr *th, unsigned long daddr)
{
  struct sk_buff *buff;
  struct tcphdr *t1;
  struct device *dev = NULL;
  int tmp;

  if(sk->zapped)
	return;		/* We have been reset, we may not send again */
  /*
   * We need to grab some memory, and put together an ack,
   * and then put it into the queue to be sent.
   */
  buff = sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
  if (buff == NULL) {
	/* Force it to send an ack. */
	sk->ack_backlog++;
	if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) {
		reset_timer(sk, TIME_WRITE, 10);
	}
if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
	return;
  }

  buff->mem_addr = buff;
  buff->mem_len = MAX_ACK_SIZE;
  buff->len = sizeof(struct tcphdr);
  buff->sk = sk;
  t1 =(struct tcphdr *) buff->data;

  /* Put in the IP header and routing stuff. */
  tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
  if (tmp < 0) {
  	buff->free=1;
	sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n");
	return;
  }
  buff->len += tmp;
  t1 =(struct tcphdr *)((char *)t1 +tmp);

  /* FIXME: */
  memcpy(t1, th, sizeof(*t1)); /* this should probably be removed */

  /* swap the send and the receive. */
  t1->dest = th->source;
  t1->source = th->dest;
  t1->seq = ntohl(sequence);
  t1->ack = 1;
  sk->window = tcp_select_window(sk);/*sk->prot->rspace(sk);*/
  t1->window = ntohs(sk->window);
  t1->res1 = 0;
  t1->res2 = 0;
  t1->rst = 0;
  t1->urg = 0;
  t1->syn = 0;
  t1->psh = 0;
  t1->fin = 0;
  if (ack == sk->acked_seq) {
	sk->ack_backlog = 0;
	sk->bytes_rcv = 0;
	sk->ack_timed = 0;
	if (sk->send_head == NULL && sk->wfront == NULL && sk->timeout == TIME_WRITE) 
	{
		if(sk->keepopen)
			reset_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
		else
			delete_timer(sk);
	}
  }
  t1->ack_seq = ntohl(ack);
  t1->doff = sizeof(*t1)/4;
  tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
  if (sk->debug)
  	 printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);
  sk->prot->queue_xmit(sk, dev, buff, 1);
}


/* This routine builds a generic TCP header. */
static int
tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
{

  /* FIXME: want to get rid of this. */
  memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
  th->seq = htonl(sk->write_seq);
  th->psh =(push == 0) ? 1 : 0;
  th->doff = sizeof(*th)/4;
  th->ack = 1;
  th->fin = 0;
  sk->ack_backlog = 0;
  sk->bytes_rcv = 0;
  sk->ack_timed = 0;
  th->ack_seq = htonl(sk->acked_seq);
  sk->window = tcp_select_window(sk)/*sk->prot->rspace(sk)*/;
  th->window = htons(sk->window);

  return(sizeof(*th));
}

/*
 * This routine copies from a user buffer into a socket,
 * and starts the transmit system.
 */
static int
tcp_write(struct sock *sk, unsigned char *from,
	  int len, int nonblock, unsigned flags)
{
  int copied = 0;
  int copy;
  int tmp;
  struct sk_buff *skb;
  struct sk_buff *send_tmp;
  unsigned char *buff;
  struct proto *prot;
  struct device *dev = NULL;

  DPRINTF((DBG_TCP, "tcp_write(sk=%X, from=%X, len=%d, nonblock=%d, flags=%X)\n",
					sk, from, len, nonblock, flags));

  sk->inuse=1;
  prot = sk->prot;
  while(len > 0) {
	if (sk->err) {			/* Stop on an error */
		release_sock(sk);
		if (copied) return(copied);
		tmp = -sk->err;
		sk->err = 0;
		return(tmp);
	}

	/* First thing we do is make sure that we are established. */	 
	if (sk->shutdown & SEND_SHUTDOWN) {
		release_sock(sk);
		sk->err = EPIPE;
		if (copied) return(copied);
		sk->err = 0;
		return(-EPIPE);
	}


	/* Wait for a connection to finish. */
	
	while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) {
		if (sk->err) {
			release_sock(sk);
			if (copied) return(copied);
			tmp = -sk->err;
			sk->err = 0;
			return(tmp);
		}

		if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) {
			release_sock(sk);
			DPRINTF((DBG_TCP, "tcp_write: return 1\n"));
			if (copied) return(copied);

			if (sk->err) {
				tmp = -sk->err;
				sk->err = 0;
				return(tmp);
			}

			if (sk->keepopen) {
				send_sig(SIGPIPE, current, 0);
			}
			return(-EPIPE);
		}

		if (nonblock || copied) {
			release_sock(sk);
			DPRINTF((DBG_TCP, "tcp_write: return 2\n"));
			if (copied) return(copied);
			return(-EAGAIN);
		}

		release_sock(sk);
		cli();
		if (sk->state != TCP_ESTABLISHED &&
		    sk->state != TCP_CLOSE_WAIT && sk->err == 0) {
			interruptible_sleep_on(sk->sleep);
			if (current->signal & ~current->blocked) {
				sti();
				DPRINTF((DBG_TCP, "tcp_write: return 3\n"));
				if (copied) return(copied);
				return(-ERESTARTSYS);
			}
		}
		sk->inuse = 1;
		sti();
	}

/*
 * The following code can result in copy <= if sk->mss is ever
 * decreased.  It shouldn't be.  sk->mss is min(sk->mtu, sk->max_window).
 * sk->mtu is constant once SYN processing is finished.  I.e. we
 * had better not get here until we've seen his SYN and at least one
 * valid ack.  (The SYN sets sk->mtu and the ack sets sk->max_window.)
 * But ESTABLISHED should guarantee that.  sk->max_window is by definition
 * non-decreasing.  Note that any ioctl to set user_mss must be done
 * before the exchange of SYN's.  If the initial ack from the other
 * end has a window of 0, max_window and thus mss will both be 0.
 */

	/* Now we need to check if we have a half built packet. */
	if ((skb = tcp_dequeue_partial(sk)) != NULL) {
	        int hdrlen;

	         /* IP header + TCP header */
		hdrlen = ((unsigned long)skb->h.th - (unsigned long)skb->data)
		         + sizeof(struct tcphdr);

		/* Add more stuff to the end of skb->len */
		if (!(flags & MSG_OOB)) {
			copy = min(sk->mss - (skb->len - hdrlen), len);
			/* FIXME: this is really a bug. */
			if (copy <= 0) {
			  printk("TCP: **bug**: \"copy\" <= 0!!\n");
			  copy = 0;
			}
	  
			memcpy_fromfs(skb->data + skb->len, from, copy);
			skb->len += copy;
			from += copy;
			copied += copy;
			len -= copy;
			sk->write_seq += copy;
		      }
		if ((skb->len - hdrlen) >= sk->mss ||
		    (flags & MSG_OOB) ||
		    !sk->packets_out)
			tcp_send_skb(sk, skb);
		else
			tcp_enqueue_partial(skb, sk);
		continue;
	}

	/*
	 * We also need to worry about the window.
 	 * If window < 1/2 the maximum window we've seen from this
 	 *   host, don't use it.  This is sender side
 	 *   silly window prevention, as specified in RFC1122.
 	 *   (Note that this is diffferent than earlier versions of
 	 *   SWS prevention, e.g. RFC813.).  What we actually do is 
	 *   use the whole MSS.  Since the results in the right
	 *   edge of the packet being outside the window, it will
	 *   be queued for later rather than sent.
	 */

	copy = sk->window_seq - sk->write_seq;
	if (copy <= 0 || copy < (sk->max_window >> 1) || copy > sk->mss)
		copy = sk->mss;
	if (copy > len)
		copy = len;

  /* We should really check the window here also. */
	send_tmp = NULL;
	if (copy < sk->mss && !(flags & MSG_OOB)) {
	/* We will release the socket incase we sleep here. */
	  release_sock(sk);
	  /* NB: following must be mtu, because mss can be increased.
	   * mss is always <= mtu */
	  skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header + sizeof(*skb), 0, GFP_KERNEL);
	  sk->inuse = 1;
	  send_tmp = skb;
	} else {
		/* We will release the socket incase we sleep here. */
	  release_sock(sk);
	  skb = prot->wmalloc(sk, copy + prot->max_header + sizeof(*skb), 0, GFP_KERNEL);
	  sk->inuse = 1;
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -