📄 tcp.c
字号:
} 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 shorttcp_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 voidtcp_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 inttcp_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 inttcp_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; } /* If we didn't get any memory, we need to sleep. */ if (skb == NULL) { if (nonblock /* || copied */) { release_sock(sk); DPRINTF((DBG_TCP, "tcp_write: return 4\n")); if (copied) return(copied); return(-EAGAIN); } /* FIXME: here is another race condition. */ tmp = sk->wmem_alloc; release_sock(sk); cli(); /* Again we will try to avoid it. */ if (tmp <= sk->wmem_alloc && (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 5\n")); if (copied) return(copied); return(-ERESTARTSYS); } } sk->inuse = 1; sti(); continue; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -