📄 tcp.c
字号:
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 + -