diff options
author | Michael I. Bushnell <mib@gnu.org> | 1995-07-12 15:42:49 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1995-07-12 15:42:49 +0000 |
commit | c7923f6aa252a29ccb4f16bd91469c9000a2bd94 (patch) | |
tree | 16980ab171a87900527e877ce7d92eebb93d24ac | |
parent | cc6600f77bdd34197cacf8e67a264dcadcb4f2d4 (diff) |
Initial revision
-rw-r--r-- | pfinet/linux-inet/af_inet.c | 1564 | ||||
-rw-r--r-- | pfinet/linux-inet/arp.c | 1294 | ||||
-rw-r--r-- | pfinet/linux-inet/dev.c | 1447 | ||||
-rw-r--r-- | pfinet/linux-inet/igmp.c | 390 | ||||
-rw-r--r-- | pfinet/linux-inet/proc.c | 251 | ||||
-rw-r--r-- | pfinet/linux-inet/raw.c | 319 | ||||
-rw-r--r-- | pfinet/linux-inet/route.c | 672 | ||||
-rw-r--r-- | pfinet/linux-inet/sock.h | 316 | ||||
-rw-r--r-- | pfinet/linux-inet/tcp.c | 5100 | ||||
-rw-r--r-- | pfinet/linux-inet/udp.c | 735 |
10 files changed, 12088 insertions, 0 deletions
diff --git a/pfinet/linux-inet/af_inet.c b/pfinet/linux-inet/af_inet.c new file mode 100644 index 00000000..a82b59c1 --- /dev/null +++ b/pfinet/linux-inet/af_inet.c @@ -0,0 +1,1564 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * AF_INET protocol family socket handler. + * + * Version: @(#)af_inet.c (from sock.c) 1.0.17 06/02/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Florian La Roche, <flla@stud.uni-sb.de> + * Alan Cox, <A.Cox@swansea.ac.uk> + * + * Changes (see also sock.c) + * + * A.N.Kuznetsov : Socket death error in accept(). + * John Richardson : Fix non blocking error in connect() + * so sockets that fail to connect + * don't return -EINPROGRESS. + * Alan Cox : Asynchronous I/O support + * Alan Cox : Keep correct socket pointer on sock structures + * when accept() ed + * Alan Cox : Semantics of SO_LINGER aren't state moved + * to close when you look carefully. With + * this fixed and the accept bug fixed + * some RPC stuff seems happier. + * Niibe Yutaka : 4.4BSD style write async I/O + * Alan Cox, + * Tony Gale : Fixed reuse semantics. + * Alan Cox : bind() shouldn't abort existing but dead + * sockets. Stops FTP netin:.. I hope. + * Alan Cox : bind() works correctly for RAW sockets. Note + * that FreeBSD at least is broken in this respect + * so be careful with compatibility tests... + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/interrupt.h> + +#include <asm/segment.h> +#include <asm/system.h> + +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "ip.h" +#include "protocol.h" +#include "arp.h" +#include "rarp.h" +#include "route.h" +#include "tcp.h" +#include "udp.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "raw.h" +#include "icmp.h" + +#define min(a,b) ((a)<(b)?(a):(b)) + +extern struct proto packet_prot; + + +/* + * See if a socket number is in use. + */ + +static int sk_inuse(struct proto *prot, int num) +{ + struct sock *sk; + + for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )]; + sk != NULL; sk=sk->next) + { + if (sk->num == num) + return(1); + } + return(0); +} + + +/* + * Pick a new socket number + */ + +unsigned short get_new_socknum(struct proto *prot, unsigned short base) +{ + static int start=0; + + /* + * Used to cycle through the port numbers so the + * chances of a confused connection drop. + */ + + int i, j; + int best = 0; + int size = 32767; /* a big num. */ + struct sock *sk; + + if (base == 0) + base = PROT_SOCK+1+(start % 1024); + if (base <= PROT_SOCK) + { + base += PROT_SOCK+(start % 1024); + } + + /* Now look through the entire array and try to find an empty ptr. */ + for(i=0; i < SOCK_ARRAY_SIZE; i++) + { + j = 0; + sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)]; + while(sk != NULL) + { + sk = sk->next; + j++; + } + if (j == 0) + { + start =(i+1+start )%1024; + return(i+base+1); + } + if (j < size) + { + best = i; + size = j; + } + } + + /* Now make sure the one we want is not in use. */ + + while(sk_inuse(prot, base +best+1)) + { + best += SOCK_ARRAY_SIZE; + } + return(best+base+1); +} + +/* + * Add a socket into the socket tables by number. + */ + +void put_sock(unsigned short num, struct sock *sk) +{ + struct sock *sk1; + struct sock *sk2; + int mask; + unsigned long flags; + + sk->num = num; + sk->next = NULL; + num = num &(SOCK_ARRAY_SIZE -1); + + /* We can't have an interrupt re-enter here. */ + save_flags(flags); + cli(); + + sk->prot->inuse += 1; + if (sk->prot->highestinuse < sk->prot->inuse) + sk->prot->highestinuse = sk->prot->inuse; + + if (sk->prot->sock_array[num] == NULL) + { + sk->prot->sock_array[num] = sk; + restore_flags(flags); + return; + } + restore_flags(flags); + for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) + { + if ((mask & sk->saddr) && + (mask & sk->saddr) != (mask & 0xffffffff)) + { + mask = mask << 8; + break; + } + } + cli(); + sk1 = sk->prot->sock_array[num]; + for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) + { + if (!(sk2->saddr & mask)) + { + if (sk2 == sk1) + { + sk->next = sk->prot->sock_array[num]; + sk->prot->sock_array[num] = sk; + sti(); + return; + } + sk->next = sk2; + sk1->next= sk; + sti(); + return; + } + sk1 = sk2; + } + + /* Goes at the end. */ + sk->next = NULL; + sk1->next = sk; + sti(); +} + +/* + * Remove a socket from the socket tables. + */ + +static void remove_sock(struct sock *sk1) +{ + struct sock *sk2; + unsigned long flags; + + if (!sk1->prot) + { + printk("sock.c: remove_sock: sk1->prot == NULL\n"); + return; + } + + /* We can't have this changing out from under us. */ + save_flags(flags); + cli(); + sk2 = sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)]; + if (sk2 == sk1) + { + sk1->prot->inuse -= 1; + sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)] = sk1->next; + restore_flags(flags); + return; + } + + while(sk2 && sk2->next != sk1) + { + sk2 = sk2->next; + } + + if (sk2) + { + sk1->prot->inuse -= 1; + sk2->next = sk1->next; + restore_flags(flags); + return; + } + restore_flags(flags); +} + +/* + * Destroy an AF_INET socket + */ + +void destroy_sock(struct sock *sk) +{ + struct sk_buff *skb; + + sk->inuse = 1; /* just to be safe. */ + + /* In case it's sleeping somewhere. */ + if (!sk->dead) + sk->write_space(sk); + + remove_sock(sk); + + /* Now we can no longer get new packets. */ + delete_timer(sk); + /* Nor send them */ + del_timer(&sk->retransmit_timer); + + while ((skb = tcp_dequeue_partial(sk)) != NULL) { + IS_SKB(skb); + kfree_skb(skb, FREE_WRITE); + } + + /* Cleanup up the write buffer. */ + while((skb = skb_dequeue(&sk->write_queue)) != NULL) { + IS_SKB(skb); + kfree_skb(skb, FREE_WRITE); + } + + /* + * Don't discard received data until the user side kills its + * half of the socket. + */ + + if (sk->dead) + { + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + { + /* + * This will take care of closing sockets that were + * listening and didn't accept everything. + */ + if (skb->sk != NULL && skb->sk != sk) + { + IS_SKB(skb); + skb->sk->dead = 1; + skb->sk->prot->close(skb->sk, 0); + } + IS_SKB(skb); + kfree_skb(skb, FREE_READ); + } + } + + /* Now we need to clean up the send head. */ + cli(); + for(skb = sk->send_head; skb != NULL; ) + { + struct sk_buff *skb2; + + /* + * We need to remove skb from the transmit queue, + * or maybe the arp queue. + */ + if (skb->next && skb->prev) { +/* printk("destroy_sock: unlinked skb\n");*/ + IS_SKB(skb); + skb_unlink(skb); + } + skb->dev = NULL; + skb2 = skb->link3; + kfree_skb(skb, FREE_WRITE); + skb = skb2; + } + sk->send_head = NULL; + sti(); + + /* And now the backlog. */ + while((skb=skb_dequeue(&sk->back_log))!=NULL) + { + /* this should never happen. */ +/* printk("cleaning back_log\n");*/ + kfree_skb(skb, FREE_READ); + } + + /* Now if it has a half accepted/ closed socket. */ + if (sk->pair) + { + sk->pair->dead = 1; + sk->pair->prot->close(sk->pair, 0); + sk->pair = NULL; + } + + /* + * Now if everything is gone we can free the socket + * structure, otherwise we need to keep it around until + * everything is gone. + */ + + if (sk->dead && sk->rmem_alloc == 0 && sk->wmem_alloc == 0) + { + kfree_s((void *)sk,sizeof(*sk)); + } + else + { + /* this should never happen. */ + /* actually it can if an ack has just been sent. */ + sk->destroy = 1; + sk->ack_backlog = 0; + sk->inuse = 0; + reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME); + } +} + +/* + * The routines beyond this point handle the behaviour of an AF_INET + * socket object. Mostly it punts to the subprotocols of IP to do + * the work. + */ + +static int inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + + switch(cmd) + { + case F_SETOWN: + /* + * This is a little restrictive, but it's the only + * way to make sure that you can't send a sigurg to + * another process. + */ + if (!suser() && current->pgrp != -arg && + current->pid != arg) return(-EPERM); + sk->proc = arg; + return(0); + case F_GETOWN: + return(sk->proc); + default: + return(-EINVAL); + } +} + +/* + * Set socket options on an inet socket. + */ + +static int inet_setsockopt(struct socket *sock, int level, int optname, + char *optval, int optlen) +{ + struct sock *sk = (struct sock *) sock->data; + if (level == SOL_SOCKET) + return sock_setsockopt(sk,level,optname,optval,optlen); + if (sk->prot->setsockopt==NULL) + return(-EOPNOTSUPP); + else + return sk->prot->setsockopt(sk,level,optname,optval,optlen); +} + +/* + * Get a socket option on an AF_INET socket. + */ + +static int inet_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + struct sock *sk = (struct sock *) sock->data; + if (level == SOL_SOCKET) + return sock_getsockopt(sk,level,optname,optval,optlen); + if(sk->prot->getsockopt==NULL) + return(-EOPNOTSUPP); + else + return sk->prot->getsockopt(sk,level,optname,optval,optlen); +} + +/* + * Automatically bind an unbound socket. + */ + +static int inet_autobind(struct sock *sk) +{ + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + return 0; +} + +/* + * Move a socket into listening state. + */ + +static int inet_listen(struct socket *sock, int backlog) +{ + struct sock *sk = (struct sock *) sock->data; + + if(inet_autobind(sk)!=0) + return -EAGAIN; + + /* We might as well re use these. */ + /* + * note that the backlog is "unsigned char", so truncate it + * somewhere. We might as well truncate it to what everybody + * else does.. + */ + if (backlog > 5) + backlog = 5; + sk->max_ack_backlog = backlog; + if (sk->state != TCP_LISTEN) + { + sk->ack_backlog = 0; + sk->state = TCP_LISTEN; + } + return(0); +} + +/* + * Default callbacks for user INET sockets. These just wake up + * the user owning the socket. + */ + +static void def_callback1(struct sock *sk) +{ + if(!sk->dead) + wake_up_interruptible(sk->sleep); +} + +static void def_callback2(struct sock *sk,int len) +{ + if(!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 1); + } +} + +static void def_callback3(struct sock *sk) +{ + if(!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 2); + } +} + +/* + * Create an inet socket. + * + * FIXME: Gcc would generate much better code if we set the parameters + * up in in-memory structure order. Gcc68K even more so + */ + +static int inet_create(struct socket *sock, int protocol) +{ + struct sock *sk; + struct proto *prot; + int err; + + sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL); + if (sk == NULL) + return(-ENOBUFS); + sk->num = 0; + sk->reuse = 0; + switch(sock->type) + { + case SOCK_STREAM: + case SOCK_SEQPACKET: + if (protocol && protocol != IPPROTO_TCP) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + protocol = IPPROTO_TCP; + sk->no_check = TCP_NO_CHECK; + prot = &tcp_prot; + break; + + case SOCK_DGRAM: + if (protocol && protocol != IPPROTO_UDP) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + protocol = IPPROTO_UDP; + sk->no_check = UDP_NO_CHECK; + prot=&udp_prot; + break; + + case SOCK_RAW: + if (!suser()) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPERM); + } + if (!protocol) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + prot = &raw_prot; + sk->reuse = 1; + sk->no_check = 0; /* + * Doesn't matter no checksum is + * performed anyway. + */ + sk->num = protocol; + break; + + case SOCK_PACKET: + if (!suser()) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPERM); + } + if (!protocol) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + prot = &packet_prot; + sk->reuse = 1; + sk->no_check = 0; /* Doesn't matter no checksum is + * performed anyway. + */ + sk->num = protocol; + break; + + default: + kfree_s((void *)sk, sizeof(*sk)); + return(-ESOCKTNOSUPPORT); + } + sk->socket = sock; +#ifdef CONFIG_TCP_NAGLE_OFF + sk->nonagle = 1; +#else + sk->nonagle = 0; +#endif + sk->type = sock->type; + sk->stamp.tv_sec=0; + sk->protocol = protocol; + sk->wmem_alloc = 0; + sk->rmem_alloc = 0; + sk->sndbuf = SK_WMEM_MAX; + sk->rcvbuf = SK_RMEM_MAX; + sk->pair = NULL; + sk->opt = NULL; + sk->write_seq = 0; + sk->acked_seq = 0; + sk->copied_seq = 0; + sk->fin_seq = 0; + sk->urg_seq = 0; + sk->urg_data = 0; + sk->proc = 0; + sk->rtt = 0; /*TCP_WRITE_TIME << 3;*/ + sk->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/ + sk->mdev = 0; + sk->backoff = 0; + sk->packets_out = 0; + sk->cong_window = 1; /* start with only sending one packet at a time. */ + sk->cong_count = 0; + sk->ssthresh = 0; + sk->max_window = 0; + sk->urginline = 0; + sk->intr = 0; + sk->linger = 0; + sk->destroy = 0; + sk->priority = 1; + sk->shutdown = 0; + sk->keepopen = 0; + sk->zapped = 0; + sk->done = 0; + sk->ack_backlog = 0; + sk->window = 0; + sk->bytes_rcv = 0; + sk->state = TCP_CLOSE; + sk->dead = 0; + sk->ack_timed = 0; + sk->partial = NULL; + sk->user_mss = 0; + sk->debug = 0; + + /* this is how many unacked bytes we will accept for this socket. */ + sk->max_unacked = 2048; /* needs to be at most 2 full packets. */ + + /* how many packets we should send before forcing an ack. + if this is set to zero it is the same as sk->delay_acks = 0 */ + sk->max_ack_backlog = 0; + sk->inuse = 0; + sk->delay_acks = 0; + skb_queue_head_init(&sk->write_queue); + skb_queue_head_init(&sk->receive_queue); + sk->mtu = 576; + sk->prot = prot; + sk->sleep = sock->wait; + sk->daddr = 0; + sk->saddr = 0 /* ip_my_addr() */; + sk->err = 0; + sk->next = NULL; + sk->pair = NULL; + sk->send_tail = NULL; + sk->send_head = NULL; + sk->timeout = 0; + sk->broadcast = 0; + sk->localroute = 0; + init_timer(&sk->timer); + init_timer(&sk->retransmit_timer); + sk->timer.data = (unsigned long)sk; + sk->timer.function = &net_timer; + skb_queue_head_init(&sk->back_log); + sk->blog = 0; + sock->data =(void *) sk; + sk->dummy_th.doff = sizeof(sk->dummy_th)/4; + sk->dummy_th.res1=0; + sk->dummy_th.res2=0; + sk->dummy_th.urg_ptr = 0; + sk->dummy_th.fin = 0; + sk->dummy_th.syn = 0; + sk->dummy_th.rst = 0; + sk->dummy_th.psh = 0; + sk->dummy_th.ack = 0; + sk->dummy_th.urg = 0; + sk->dummy_th.dest = 0; + sk->ip_tos=0; + sk->ip_ttl=64; +#ifdef CONFIG_IP_MULTICAST + sk->ip_mc_loop=1; + sk->ip_mc_ttl=1; + *sk->ip_mc_name=0; + sk->ip_mc_list=NULL; +#endif + + sk->state_change = def_callback1; + sk->data_ready = def_callback2; + sk->write_space = def_callback3; + sk->error_report = def_callback1; + + if (sk->num) + { + /* + * It assumes that any protocol which allows + * the user to assign a number at socket + * creation time automatically + * shares. + */ + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + if (sk->prot->init) + { + err = sk->prot->init(sk); + if (err != 0) + { + destroy_sock(sk); + return(err); + } + } + return(0); +} + + +/* + * Duplicate a socket. + */ + +static int inet_dup(struct socket *newsock, struct socket *oldsock) +{ + return(inet_create(newsock,((struct sock *)(oldsock->data))->protocol)); +} + +/* + * Return 1 if we still have things to send in our buffers. + */ +static inline int closing(struct sock * sk) +{ + switch (sk->state) { + case TCP_FIN_WAIT1: + case TCP_CLOSING: + case TCP_LAST_ACK: + return 1; + } + return 0; +} + + +/* + * The peer socket should always be NULL (or else). When we call this + * function we are destroying the object and from then on nobody + * should refer to it. + */ + +static int inet_release(struct socket *sock, struct socket *peer) +{ + struct sock *sk = (struct sock *) sock->data; + if (sk == NULL) + return(0); + + sk->state_change(sk); + + /* Start closing the connection. This may take a while. */ + +#ifdef CONFIG_IP_MULTICAST + /* Applications forget to leave groups before exiting */ + ip_mc_drop_socket(sk); +#endif + /* + * If linger is set, we don't return until the close + * is complete. Other wise we return immediately. The + * actually closing is done the same either way. + * + * If the close is due to the process exiting, we never + * linger.. + */ + + if (sk->linger == 0 || (current->flags & PF_EXITING)) + { + sk->prot->close(sk,0); + sk->dead = 1; + } + else + { + sk->prot->close(sk, 0); + cli(); + if (sk->lingertime) + current->timeout = jiffies + HZ*sk->lingertime; + while(closing(sk) && current->timeout>0) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + break; +#if 0 + /* not working now - closes can't be restarted */ + sti(); + current->timeout=0; + return(-ERESTARTSYS); +#endif + } + } + current->timeout=0; + sti(); + sk->dead = 1; + } + sk->inuse = 1; + + /* This will destroy it. */ + release_sock(sk); + sock->data = NULL; + sk->socket = NULL; + return(0); +} + + +/* this needs to be changed to disallow + the rebinding of sockets. What error + should it return? */ + +static int inet_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len) +{ + struct sockaddr_in *addr=(struct sockaddr_in *)uaddr; + struct sock *sk=(struct sock *)sock->data, *sk2; + unsigned short snum = 0 /* Stoopid compiler.. this IS ok */; + int chk_addr_ret; + + /* check this error. */ + if (sk->state != TCP_CLOSE) + return(-EIO); + if(addr_len<sizeof(struct sockaddr_in)) + return -EINVAL; + + if(sock->type != SOCK_RAW) + { + if (sk->num != 0) + return(-EINVAL); + + snum = ntohs(addr->sin_port); + + /* + * We can't just leave the socket bound wherever it is, it might + * be bound to a privileged port. However, since there seems to + * be a bug here, we will leave it if the port is not privileged. + */ + if (snum == 0) + { + snum = get_new_socknum(sk->prot, 0); + } + if (snum < PROT_SOCK && !suser()) + return(-EACCES); + } + + chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr); + if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST) + return(-EADDRNOTAVAIL); /* Source address MUST be ours! */ + + if (chk_addr_ret || addr->sin_addr.s_addr == 0) + sk->saddr = addr->sin_addr.s_addr; + + if(sock->type != SOCK_RAW) + { + /* Make sure we are allowed to bind here. */ + cli(); + for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]; + sk2 != NULL; sk2 = sk2->next) + { + /* should be below! */ + if (sk2->num != snum) + continue; + if (!sk->reuse) + { + sti(); + return(-EADDRINUSE); + } + + if (sk2->num != snum) + continue; /* more than one */ + if (sk2->saddr != sk->saddr) + continue; /* socket per slot ! -FB */ + if (!sk2->reuse || sk2->state==TCP_LISTEN) + { + sti(); + return(-EADDRINUSE); + } + } + sti(); + + remove_sock(sk); + put_sock(snum, sk); + sk->dummy_th.source = ntohs(sk->num); + sk->daddr = 0; + sk->dummy_th.dest = 0; + } + return(0); +} + +/* + * Handle sk->err properly. The cli/sti matter. + */ + +static int inet_error(struct sock *sk) +{ + unsigned long flags; + int err; + save_flags(flags); + cli(); + err=sk->err; + sk->err=0; + restore_flags(flags); + return -err; +} + +/* + * Connect to a remote host. There is regrettably still a little + * TCP 'magic' in here. + */ + +static int inet_connect(struct socket *sock, struct sockaddr * uaddr, + int addr_len, int flags) +{ + struct sock *sk=(struct sock *)sock->data; + int err; + sock->conn = NULL; + + if (sock->state == SS_CONNECTING && tcp_connected(sk->state)) + { + sock->state = SS_CONNECTED; + /* Connection completing after a connect/EINPROGRESS/select/connect */ + return 0; /* Rock and roll */ + } + + if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK)) + return -EALREADY; /* Connecting is currently in progress */ + + if (sock->state != SS_CONNECTING) + { + /* We may need to bind the socket. */ + if(inet_autobind(sk)!=0) + return(-EAGAIN); + if (sk->prot->connect == NULL) + return(-EOPNOTSUPP); + err = sk->prot->connect(sk, (struct sockaddr_in *)uaddr, addr_len); + if (err < 0) + return(err); + sock->state = SS_CONNECTING; + } + + if (sk->state > TCP_FIN_WAIT2 && sock->state==SS_CONNECTING) + { + sock->state=SS_UNCONNECTED; + cli(); + err=sk->err; + sk->err=0; + sti(); + return -err; + } + + if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK)) + return(-EINPROGRESS); + + cli(); /* avoid the race condition */ + while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + return(-ERESTARTSYS); + } + /* This fixes a nasty in the tcp/ip code. There is a hideous hassle with + icmp error packets wanting to close a tcp or udp socket. */ + if(sk->err && sk->protocol == IPPROTO_TCP) + { + sti(); + sock->state = SS_UNCONNECTED; + err = -sk->err; + sk->err=0; + return err; /* set by tcp_err() */ + } + } + sti(); + sock->state = SS_CONNECTED; + + if (sk->state != TCP_ESTABLISHED && sk->err) + { + sock->state = SS_UNCONNECTED; + err=sk->err; + sk->err=0; + return(-err); + } + return(0); +} + + +static int inet_socketpair(struct socket *sock1, struct socket *sock2) +{ + return(-EOPNOTSUPP); +} + + +/* + * Accept a pending connection. The TCP layer now gives BSD semantics. + */ + +static int inet_accept(struct socket *sock, struct socket *newsock, int flags) +{ + struct sock *sk1, *sk2; + int err; + + sk1 = (struct sock *) sock->data; + + /* + * We've been passed an extra socket. + * We need to free it up because the tcp module creates + * its own when it accepts one. + */ + if (newsock->data) + { + struct sock *sk=(struct sock *)newsock->data; + newsock->data=NULL; + sk->dead = 1; + destroy_sock(sk); + } + + if (sk1->prot->accept == NULL) + return(-EOPNOTSUPP); + + /* Restore the state if we have been interrupted, and then returned. */ + if (sk1->pair != NULL ) + { + sk2 = sk1->pair; + sk1->pair = NULL; + } + else + { + sk2 = sk1->prot->accept(sk1,flags); + if (sk2 == NULL) + { + if (sk1->err <= 0) + printk("Warning sock.c:sk1->err <= 0. Returning non-error.\n"); + err=sk1->err; + sk1->err=0; + return(-err); + } + } + newsock->data = (void *)sk2; + sk2->sleep = newsock->wait; + sk2->socket = newsock; + newsock->conn = NULL; + if (flags & O_NONBLOCK) + return(0); + + cli(); /* avoid the race. */ + while(sk2->state == TCP_SYN_RECV) + { + interruptible_sleep_on(sk2->sleep); + if (current->signal & ~current->blocked) + { + sti(); + sk1->pair = sk2; + sk2->sleep = NULL; + sk2->socket=NULL; + newsock->data = NULL; + return(-ERESTARTSYS); + } + } + sti(); + + if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) + { + err = -sk2->err; + sk2->err=0; + sk2->dead=1; /* ANK */ + destroy_sock(sk2); + newsock->data = NULL; + return(err); + } + newsock->state = SS_CONNECTED; + return(0); +} + + +/* + * This does both peername and sockname. + */ + +static int inet_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_in *sin=(struct sockaddr_in *)uaddr; + struct sock *sk; + + sin->sin_family = AF_INET; + sk = (struct sock *) sock->data; + if (peer) + { + if (!tcp_connected(sk->state)) + return(-ENOTCONN); + sin->sin_port = sk->dummy_th.dest; + sin->sin_addr.s_addr = sk->daddr; + } + else + { + sin->sin_port = sk->dummy_th.source; + if (sk->saddr == 0) + sin->sin_addr.s_addr = ip_my_addr(); + else + sin->sin_addr.s_addr = sk->saddr; + } + *uaddr_len = sizeof(*sin); + return(0); +} + + +/* + * The assorted BSD I/O operations + */ + +static int inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags, struct sockaddr *sin, int *addr_len ) +{ + struct sock *sk = (struct sock *) sock->data; + + if (sk->prot->recvfrom == NULL) + return(-EOPNOTSUPP); + if(sk->err) + return inet_error(sk); + /* We may need to bind the socket. */ + if(inet_autobind(sk)!=0) + return(-EAGAIN); + return(sk->prot->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags, + (struct sockaddr_in*)sin, addr_len)); +} + + +static int inet_recv(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags) +{ + /* BSD explicitly states these are the same - so we do it this way to be sure */ + return inet_recvfrom(sock,ubuf,size,noblock,flags,NULL,NULL); +} + +static int inet_read(struct socket *sock, char *ubuf, int size, int noblock) +{ + struct sock *sk = (struct sock *) sock->data; + + if(sk->err) + return inet_error(sk); + /* We may need to bind the socket. */ + if(inet_autobind(sk)) + return(-EAGAIN); + return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock, 0)); +} + +static int inet_send(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags) +{ + struct sock *sk = (struct sock *) sock->data; + if (sk->shutdown & SEND_SHUTDOWN) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + if(sk->err) + return inet_error(sk); + /* We may need to bind the socket. */ + if(inet_autobind(sk)!=0) + return(-EAGAIN); + return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags)); +} + +static int inet_write(struct socket *sock, char *ubuf, int size, int noblock) +{ + return inet_send(sock,ubuf,size,noblock,0); +} + +static int inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags, struct sockaddr *sin, int addr_len) +{ + struct sock *sk = (struct sock *) sock->data; + if (sk->shutdown & SEND_SHUTDOWN) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + if (sk->prot->sendto == NULL) + return(-EOPNOTSUPP); + if(sk->err) + return inet_error(sk); + /* We may need to bind the socket. */ + if(inet_autobind(sk)!=0) + return -EAGAIN; + return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, + (struct sockaddr_in *)sin, addr_len)); +} + + +static int inet_shutdown(struct socket *sock, int how) +{ + struct sock *sk=(struct sock*)sock->data; + + /* + * This should really check to make sure + * the socket is a TCP socket. (WHY AC...) + */ + how++; /* maps 0->1 has the advantage of making bit 1 rcvs and + 1->2 bit 2 snds. + 2->3 */ + if ((how & ~SHUTDOWN_MASK) || how==0) /* MAXINT->0 */ + return(-EINVAL); + if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED) + sock->state = SS_CONNECTED; + if (!tcp_connected(sk->state)) + return(-ENOTCONN); + sk->shutdown |= how; + if (sk->prot->shutdown) + sk->prot->shutdown(sk, how); + return(0); +} + + +static int inet_select(struct socket *sock, int sel_type, select_table *wait ) +{ + struct sock *sk=(struct sock *) sock->data; + if (sk->prot->select == NULL) + { + return(0); + } + return(sk->prot->select(sk, sel_type, wait)); +} + +/* + * ioctl() calls you can issue on an INET socket. Most of these are + * device configuration and stuff and very rarely used. Some ioctls + * pass on to the socket itself. + * + * NOTE: I like the idea of a module for the config stuff. ie ifconfig + * loads the devconfigure module does its configuring and unloads it. + * There's a good 20K of config code hanging around the kernel. + */ + +static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk=(struct sock *)sock->data; + int err; + + switch(cmd) + { + case FIOSETOWN: + case SIOCSPGRP: + err=verify_area(VERIFY_READ,(int *)arg,sizeof(long)); + if(err) + return err; + sk->proc = get_fs_long((int *) arg); + return(0); + case FIOGETOWN: + case SIOCGPGRP: + err=verify_area(VERIFY_WRITE,(void *) arg, sizeof(long)); + if(err) + return err; + put_fs_long(sk->proc,(int *)arg); + return(0); + case SIOCGSTAMP: + if(sk->stamp.tv_sec==0) + return -ENOENT; + err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval)); + if(err) + return err; + memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval)); + return 0; + case SIOCADDRT: case SIOCADDRTOLD: + case SIOCDELRT: case SIOCDELRTOLD: + return(ip_rt_ioctl(cmd,(void *) arg)); + case SIOCDARP: + case SIOCGARP: + case SIOCSARP: + return(arp_ioctl(cmd,(void *) arg)); +#ifdef CONFIG_INET_RARP + case SIOCDRARP: + case SIOCGRARP: + case SIOCSRARP: + return(rarp_ioctl(cmd,(void *) arg)); +#endif + case SIOCGIFCONF: + case SIOCGIFFLAGS: + case SIOCSIFFLAGS: + case SIOCGIFADDR: + case SIOCSIFADDR: + +/* begin multicast support change */ + case SIOCADDMULTI: + case SIOCDELMULTI: +/* end multicast support change */ + + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + case SIOCGIFMEM: + case SIOCSIFMEM: + case SIOCGIFMTU: + case SIOCSIFMTU: + case SIOCSIFLINK: + case SIOCGIFHWADDR: + case SIOCSIFHWADDR: + case OLD_SIOCGIFHWADDR: + case SIOCSIFMAP: + case SIOCGIFMAP: + case SIOCSIFSLAVE: + case SIOCGIFSLAVE: + return(dev_ioctl(cmd,(void *) arg)); + + default: + if ((cmd >= SIOCDEVPRIVATE) && + (cmd <= (SIOCDEVPRIVATE + 15))) + return(dev_ioctl(cmd,(void *) arg)); + + if (sk->prot->ioctl==NULL) + return(-EINVAL); + return(sk->prot->ioctl(sk, cmd, arg)); + } + /*NOTREACHED*/ + return(0); +} + +/* + * This routine must find a socket given a TCP or UDP header. + * Everything is assumed to be in net order. + * + * We give priority to more closely bound ports: if some socket + * is bound to a particular foreign address, it will get the packet + * rather than somebody listening to any address.. + */ + +struct sock *get_sock(struct proto *prot, unsigned short num, + unsigned long raddr, + unsigned short rnum, unsigned long laddr) +{ + struct sock *s; + struct sock *result = NULL; + int badness = -1; + unsigned short hnum; + + hnum = ntohs(num); + + /* + * SOCK_ARRAY_SIZE must be a power of two. This will work better + * than a prime unless 3 or more sockets end up using the same + * array entry. This should not be a problem because most + * well known sockets don't overlap that much, and for + * the other ones, we can just be careful about picking our + * socket number when we choose an arbitrary one. + */ + + for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)]; + s != NULL; s = s->next) + { + int score = 0; + + if (s->num != hnum) + continue; + + if(s->dead && (s->state == TCP_CLOSE)) + continue; + /* local address matches? */ + if (s->saddr) { + if (s->saddr != laddr) + continue; + score++; + } + /* remote address matches? */ + if (s->daddr) { + if (s->daddr != raddr) + continue; + score++; + } + /* remote port matches? */ + if (s->dummy_th.dest) { + if (s->dummy_th.dest != rnum) + continue; + score++; + } + /* perfect match? */ + if (score == 3) + return s; + /* no, check if this is the best so far.. */ + if (score <= badness) + continue; + result = s; + badness = score; + } + return result; +} + +/* + * Deliver a datagram to raw sockets. + */ + +struct sock *get_sock_raw(struct sock *sk, + unsigned short num, + unsigned long raddr, + unsigned long laddr) +{ + struct sock *s; + + s=sk; + + for(; s != NULL; s = s->next) + { + if (s->num != num) + continue; + if(s->dead && (s->state == TCP_CLOSE)) + continue; + if(s->daddr && s->daddr!=raddr) + continue; + if(s->saddr && s->saddr!=laddr) + continue; + return(s); + } + return(NULL); +} + +#ifdef CONFIG_IP_MULTICAST +/* + * Deliver a datagram to broadcast/multicast sockets. + */ + +struct sock *get_sock_mcast(struct sock *sk, + unsigned short num, + unsigned long raddr, + unsigned short rnum, unsigned long laddr) +{ + struct sock *s; + unsigned short hnum; + + hnum = ntohs(num); + + /* + * SOCK_ARRAY_SIZE must be a power of two. This will work better + * than a prime unless 3 or more sockets end up using the same + * array entry. This should not be a problem because most + * well known sockets don't overlap that much, and for + * the other ones, we can just be careful about picking our + * socket number when we choose an arbitrary one. + */ + + s=sk; + + for(; s != NULL; s = s->next) + { + if (s->num != hnum) + continue; + if(s->dead && (s->state == TCP_CLOSE)) + continue; + if(s->daddr && s->daddr!=raddr) + continue; + if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) + continue; + if(s->saddr && s->saddr!=laddr) + continue; + return(s); + } + return(NULL); +} + +#endif + +static struct proto_ops inet_proto_ops = { + AF_INET, + + inet_create, + inet_dup, + inet_release, + inet_bind, + inet_connect, + inet_socketpair, + inet_accept, + inet_getname, + inet_read, + inet_write, + inet_select, + inet_ioctl, + inet_listen, + inet_send, + inet_recv, + inet_sendto, + inet_recvfrom, + inet_shutdown, + inet_setsockopt, + inet_getsockopt, + inet_fcntl, +}; + +extern unsigned long seq_offset; + +/* + * Called by socket.c on kernel startup. + */ + +void inet_proto_init(struct net_proto *pro) +{ + struct inet_protocol *p; + int i; + + + printk("Swansea University Computer Society TCP/IP for NET3.019\n"); + + /* + * Tell SOCKET that we are alive... + */ + + (void) sock_register(inet_proto_ops.family, &inet_proto_ops); + + seq_offset = CURRENT_TIME*250; + + /* + * Add all the protocols. + */ + + for(i = 0; i < SOCK_ARRAY_SIZE; i++) + { + tcp_prot.sock_array[i] = NULL; + udp_prot.sock_array[i] = NULL; + raw_prot.sock_array[i] = NULL; + } + tcp_prot.inuse = 0; + tcp_prot.highestinuse = 0; + udp_prot.inuse = 0; + udp_prot.highestinuse = 0; + raw_prot.inuse = 0; + raw_prot.highestinuse = 0; + + printk("IP Protocols: "); + for(p = inet_protocol_base; p != NULL;) + { + struct inet_protocol *tmp = (struct inet_protocol *) p->next; + inet_add_protocol(p); + printk("%s%s",p->name,tmp?", ":"\n"); + p = tmp; + } + /* + * Set the ARP module up + */ + arp_init(); + /* + * Set the IP module up + */ + ip_init(); +} + diff --git a/pfinet/linux-inet/arp.c b/pfinet/linux-inet/arp.c new file mode 100644 index 00000000..f793b921 --- /dev/null +++ b/pfinet/linux-inet/arp.c @@ -0,0 +1,1294 @@ +/* linux/net/inet/arp.c + * + * Copyright (C) 1994 by Florian La Roche + * + * This module implements the Address Resolution Protocol ARP (RFC 826), + * which is used to convert IP addresses (or in the future maybe other + * high-level addresses into a low-level hardware address (like an Ethernet + * address). + * + * FIXME: + * Experiment with better retransmit timers + * Clean up the timer deletions + * If you create a proxy entry set your interface address to the address + * and then delete it, proxies may get out of sync with reality - check this + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * Fixes: + * Alan Cox : Removed the ethernet assumptions in Florian's code + * Alan Cox : Fixed some small errors in the ARP logic + * Alan Cox : Allow >4K in /proc + * Alan Cox : Make ARP add its own protocol entry + * + * Ross Martin : Rewrote arp_rcv() and arp_get_info() + * Stephen Henson : Add AX25 support to arp_get_info() + * Alan Cox : Drop data when a device is downed. + * Alan Cox : Use init_timer(). + * Alan Cox : Double lock fixes. + * Martin Seine : Move the arphdr structure + * to if_arp.h for compatibility. + * with BSD based programs. + * Andrew Tridgell : Added ARP netmask code and + * re-arranged proxy handling. + * Alan Cox : Changed to use notifiers. + * Niibe Yutaka : Reply for this device or proxies only. + * Alan Cox : Don't proxy across hardware types! + */ + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/config.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/in.h> +#include <linux/mm.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <stdarg.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "ip.h" +#include "route.h" +#include "protocol.h" +#include "tcp.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "arp.h" +#ifdef CONFIG_AX25 +#include "ax25.h" +#endif + + +/* + * This structure defines the ARP mapping cache. As long as we make changes + * in this structure, we keep interrupts of. But normally we can copy the + * hardware address and the device pointer in a local variable and then make + * any "long calls" to send a packet out. + */ + +struct arp_table +{ + struct arp_table *next; /* Linked entry list */ + unsigned long last_used; /* For expiry */ + unsigned int flags; /* Control status */ + unsigned long ip; /* ip address of entry */ + unsigned long mask; /* netmask - used for generalised proxy arps (tridge) */ + unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */ + unsigned char hlen; /* Length of hardware address */ + unsigned short htype; /* Type of hardware in use */ + struct device *dev; /* Device the entry is tied to */ + + /* + * The following entries are only used for unresolved hw addresses. + */ + + struct timer_list timer; /* expire timer */ + int retries; /* remaining retries */ + struct sk_buff_head skb; /* list of queued packets */ +}; + + +/* + * Configurable Parameters (don't touch unless you know what you are doing + */ + +/* + * If an arp request is send, ARP_RES_TIME is the timeout value until the + * next request is send. + */ + +#define ARP_RES_TIME (250*(HZ/10)) + +/* + * The number of times an arp request is send, until the host is + * considered unreachable. + */ + +#define ARP_MAX_TRIES 3 + +/* + * After that time, an unused entry is deleted from the arp table. + */ + +#define ARP_TIMEOUT (600*HZ) + +/* + * How often is the function 'arp_check_retries' called. + * An entry is invalidated in the time between ARP_TIMEOUT and + * (ARP_TIMEOUT+ARP_CHECK_INTERVAL). + */ + +#define ARP_CHECK_INTERVAL (60 * HZ) + +enum proxy { + PROXY_EXACT=0, + PROXY_ANY, + PROXY_NONE, +}; + +/* Forward declarations. */ +static void arp_check_expire (unsigned long); +static struct arp_table *arp_lookup(unsigned long paddr, enum proxy proxy); + + +static struct timer_list arp_timer = + { NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire }; + +/* + * The default arp netmask is just 255.255.255.255 which means it's + * a single machine entry. Only proxy entries can have other netmasks + * +*/ + +#define DEF_ARP_NETMASK (~0) + + +/* + * The size of the hash table. Must be a power of two. + * Maybe we should remove hashing in the future for arp and concentrate + * on Patrick Schaaf's Host-Cache-Lookup... + */ + + +#define ARP_TABLE_SIZE 16 + +/* The ugly +1 here is to cater for proxy entries. They are put in their + own list for efficiency of lookup. If you don't want to find a proxy + entry then don't look in the last entry, otherwise do +*/ + +#define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1) + +struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] = +{ + NULL, +}; + + +/* + * The last bits in the IP address are used for the cache lookup. + * A special entry is used for proxy arp entries + */ + +#define HASH(paddr) (htonl(paddr) & (ARP_TABLE_SIZE - 1)) +#define PROXY_HASH ARP_TABLE_SIZE + +/* + * Check if there are too old entries and remove them. If the ATF_PERM + * flag is set, they are always left in the arp cache (permanent entry). + * Note: Only fully resolved entries, which don't have any packets in + * the queue, can be deleted, since ARP_TIMEOUT is much greater than + * ARP_MAX_TRIES*ARP_RES_TIME. + */ + +static void arp_check_expire(unsigned long dummy) +{ + int i; + unsigned long now = jiffies; + unsigned long flags; + save_flags(flags); + cli(); + + for (i = 0; i < FULL_ARP_TABLE_SIZE; i++) + { + struct arp_table *entry; + struct arp_table **pentry = &arp_tables[i]; + + while ((entry = *pentry) != NULL) + { + if ((now - entry->last_used) > ARP_TIMEOUT + && !(entry->flags & ATF_PERM)) + { + *pentry = entry->next; /* remove from list */ + del_timer(&entry->timer); /* Paranoia */ + kfree_s(entry, sizeof(struct arp_table)); + } + else + pentry = &entry->next; /* go to next entry */ + } + } + restore_flags(flags); + + /* + * Set the timer again. + */ + + del_timer(&arp_timer); + arp_timer.expires = ARP_CHECK_INTERVAL; + add_timer(&arp_timer); +} + + +/* + * Release all linked skb's and the memory for this entry. + */ + +static void arp_release_entry(struct arp_table *entry) +{ + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + /* Release the list of `skb' pointers. */ + while ((skb = skb_dequeue(&entry->skb)) != NULL) + { + skb_device_lock(skb); + restore_flags(flags); + dev_kfree_skb(skb, FREE_WRITE); + } + restore_flags(flags); + del_timer(&entry->timer); + kfree_s(entry, sizeof(struct arp_table)); + return; +} + +/* + * Purge a device from the ARP queue + */ + +int arp_device_event(unsigned long event, void *ptr) +{ + struct device *dev=ptr; + int i; + unsigned long flags; + + if(event!=NETDEV_DOWN) + return NOTIFY_DONE; + /* + * This is a bit OTT - maybe we need some arp semaphores instead. + */ + + save_flags(flags); + cli(); + for (i = 0; i < FULL_ARP_TABLE_SIZE; i++) + { + struct arp_table *entry; + struct arp_table **pentry = &arp_tables[i]; + + while ((entry = *pentry) != NULL) + { + if(entry->dev==dev) + { + *pentry = entry->next; /* remove from list */ + del_timer(&entry->timer); /* Paranoia */ + kfree_s(entry, sizeof(struct arp_table)); + } + else + pentry = &entry->next; /* go to next entry */ + } + } + restore_flags(flags); + return NOTIFY_DONE; +} + + +/* + * Create and send an arp packet. If (dest_hw == NULL), we create a broadcast + * message. + */ + +void arp_send(int type, int ptype, unsigned long dest_ip, + struct device *dev, unsigned long src_ip, + unsigned char *dest_hw, unsigned char *src_hw) +{ + struct sk_buff *skb; + struct arphdr *arp; + unsigned char *arp_ptr; + + /* + * No arp on this interface. + */ + + if(dev->flags&IFF_NOARP) + return; + + /* + * Allocate a buffer + */ + + skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4) + + dev->hard_header_len, GFP_ATOMIC); + if (skb == NULL) + { + printk("ARP: no memory to send an arp packet\n"); + return; + } + skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4); + skb->arp = 1; + skb->dev = dev; + skb->free = 1; + + /* + * Fill the device header for the ARP frame + */ + + dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb); + + /* Fill out the arp protocol part. */ + arp = (struct arphdr *) (skb->data + dev->hard_header_len); + arp->ar_hrd = htons(dev->type); +#ifdef CONFIG_AX25 + arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP); +#else + arp->ar_pro = htons(ETH_P_IP); +#endif + arp->ar_hln = dev->addr_len; + arp->ar_pln = 4; + arp->ar_op = htons(type); + + arp_ptr=(unsigned char *)(arp+1); + + memcpy(arp_ptr, src_hw, dev->addr_len); + arp_ptr+=dev->addr_len; + memcpy(arp_ptr, &src_ip,4); + arp_ptr+=4; + if (dest_hw != NULL) + memcpy(arp_ptr, dest_hw, dev->addr_len); + else + memset(arp_ptr, 0, dev->addr_len); + arp_ptr+=dev->addr_len; + memcpy(arp_ptr, &dest_ip, 4); + + dev_queue_xmit(skb, dev, 0); +} + + +/* + * This function is called, if an entry is not resolved in ARP_RES_TIME. + * Either resend a request, or give it up and free the entry. + */ + +static void arp_expire_request (unsigned long arg) +{ + struct arp_table *entry = (struct arp_table *) arg; + struct arp_table **pentry; + unsigned long hash; + unsigned long flags; + + save_flags(flags); + cli(); + + /* + * Since all timeouts are handled with interrupts enabled, there is a + * small chance, that this entry has just been resolved by an incoming + * packet. This is the only race condition, but it is handled... + */ + + if (entry->flags & ATF_COM) + { + restore_flags(flags); + return; + } + + if (--entry->retries > 0) + { + unsigned long ip = entry->ip; + struct device *dev = entry->dev; + + /* Set new timer. */ + del_timer(&entry->timer); + entry->timer.expires = ARP_RES_TIME; + add_timer(&entry->timer); + restore_flags(flags); + arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr, + NULL, dev->dev_addr); + return; + } + + /* + * Arp request timed out. Delete entry and all waiting packets. + * If we give each entry a pointer to itself, we don't have to + * loop through everything again. Maybe hash is good enough, but + * I will look at it later. + */ + + hash = HASH(entry->ip); + + /* proxy entries shouldn't really time out so this is really + only here for completeness + */ + if (entry->flags & ATF_PUBL) + pentry = &arp_tables[PROXY_HASH]; + else + pentry = &arp_tables[hash]; + while (*pentry != NULL) + { + if (*pentry == entry) + { + *pentry = entry->next; /* delete from linked list */ + del_timer(&entry->timer); + restore_flags(flags); + arp_release_entry(entry); + return; + } + pentry = &(*pentry)->next; + } + restore_flags(flags); + printk("Possible ARP queue corruption.\n"); + /* + * We should never arrive here. + */ +} + + +/* + * This will try to retransmit everything on the queue. + */ + +static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest) +{ + struct sk_buff *skb; + + unsigned long flags; + + /* + * Empty the entire queue, building its data up ready to send + */ + + if(!(entry->flags&ATF_COM)) + { + printk("arp_send_q: incomplete entry for %s\n", + in_ntoa(entry->ip)); + return; + } + + save_flags(flags); + + cli(); + while((skb = skb_dequeue(&entry->skb)) != NULL) + { + IS_SKB(skb); + skb_device_lock(skb); + restore_flags(flags); + if(!skb->dev->rebuild_header(skb->data,skb->dev,skb->raddr,skb)) + { + skb->arp = 1; + if(skb->sk==NULL) + dev_queue_xmit(skb, skb->dev, 0); + else + dev_queue_xmit(skb,skb->dev,skb->sk->priority); + } + else + { + /* This routine is only ever called when 'entry' is + complete. Thus this can't fail. */ + printk("arp_send_q: The impossible occurred. Please notify Alan.\n"); + printk("arp_send_q: active entity %s\n",in_ntoa(entry->ip)); + printk("arp_send_q: failed to find %s\n",in_ntoa(skb->raddr)); + } + } + restore_flags(flags); +} + + +/* + * Delete an ARP mapping entry in the cache. + */ + +void arp_destroy(unsigned long ip_addr, int force) +{ + int checked_proxies = 0; + struct arp_table *entry; + struct arp_table **pentry; + unsigned long hash = HASH(ip_addr); + +ugly: + cli(); + pentry = &arp_tables[hash]; + if (! *pentry) /* also check proxy entries */ + pentry = &arp_tables[PROXY_HASH]; + + while ((entry = *pentry) != NULL) + { + if (entry->ip == ip_addr) + { + if ((entry->flags & ATF_PERM) && !force) + return; + *pentry = entry->next; + del_timer(&entry->timer); + sti(); + arp_release_entry(entry); + /* this would have to be cleaned up */ + goto ugly; + /* perhaps like this ? + cli(); + entry = *pentry; + */ + } + pentry = &entry->next; + if (!checked_proxies && ! *pentry) + { /* ugly. we have to make sure we check proxy + entries as well */ + checked_proxies = 1; + pentry = &arp_tables[PROXY_HASH]; + } + } + sti(); +} + + +/* + * Receive an arp request by the device layer. Maybe I rewrite it, to + * use the incoming packet for the reply. The time for the current + * "overhead" isn't that high... + */ + +int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ +/* + * We shouldn't use this type conversion. Check later. + */ + + struct arphdr *arp = (struct arphdr *)skb->h.raw; + unsigned char *arp_ptr= (unsigned char *)(arp+1); + struct arp_table *entry; + struct arp_table *proxy_entry; + int addr_hint,hlen,htype; + unsigned long hash; + unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */ + long sip,tip; + unsigned char *sha,*tha; + +/* + * The hardware length of the packet should match the hardware length + * of the device. Similarly, the hardware types should match. The + * device should be ARP-able. Also, if pln is not 4, then the lookup + * is not from an IP number. We can't currently handle this, so toss + * it. + */ + if (arp->ar_hln != dev->addr_len || + dev->type != ntohs(arp->ar_hrd) || + dev->flags & IFF_NOARP || + arp->ar_pln != 4) + { + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * Another test. + * The logic here is that the protocol being looked up by arp should + * match the protocol the device speaks. If it doesn't, there is a + * problem, so toss the packet. + */ + switch(dev->type) + { +#ifdef CONFIG_AX25 + case ARPHRD_AX25: + if(arp->ar_pro != htons(AX25_P_IP)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + break; +#endif + case ARPHRD_ETHER: + case ARPHRD_ARCNET: + if(arp->ar_pro != htons(ETH_P_IP)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + break; + + default: + printk("ARP: dev->type mangled!\n"); + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * Extract fields + */ + + hlen = dev->addr_len; + htype = dev->type; + + sha=arp_ptr; + arp_ptr+=hlen; + memcpy(&sip,arp_ptr,4); + arp_ptr+=4; + tha=arp_ptr; + arp_ptr+=hlen; + memcpy(&tip,arp_ptr,4); + +/* + * Check for bad requests for 127.0.0.1. If this is one such, delete it. + */ + if(tip == INADDR_LOOPBACK) + { + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * Process entry. The idea here is we want to send a reply if it is a + * request for us or if it is a request for someone else that we hold + * a proxy for. We want to add an entry to our cache if it is a reply + * to us or if it is a request for our address. + * (The assumption for this last is that if someone is requesting our + * address, they are probably intending to talk to us, so it saves time + * if we cache their address. Their address is also probably not in + * our cache, since ours is not in their cache.) + * + * Putting this another way, we only care about replies if they are to + * us, in which case we add them to the cache. For requests, we care + * about those for us and those for our proxies. We reply to both, + * and in the case of requests for us we add the requester to the arp + * cache. + */ + + addr_hint = ip_chk_addr(tip); + + if(arp->ar_op == htons(ARPOP_REPLY)) + { + if(addr_hint!=IS_MYADDR) + { +/* + * Replies to other machines get tossed. + */ + kfree_skb(skb, FREE_READ); + return 0; + } +/* + * Fall through to code below that adds sender to cache. + */ + } + else + { +/* + * It is now an arp request + */ +/* + * Only reply for the real device address or when it's in our proxy tables + */ + if(tip!=dev->pa_addr) + { +/* + * To get in here, it is a request for someone else. We need to + * check if that someone else is one of our proxies. If it isn't, + * we can toss it. + */ + cli(); + for(proxy_entry=arp_tables[PROXY_HASH]; + proxy_entry; + proxy_entry = proxy_entry->next) + { + /* we will respond to a proxy arp request + if the masked arp table ip matches the masked + tip. This allows a single proxy arp table + entry to be used on a gateway machine to handle + all requests for a whole network, rather than + having to use a huge number of proxy arp entries + and having to keep them uptodate. + */ + if (proxy_entry->dev != dev && proxy_entry->htype == htype && + !((proxy_entry->ip^tip)&proxy_entry->mask)) + break; + + } + if (proxy_entry) + { + memcpy(ha, proxy_entry->ha, hlen); + sti(); + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha); + kfree_skb(skb, FREE_READ); + return 0; + } + else + { + sti(); + kfree_skb(skb, FREE_READ); + return 0; + } + } + else + { +/* + * To get here, it must be an arp request for us. We need to reply. + */ + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr); + } + } + + +/* + * Now all replies are handled. Next, anything that falls through to here + * needs to be added to the arp cache, or have its entry updated if it is + * there. + */ + + hash = HASH(sip); + cli(); + for(entry=arp_tables[hash];entry;entry=entry->next) + if(entry->ip==sip && entry->htype==htype) + break; + + if(entry) + { +/* + * Entry found; update it. + */ + memcpy(entry->ha, sha, hlen); + entry->hlen = hlen; + entry->last_used = jiffies; + if (!(entry->flags & ATF_COM)) + { +/* + * This entry was incomplete. Delete the retransmit timer + * and switch to complete status. + */ + del_timer(&entry->timer); + entry->flags |= ATF_COM; + sti(); +/* + * Send out waiting packets. We might have problems, if someone is + * manually removing entries right now -- entry might become invalid + * underneath us. + */ + arp_send_q(entry, sha); + } + else + { + sti(); + } + } + else + { +/* + * No entry found. Need to add a new entry to the arp table. + */ + entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC); + if(entry == NULL) + { + sti(); + printk("ARP: no memory for new arp entry\n"); + + kfree_skb(skb, FREE_READ); + return 0; + } + + entry->mask = DEF_ARP_NETMASK; + entry->ip = sip; + entry->hlen = hlen; + entry->htype = htype; + entry->flags = ATF_COM; + init_timer(&entry->timer); + memcpy(entry->ha, sha, hlen); + entry->last_used = jiffies; + entry->dev = skb->dev; + skb_queue_head_init(&entry->skb); + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + sti(); + } + +/* + * Replies have been sent, and entries have been added. All done. + */ + kfree_skb(skb, FREE_READ); + return 0; +} + + +/* + * Find an arp mapping in the cache. If not found, post a request. + */ + +int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, + unsigned long saddr, struct sk_buff *skb) +{ + struct arp_table *entry; + unsigned long hash; +#ifdef CONFIG_IP_MULTICAST + unsigned long taddr; +#endif + + switch (ip_chk_addr(paddr)) + { + case IS_MYADDR: + printk("ARP: arp called for own IP address\n"); + memcpy(haddr, dev->dev_addr, dev->addr_len); + skb->arp = 1; + return 0; +#ifdef CONFIG_IP_MULTICAST + case IS_MULTICAST: + if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) + { + haddr[0]=0x01; + haddr[1]=0x00; + haddr[2]=0x5e; + taddr=ntohl(paddr); + haddr[5]=taddr&0xff; + taddr=taddr>>8; + haddr[4]=taddr&0xff; + taddr=taddr>>8; + haddr[3]=taddr&0x7f; + return 0; + } + /* + * If a device does not support multicast broadcast the stuff (eg AX.25 for now) + */ +#endif + + case IS_BROADCAST: + memcpy(haddr, dev->broadcast, dev->addr_len); + skb->arp = 1; + return 0; + } + + hash = HASH(paddr); + cli(); + + /* + * Find an entry + */ + entry = arp_lookup(paddr, PROXY_NONE); + + if (entry != NULL) /* It exists */ + { + if (!(entry->flags & ATF_COM)) + { + /* + * A request was already send, but no reply yet. Thus + * queue the packet with the previous attempt + */ + + if (skb != NULL) + { + skb_queue_tail(&entry->skb, skb); + skb_device_unlock(skb); + } + sti(); + return 1; + } + + /* + * Update the record + */ + + entry->last_used = jiffies; + memcpy(haddr, entry->ha, dev->addr_len); + if (skb) + skb->arp = 1; + sti(); + return 0; + } + + /* + * Create a new unresolved entry. + */ + + entry = (struct arp_table *) kmalloc(sizeof(struct arp_table), + GFP_ATOMIC); + if (entry != NULL) + { + entry->mask = DEF_ARP_NETMASK; + entry->ip = paddr; + entry->hlen = dev->addr_len; + entry->htype = dev->type; + entry->flags = 0; + memset(entry->ha, 0, dev->addr_len); + entry->dev = dev; + entry->last_used = jiffies; + init_timer(&entry->timer); + entry->timer.function = arp_expire_request; + entry->timer.data = (unsigned long)entry; + entry->timer.expires = ARP_RES_TIME; + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + add_timer(&entry->timer); + entry->retries = ARP_MAX_TRIES; + skb_queue_head_init(&entry->skb); + if (skb != NULL) + { + skb_queue_tail(&entry->skb, skb); + skb_device_unlock(skb); + } + } + else + { + if (skb != NULL && skb->free) + kfree_skb(skb, FREE_WRITE); + } + sti(); + + /* + * If we didn't find an entry, we will try to send an ARP packet. + */ + + arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, + dev->dev_addr); + + return 1; +} + + +/* + * Write the contents of the ARP cache to a PROCfs file. + */ + +#define HBUFFERLEN 30 + +int arp_get_info(char *buffer, char **start, off_t offset, int length) +{ + int len=0; + off_t begin=0; + off_t pos=0; + int size; + struct arp_table *entry; + char hbuffer[HBUFFERLEN]; + int i,j,k; + const char hexbuf[] = "0123456789ABCDEF"; + + size = sprintf(buffer,"IP address HW type Flags HW address Mask\n"); + + pos+=size; + len+=size; + + cli(); + for(i=0; i<FULL_ARP_TABLE_SIZE; i++) + { + for(entry=arp_tables[i]; entry!=NULL; entry=entry->next) + { +/* + * Convert hardware address to XX:XX:XX:XX ... form. + */ +#ifdef CONFIG_AX25 + + if(entry->htype==ARPHRD_AX25) + strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); + else { +#endif + + for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->hlen;j++) + { + hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ]; + hbuffer[k++]=hexbuf[ entry->ha[j]&15 ]; + hbuffer[k++]=':'; + } + hbuffer[--k]=0; + +#ifdef CONFIG_AX25 + } +#endif + size = sprintf(buffer+len, + "%-17s0x%-10x0x%-10x%s", + in_ntoa(entry->ip), + (unsigned int)entry->htype, + entry->flags, + hbuffer); + size += sprintf(buffer+len+size, + " %-17s\n", + entry->mask==DEF_ARP_NETMASK? + "*":in_ntoa(entry->mask)); + + len+=size; + pos=begin+len; + + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + } + sti(); + + *start=buffer+(offset-begin); /* Start of wanted data */ + len-=(offset-begin); /* Start slop */ + if(len>length) + len=length; /* Ending slop */ + return len; +} + + +/* + * This will find an entry in the ARP table by looking at the IP address. + * If proxy is PROXY_EXACT then only exact IP matches will be allowed + * for proxy entries, otherwise the netmask will be used + */ + +static struct arp_table *arp_lookup(unsigned long paddr, enum proxy proxy) +{ + struct arp_table *entry; + unsigned long hash = HASH(paddr); + + for (entry = arp_tables[hash]; entry != NULL; entry = entry->next) + if (entry->ip == paddr) break; + + /* it's possibly a proxy entry (with a netmask) */ + if (!entry && proxy != PROXY_NONE) + for (entry=arp_tables[PROXY_HASH]; entry != NULL; entry = entry->next) + if ((proxy==PROXY_EXACT) ? (entry->ip==paddr) + : !((entry->ip^paddr)&entry->mask)) + break; + + return entry; +} + + +/* + * Set (create) an ARP cache entry. + */ + +static int arp_req_set(struct arpreq *req) +{ + struct arpreq r; + struct arp_table *entry; + struct sockaddr_in *si; + int htype, hlen; + unsigned long ip; + struct rtable *rt; + + memcpy_fromfs(&r, req, sizeof(r)); + + /* We only understand about IP addresses... */ + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + + /* + * Find out about the hardware type. + * We have to be compatible with BSD UNIX, so we have to + * assume that a "not set" value (i.e. 0) means Ethernet. + */ + + switch (r.arp_ha.sa_family) { + case ARPHRD_ETHER: + htype = ARPHRD_ETHER; + hlen = ETH_ALEN; + break; + + case ARPHRD_ARCNET: + htype = ARPHRD_ARCNET; + hlen = 1; /* length of arcnet addresses */ + break; + +#ifdef CONFIG_AX25 + case ARPHRD_AX25: + htype = ARPHRD_AX25; + hlen = 7; + break; +#endif + default: + return -EPFNOSUPPORT; + } + + si = (struct sockaddr_in *) &r.arp_pa; + ip = si->sin_addr.s_addr; + if (ip == 0) + { + printk("ARP: SETARP: requested PA is 0.0.0.0 !\n"); + return -EINVAL; + } + + /* + * Is it reachable directly ? + */ + + rt = ip_rt_route(ip, NULL, NULL); + if (rt == NULL) + return -ENETUNREACH; + + /* + * Is there an existing entry for this address? + */ + + cli(); + + /* + * Find the entry + */ + entry = arp_lookup(ip, PROXY_EXACT); + if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL)) + { + sti(); + arp_destroy(ip,1); + cli(); + entry = NULL; + } + + /* + * Do we need to create a new entry + */ + + if (entry == NULL) + { + unsigned long hash = HASH(ip); + if (r.arp_flags & ATF_PUBL) + hash = PROXY_HASH; + + entry = (struct arp_table *) kmalloc(sizeof(struct arp_table), + GFP_ATOMIC); + if (entry == NULL) + { + sti(); + return -ENOMEM; + } + entry->ip = ip; + entry->hlen = hlen; + entry->htype = htype; + init_timer(&entry->timer); + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + skb_queue_head_init(&entry->skb); + } + /* + * We now have a pointer to an ARP entry. Update it! + */ + + memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); + entry->last_used = jiffies; + entry->flags = r.arp_flags | ATF_COM; + if ((entry->flags & ATF_PUBL) && (entry->flags & ATF_NETMASK)) + { + si = (struct sockaddr_in *) &r.arp_netmask; + entry->mask = si->sin_addr.s_addr; + } + else + entry->mask = DEF_ARP_NETMASK; + entry->dev = rt->rt_dev; + sti(); + + return 0; +} + + +/* + * Get an ARP cache entry. + */ + +static int arp_req_get(struct arpreq *req) +{ + struct arpreq r; + struct arp_table *entry; + struct sockaddr_in *si; + + /* + * We only understand about IP addresses... + */ + + memcpy_fromfs(&r, req, sizeof(r)); + + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + + /* + * Is there an existing entry for this address? + */ + + si = (struct sockaddr_in *) &r.arp_pa; + cli(); + entry = arp_lookup(si->sin_addr.s_addr,PROXY_ANY); + + if (entry == NULL) + { + sti(); + return -ENXIO; + } + + /* + * We found it; copy into structure. + */ + + memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen); + r.arp_ha.sa_family = entry->htype; + r.arp_flags = entry->flags; + sti(); + + /* + * Copy the information back + */ + + memcpy_tofs(req, &r, sizeof(r)); + return 0; +} + + +/* + * Handle an ARP layer I/O control request. + */ + +int arp_ioctl(unsigned int cmd, void *arg) +{ + struct arpreq r; + struct sockaddr_in *si; + int err; + + switch(cmd) + { + case SIOCDARP: + if (!suser()) + return -EPERM; + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); + if(err) + return err; + memcpy_fromfs(&r, arg, sizeof(r)); + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + si = (struct sockaddr_in *) &r.arp_pa; + arp_destroy(si->sin_addr.s_addr, 1); + return 0; + case SIOCGARP: + err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); + if(err) + return err; + return arp_req_get((struct arpreq *)arg); + case SIOCSARP: + if (!suser()) + return -EPERM; + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); + if(err) + return err; + return arp_req_set((struct arpreq *)arg); + default: + return -EINVAL; + } + /*NOTREACHED*/ + return 0; +} + + +/* + * Called once on startup. + */ + +static struct packet_type arp_packet_type = +{ + 0, /* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */ + NULL, /* All devices */ + arp_rcv, + NULL, + NULL +}; + +static struct notifier_block arp_dev_notifier={ + arp_device_event, + NULL, + 0 +}; + +void arp_init (void) +{ + /* Register the packet type */ + arp_packet_type.type=htons(ETH_P_ARP); + dev_add_pack(&arp_packet_type); + /* Start with the regular checks for expired arp entries. */ + add_timer(&arp_timer); + /* Register for device down reports */ + register_netdevice_notifier(&arp_dev_notifier); +} + diff --git a/pfinet/linux-inet/dev.c b/pfinet/linux-inet/dev.c new file mode 100644 index 00000000..d5a80624 --- /dev/null +++ b/pfinet/linux-inet/dev.c @@ -0,0 +1,1447 @@ +/* + * NET3 Protocol independent device support routines. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Derived from the non IP parts of dev.c 1.0.19 + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * + * Additional Authors: + * Florian la Roche <rzsfl@rz.uni-sb.de> + * Alan Cox <gw4pts@gw4pts.ampr.org> + * David Hinds <dhinds@allegro.stanford.edu> + * + * Changes: + * Alan Cox : device private ioctl copies fields back. + * Alan Cox : Transmit queue code does relevant stunts to + * keep the queue safe. + * Alan Cox : Fixed double lock. + * Alan Cox : Fixed promisc NULL pointer trap + * ???????? : Support the full private ioctl range + * Alan Cox : Moved ioctl permission check into drivers + * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI + * Alan Cox : 100 backlog just doesn't cut it when + * you start doing multicast video 8) + * Alan Cox : Rewrote net_bh and list manager. + * Alan Cox : Fix ETH_P_ALL echoback lengths. + * + * Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have + * the rest as well commented in the end. + */ + +/* + * A lot of these includes will be going walkies very soon + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/if_ether.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/notifier.h> +#include "ip.h" +#include "route.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "arp.h" + + +/* + * The list of packet types we will receive (as opposed to discard) + * and the routines to invoke. + */ + +struct packet_type *ptype_base = NULL; + +/* + * Our notifier list + */ + +struct notifier_block *netdev_chain=NULL; + +/* + * Device drivers call our routines to queue packets here. We empty the + * queue in the bottom half handler. + */ + +static struct sk_buff_head backlog = +{ + (struct sk_buff *)&backlog, (struct sk_buff *)&backlog +#ifdef CONFIG_SKB_CHECK + ,SK_HEAD_SKB +#endif +}; + +/* + * We don't overdo the queue or we will thrash memory badly. + */ + +static int backlog_size = 0; + +/* + * Return the lesser of the two values. + */ + +static __inline__ unsigned long min(unsigned long a, unsigned long b) +{ + return (a < b)? a : b; +} + + +/****************************************************************************************** + + Protocol management and registration routines + +*******************************************************************************************/ + +/* + * For efficiency + */ + +static int dev_nit=0; + +/* + * Add a protocol ID to the list. Now that the input handler is + * smarter we can dispense with all the messy stuff that used to be + * here. + */ + +void dev_add_pack(struct packet_type *pt) +{ + if(pt->type==htons(ETH_P_ALL)) + dev_nit++; + pt->next = ptype_base; + ptype_base = pt; +} + + +/* + * Remove a protocol ID from the list. + */ + +void dev_remove_pack(struct packet_type *pt) +{ + struct packet_type **pt1; + if(pt->type==htons(ETH_P_ALL)) + dev_nit--; + for(pt1=&ptype_base; (*pt1)!=NULL; pt1=&((*pt1)->next)) + { + if(pt==(*pt1)) + { + *pt1=pt->next; + return; + } + } +} + +/***************************************************************************************** + + Device Interface Subroutines + +******************************************************************************************/ + +/* + * Find an interface by name. + */ + +struct device *dev_get(char *name) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (strcmp(dev->name, name) == 0) + return(dev); + } + return(NULL); +} + + +/* + * Prepare an interface for use. + */ + +int dev_open(struct device *dev) +{ + int ret = 0; + + /* + * Call device private open method + */ + if (dev->open) + ret = dev->open(dev); + + /* + * If it went open OK then set the flags + */ + + if (ret == 0) + { + dev->flags |= (IFF_UP | IFF_RUNNING); + /* + * Initialise multicasting status + */ +#ifdef CONFIG_IP_MULTICAST + /* + * Join the all host group + */ + ip_mc_allhost(dev); +#endif + dev_mc_upload(dev); + notifier_call_chain(&netdev_chain, NETDEV_UP, dev); + } + return(ret); +} + + +/* + * Completely shutdown an interface. + */ + +int dev_close(struct device *dev) +{ + /* + * Only close a device if it is up. + */ + + if (dev->flags != 0) + { + int ct=0; + dev->flags = 0; + /* + * Call the device specific close. This cannot fail. + */ + if (dev->stop) + dev->stop(dev); + + notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); +#if 0 + /* + * Delete the route to the device. + */ +#ifdef CONFIG_INET + ip_rt_flush(dev); + arp_device_down(dev); +#endif +#ifdef CONFIG_IPX + ipxrtr_device_down(dev); +#endif +#endif + /* + * Flush the multicast chain + */ + dev_mc_discard(dev); + /* + * Blank the IP addresses + */ + dev->pa_addr = 0; + dev->pa_dstaddr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + /* + * Purge any queued packets when we down the link + */ + while(ct<DEV_NUMBUFFS) + { + struct sk_buff *skb; + while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL) + if(skb->free) + kfree_skb(skb,FREE_WRITE); + ct++; + } + } + return(0); +} + + +/* + * Device change register/unregister. These are not inline or static + * as we export them to the world. + */ + +int register_netdevice_notifier(struct notifier_block *nb) +{ + return notifier_chain_register(&netdev_chain, nb); +} + +int unregister_netdevice_notifier(struct notifier_block *nb) +{ + return notifier_chain_unregister(&netdev_chain,nb); +} + + + +/* + * Send (or queue for sending) a packet. + * + * IMPORTANT: When this is called to resend frames. The caller MUST + * already have locked the sk_buff. Apart from that we do the + * rest of the magic. + */ + +void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) +{ + unsigned long flags; + int nitcount; + struct packet_type *ptype; + int where = 0; /* used to say if the packet should go */ + /* at the front or the back of the */ + /* queue - front is a retransmit try */ + + if (dev == NULL) + { + printk("dev.c: dev_queue_xmit: dev = NULL\n"); + return; + } + + if(pri>=0 && !skb_device_locked(skb)) + skb_device_lock(skb); /* Shove a lock on the frame */ +#ifdef CONFIG_SLAVE_BALANCING + save_flags(flags); + cli(); + if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue && + (dev->slave->flags & IFF_UP)) + dev=dev->slave; + restore_flags(flags); +#endif +#ifdef CONFIG_SKB_CHECK + IS_SKB(skb); +#endif + skb->dev = dev; + + /* + * This just eliminates some race conditions, but not all... + */ + + if (skb->next != NULL) + { + /* + * Make sure we haven't missed an interrupt. + */ + printk("dev_queue_xmit: worked around a missed interrupt\n"); + dev->hard_start_xmit(NULL, dev); + return; + } + + /* + * Negative priority is used to flag a frame that is being pulled from the + * queue front as a retransmit attempt. It therefore goes back on the queue + * start on a failure. + */ + + if (pri < 0) + { + pri = -pri-1; + where = 1; + } + + if (pri >= DEV_NUMBUFFS) + { + printk("bad priority in dev_queue_xmit.\n"); + pri = 1; + } + + /* + * If the address has not been resolved. Call the device header rebuilder. + * This can cover all protocols and technically not just ARP either. + */ + + if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) { + return; + } + + save_flags(flags); + cli(); + if (!where) { +#ifdef CONFIG_SLAVE_BALANCING + skb->in_dev_queue=1; +#endif + skb_queue_tail(dev->buffs + pri,skb); + skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */ + skb = skb_dequeue(dev->buffs + pri); + skb_device_lock(skb); /* New buffer needs locking down */ +#ifdef CONFIG_SLAVE_BALANCING + skb->in_dev_queue=0; +#endif + } + restore_flags(flags); + + /* copy outgoing packets to any sniffer packet handlers */ + if(!where) + { + for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) + { + /* Never send packets back to the socket + * they originated from - MvS (miquels@drinkel.ow.org) + */ + if (ptype->type == htons(ETH_P_ALL) && + (ptype->dev == dev || !ptype->dev) && + ((struct sock *)ptype->data != skb->sk)) + { + struct sk_buff *skb2; + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) + break; + /* + * The protocol knows this has (for other paths) been taken off + * and adds it back. + */ + skb2->len-=skb->dev->hard_header_len; + ptype->func(skb2, skb->dev, ptype); + nitcount--; + } + } + } + if (dev->hard_start_xmit(skb, dev) == 0) { + /* + * Packet is now solely the responsibility of the driver + */ + return; + } + + /* + * Transmission failed, put skb back into a list. Once on the list it's safe and + * no longer device locked (it can be freed safely from the device queue) + */ + cli(); +#ifdef CONFIG_SLAVE_BALANCING + skb->in_dev_queue=1; + dev->pkt_queue++; +#endif + skb_device_unlock(skb); + skb_queue_head(dev->buffs + pri,skb); + restore_flags(flags); +} + +/* + * Receive a packet from a device driver and queue it for the upper + * (protocol) levels. It always succeeds. This is the recommended + * interface to use. + */ + +void netif_rx(struct sk_buff *skb) +{ + static int dropping = 0; + + /* + * Any received buffers are un-owned and should be discarded + * when freed. These will be updated later as the frames get + * owners. + */ + skb->sk = NULL; + skb->free = 1; + if(skb->stamp.tv_sec==0) + skb->stamp = xtime; + + /* + * Check that we aren't overdoing things. + */ + + if (!backlog_size) + dropping = 0; + else if (backlog_size > 300) + dropping = 1; + + if (dropping) + { + kfree_skb(skb, FREE_READ); + return; + } + + /* + * Add it to the "backlog" queue. + */ +#ifdef CONFIG_SKB_CHECK + IS_SKB(skb); +#endif + skb_queue_tail(&backlog,skb); + backlog_size++; + + /* + * If any packet arrived, mark it for processing after the + * hardware interrupt returns. + */ + + mark_bh(NET_BH); + return; +} + + +/* + * The old interface to fetch a packet from a device driver. + * This function is the base level entry point for all drivers that + * want to send a packet to the upper (protocol) levels. It takes + * care of de-multiplexing the packet to the various modules based + * on their protocol ID. + * + * Return values: 1 <- exit I can't do any more + * 0 <- feed me more (i.e. "done", "OK"). + * + * This function is OBSOLETE and should not be used by any new + * device. + */ + +int dev_rint(unsigned char *buff, long len, int flags, struct device *dev) +{ + static int dropping = 0; + struct sk_buff *skb = NULL; + unsigned char *to; + int amount, left; + int len2; + + if (dev == NULL || buff == NULL || len <= 0) + return(1); + + if (flags & IN_SKBUFF) + { + skb = (struct sk_buff *) buff; + } + else + { + if (dropping) + { + if (skb_peek(&backlog) != NULL) + return(1); + printk("INET: dev_rint: no longer dropping packets.\n"); + dropping = 0; + } + + skb = alloc_skb(len, GFP_ATOMIC); + if (skb == NULL) + { + printk("dev_rint: packet dropped on %s (no memory) !\n", + dev->name); + dropping = 1; + return(1); + } + + /* + * First we copy the packet into a buffer, and save it for later. We + * in effect handle the incoming data as if it were from a circular buffer + */ + + to = skb->data; + left = len; + + len2 = len; + while (len2 > 0) + { + amount = min(len2, (unsigned long) dev->rmem_end - + (unsigned long) buff); + memcpy(to, buff, amount); + len2 -= amount; + left -= amount; + buff += amount; + to += amount; + if ((unsigned long) buff == dev->rmem_end) + buff = (unsigned char *) dev->rmem_start; + } + } + + /* + * Tag the frame and kick it to the proper receive routine + */ + + skb->len = len; + skb->dev = dev; + skb->free = 1; + + netif_rx(skb); + /* + * OK, all done. + */ + return(0); +} + + +/* + * This routine causes all interfaces to try to send some data. + */ + +void dev_transmit(void) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (dev->flags != 0 && !dev->tbusy) { + /* + * Kick the device + */ + dev_tint(dev); + } + } +} + + +/********************************************************************************** + + Receive Queue Processor + +***********************************************************************************/ + +/* + * This is a single non-reentrant routine which takes the received packet + * queue and throws it at the networking layers in the hope that something + * useful will emerge. + */ + +volatile char in_bh = 0; /* Non-reentrant remember */ + +int in_net_bh() /* Used by timer.c */ +{ + return(in_bh==0?0:1); +} + +/* + * When we are called the queue is ready to grab, the interrupts are + * on and hardware can interrupt and queue to the receive queue a we + * run with no problems. + * This is run as a bottom half after an interrupt handler that does + * mark_bh(NET_BH); + */ + +void net_bh(void *tmp) +{ + struct sk_buff *skb; + struct packet_type *ptype; + struct packet_type *pt_prev; + unsigned short type; + + /* + * Atomically check and mark our BUSY state. + */ + + if (set_bit(1, (void*)&in_bh)) + return; + + /* + * Can we send anything now? We want to clear the + * decks for any more sends that get done as we + * process the input. + */ + + dev_transmit(); + + /* + * Any data left to process. This may occur because a + * mark_bh() is done after we empty the queue including + * that from the device which does a mark_bh() just after + */ + + cli(); + + /* + * While the queue is not empty + */ + + while((skb=skb_dequeue(&backlog))!=NULL) + { + /* + * We have a packet. Therefore the queue has shrunk + */ + backlog_size--; + + sti(); + + /* + * Bump the pointer to the next structure. + * This assumes that the basic 'skb' pointer points to + * the MAC header, if any (as indicated by its "length" + * field). Take care now! + */ + + skb->h.raw = skb->data + skb->dev->hard_header_len; + skb->len -= skb->dev->hard_header_len; + + /* + * Fetch the packet protocol ID. This is also quite ugly, as + * it depends on the protocol driver (the interface itself) to + * know what the type is, or where to get it from. The Ethernet + * interfaces fetch the ID from the two bytes in the Ethernet MAC + * header (the h_proto field in struct ethhdr), but other drivers + * may either use the ethernet ID's or extra ones that do not + * clash (eg ETH_P_AX25). We could set this before we queue the + * frame. In fact I may change this when I have time. + */ + + type = skb->dev->type_trans(skb, skb->dev); + + /* + * We got a packet ID. Now loop over the "known protocols" + * table (which is actually a linked list, but this will + * change soon if I get my way- FvK), and forward the packet + * to anyone who wants it. + * + * [FvK didn't get his way but he is right this ought to be + * hashed so we typically get a single hit. The speed cost + * here is minimal but no doubt adds up at the 4,000+ pkts/second + * rate we can hit flat out] + */ + pt_prev = NULL; + for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) + { + if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev)) + { + /* + * We already have a match queued. Deliver + * to it and then remember the new match + */ + if(pt_prev) + { + struct sk_buff *skb2; + + skb2=skb_clone(skb, GFP_ATOMIC); + + /* + * Kick the protocol handler. This should be fast + * and efficient code. + */ + + if(skb2) + pt_prev->func(skb2, skb->dev, pt_prev); + } + /* Remember the current last to do */ + pt_prev=ptype; + } + } /* End of protocol list loop */ + + /* + * Is there a last item to send to ? + */ + + if(pt_prev) + pt_prev->func(skb, skb->dev, pt_prev); + /* + * Has an unknown packet has been received ? + */ + + else + kfree_skb(skb, FREE_WRITE); + + /* + * Again, see if we can transmit anything now. + * [Ought to take this out judging by tests it slows + * us down not speeds us up] + */ + + dev_transmit(); + cli(); + } /* End of queue loop */ + + /* + * We have emptied the queue + */ + + in_bh = 0; + sti(); + + /* + * One last output flush. + */ + + dev_transmit(); +} + + +/* + * This routine is called when an device driver (i.e. an + * interface) is ready to transmit a packet. + */ + +void dev_tint(struct device *dev) +{ + int i; + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + /* + * Work the queues in priority order + */ + + for(i = 0;i < DEV_NUMBUFFS; i++) + { + /* + * Pull packets from the queue + */ + + + cli(); + while((skb=skb_dequeue(&dev->buffs[i]))!=NULL) + { + /* + * Stop anyone freeing the buffer while we retransmit it + */ + skb_device_lock(skb); + restore_flags(flags); + /* + * Feed them to the output stage and if it fails + * indicate they re-queue at the front. + */ + dev_queue_xmit(skb,dev,-i - 1); + /* + * If we can take no more then stop here. + */ + if (dev->tbusy) + return; + cli(); + } + } + restore_flags(flags); +} + + +/* + * Perform a SIOCGIFCONF call. This structure will change + * size shortly, and there is nothing I can do about it. + * Thus we will need a 'compatibility mode'. + */ + +static int dev_ifconf(char *arg) +{ + struct ifconf ifc; + struct ifreq ifr; + struct device *dev; + char *pos; + int len; + int err; + + /* + * Fetch the caller's info block. + */ + + err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); + if(err) + return err; + memcpy_fromfs(&ifc, arg, sizeof(struct ifconf)); + len = ifc.ifc_len; + pos = ifc.ifc_buf; + + /* + * We now walk the device list filling each active device + * into the array. + */ + + err=verify_area(VERIFY_WRITE,pos,len); + if(err) + return err; + + /* + * Loop over the interfaces, and write an info block for each. + */ + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if(!(dev->flags & IFF_UP)) /* Downed devices don't count */ + continue; + memset(&ifr, 0, sizeof(struct ifreq)); + strcpy(ifr.ifr_name, dev->name); + (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family; + (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; + + /* + * Write this block to the caller's space. + */ + + memcpy_tofs(pos, &ifr, sizeof(struct ifreq)); + pos += sizeof(struct ifreq); + len -= sizeof(struct ifreq); + + /* + * Have we run out of space here ? + */ + + if (len < sizeof(struct ifreq)) + break; + } + + /* + * All done. Write the updated control block back to the caller. + */ + + ifc.ifc_len = (pos - ifc.ifc_buf); + ifc.ifc_req = (struct ifreq *) ifc.ifc_buf; + memcpy_tofs(arg, &ifc, sizeof(struct ifconf)); + + /* + * Report how much was filled in + */ + + return(pos - arg); +} + + +/* + * This is invoked by the /proc filesystem handler to display a device + * in detail. + */ + +static int sprintf_stats(char *buffer, struct device *dev) +{ + struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL); + int size; + + if (stats) + size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n", + dev->name, + stats->rx_packets, stats->rx_errors, + stats->rx_dropped + stats->rx_missed_errors, + stats->rx_fifo_errors, + stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors, + stats->tx_packets, stats->tx_errors, stats->tx_dropped, + stats->tx_fifo_errors, stats->collisions, + stats->tx_carrier_errors + stats->tx_aborted_errors + + stats->tx_window_errors + stats->tx_heartbeat_errors); + else + size = sprintf(buffer, "%6s: No statistics available.\n", dev->name); + + return size; +} + +/* + * Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface + * to create /proc/net/dev + */ + +int dev_get_info(char *buffer, char **start, off_t offset, int length) +{ + int len=0; + off_t begin=0; + off_t pos=0; + int size; + + struct device *dev; + + + size = sprintf(buffer, "Inter-| Receive | Transmit\n" + " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n"); + + pos+=size; + len+=size; + + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + size = sprintf_stats(buffer+len, dev); + len+=size; + pos=begin+len; + + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + + *start=buffer+(offset-begin); /* Start of wanted data */ + len-=(offset-begin); /* Start slop */ + if(len>length) + len=length; /* Ending slop */ + return len; +} + + +/* + * This checks bitmasks for the ioctl calls for devices. + */ + +static inline int bad_mask(unsigned long mask, unsigned long addr) +{ + if (addr & (mask = ~mask)) + return 1; + mask = ntohl(mask); + if (mask & (mask+1)) + return 1; + return 0; +} + +/* + * Perform the SIOCxIFxxx calls. + * + * The socket layer has seen an ioctl the address family thinks is + * for the device. At this point we get invoked to make a decision + */ + +static int dev_ifsioc(void *arg, unsigned int getset) +{ + struct ifreq ifr; + struct device *dev; + int ret; + + /* + * Fetch the caller's info block into kernel space + */ + + int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); + if(err) + return err; + + memcpy_fromfs(&ifr, arg, sizeof(struct ifreq)); + + /* + * See which interface the caller is talking about. + */ + + if ((dev = dev_get(ifr.ifr_name)) == NULL) + return(-ENODEV); + + switch(getset) + { + case SIOCGIFFLAGS: /* Get interface flags */ + ifr.ifr_flags = dev->flags; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFFLAGS: /* Set interface flags */ + { + int old_flags = dev->flags; +#ifdef CONFIG_SLAVE_BALANCING + if(dev->flags&IFF_SLAVE) + return -EBUSY; +#endif + dev->flags = ifr.ifr_flags & ( + IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | + IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | + IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER + | IFF_MULTICAST); +#ifdef CONFIG_SLAVE_BALANCING + if(!(dev->flags&IFF_MASTER) && dev->slave) + { + dev->slave->flags&=~IFF_SLAVE; + dev->slave=NULL; + } +#endif + /* + * Load in the correct multicast list now the flags have changed. + */ + + dev_mc_upload(dev); +#if 0 + if( dev->set_multicast_list!=NULL) + { + + /* + * Has promiscuous mode been turned off + */ + + if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0)) + dev->set_multicast_list(dev,0,NULL); + + /* + * Has it been turned on + */ + + if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0)) + dev->set_multicast_list(dev,-1,NULL); + } +#endif + /* + * Have we downed the interface + */ + + if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) + { + ret = dev_close(dev); + } + else + { + /* + * Have we upped the interface + */ + + ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP)) + ? dev_open(dev) : 0; + /* + * Check the flags. + */ + if(ret<0) + dev->flags&=~IFF_UP; /* Didn't open so down the if */ + } + } + break; + + case SIOCGIFADDR: /* Get interface address (and family) */ + (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; + (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + + case SIOCSIFADDR: /* Set interface address (and family) */ + dev->pa_addr = (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_addr.s_addr; + dev->family = ifr.ifr_addr.sa_family; + +#ifdef CONFIG_INET + /* This is naughty. When net-032e comes out It wants moving into the net032 + code not the kernel. Till then it can sit here (SIGH) */ + dev->pa_mask = ip_get_mask(dev->pa_addr); +#endif + dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; + ret = 0; + break; + + case SIOCGIFBRDADDR: /* Get the broadcast address */ + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + + case SIOCSIFBRDADDR: /* Set the broadcast address */ + dev->pa_brdaddr = (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_addr.s_addr; + ret = 0; + break; + + case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */ + (*(struct sockaddr_in *) + &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + + case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */ + dev->pa_dstaddr = (*(struct sockaddr_in *) + &ifr.ifr_dstaddr).sin_addr.s_addr; + ret = 0; + break; + + case SIOCGIFNETMASK: /* Get the netmask for the interface */ + (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask; + (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + + case SIOCSIFNETMASK: /* Set the netmask for the interface */ + { + unsigned long mask = (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_addr.s_addr; + ret = -EINVAL; + /* + * The mask we set must be legal. + */ + if (bad_mask(mask,0)) + break; + dev->pa_mask = mask; + ret = 0; + } + break; + + case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */ + + ifr.ifr_metric = dev->metric; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + + case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */ + dev->metric = ifr.ifr_metric; + ret = 0; + break; + + case SIOCGIFMTU: /* Get the MTU of a device */ + ifr.ifr_mtu = dev->mtu; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + + case SIOCSIFMTU: /* Set the MTU of a device */ + + /* + * MTU must be positive and under the page size problem + */ + + if(ifr.ifr_mtu<1 || ifr.ifr_mtu>3800) + return -EINVAL; + dev->mtu = ifr.ifr_mtu; + ret = 0; + break; + + case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently + do not support it */ + printk("NET: ioctl(SIOCGIFMEM, %p)\n", arg); + ret = -EINVAL; + break; + + case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */ + printk("NET: ioctl(SIOCSIFMEM, %p)\n", arg); + ret = -EINVAL; + break; + + case OLD_SIOCGIFHWADDR: /* Get the hardware address. This will change and SIFHWADDR will be added */ + memcpy(ifr.old_ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN); + memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); + ret=0; + break; + + case SIOCGIFHWADDR: + memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); + ifr.ifr_hwaddr.sa_family=dev->type; + memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); + ret=0; + break; + + case SIOCSIFHWADDR: + if(dev->set_mac_address==NULL) + return -EOPNOTSUPP; + if(ifr.ifr_hwaddr.sa_family!=dev->type) + return -EINVAL; + ret=dev->set_mac_address(dev,ifr.ifr_hwaddr.sa_data); + break; + + case SIOCGIFMAP: + ifr.ifr_map.mem_start=dev->mem_start; + ifr.ifr_map.mem_end=dev->mem_end; + ifr.ifr_map.base_addr=dev->base_addr; + ifr.ifr_map.irq=dev->irq; + ifr.ifr_map.dma=dev->dma; + ifr.ifr_map.port=dev->if_port; + memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); + ret=0; + break; + + case SIOCSIFMAP: + if(dev->set_config==NULL) + return -EOPNOTSUPP; + return dev->set_config(dev,&ifr.ifr_map); + + case SIOCGIFSLAVE: +#ifdef CONFIG_SLAVE_BALANCING + if(dev->slave==NULL) + return -ENOENT; + strncpy(ifr.ifr_name,dev->name,sizeof(ifr.ifr_name)); + memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); + ret=0; +#else + return -ENOENT; +#endif + break; +#ifdef CONFIG_SLAVE_BALANCING + case SIOCSIFSLAVE: + { + + /* + * Fun game. Get the device up and the flags right without + * letting some scummy user confuse us. + */ + unsigned long flags; + struct device *slave=dev_get(ifr.ifr_slave); + save_flags(flags); + if(slave==NULL) + { + return -ENODEV; + } + cli(); + if((slave->flags&(IFF_UP|IFF_RUNNING))!=(IFF_UP|IFF_RUNNING)) + { + restore_flags(flags); + return -EINVAL; + } + if(dev->flags&IFF_SLAVE) + { + restore_flags(flags); + return -EBUSY; + } + if(dev->slave!=NULL) + { + restore_flags(flags); + return -EBUSY; + } + if(slave->flags&IFF_SLAVE) + { + restore_flags(flags); + return -EBUSY; + } + dev->slave=slave; + slave->flags|=IFF_SLAVE; + dev->flags|=IFF_MASTER; + restore_flags(flags); + ret=0; + } + break; +#endif + + case SIOCADDMULTI: + if(dev->set_multicast_list==NULL) + return -EINVAL; + if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC) + return -EINVAL; + dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1); + return 0; + + case SIOCDELMULTI: + if(dev->set_multicast_list==NULL) + return -EINVAL; + if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC) + return -EINVAL; + dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1); + return 0; + /* + * Unknown or private ioctl + */ + + default: + if((getset >= SIOCDEVPRIVATE) && + (getset <= (SIOCDEVPRIVATE + 15))) { + if(dev->do_ioctl==NULL) + return -EOPNOTSUPP; + ret=dev->do_ioctl(dev, &ifr, getset); + memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); + break; + } + + ret = -EINVAL; + } + return(ret); +} + + +/* + * This function handles all "interface"-type I/O control requests. The actual + * 'doing' part of this is dev_ifsioc above. + */ + +int dev_ioctl(unsigned int cmd, void *arg) +{ + switch(cmd) + { + case SIOCGIFCONF: + (void) dev_ifconf((char *) arg); + return 0; + + /* + * Ioctl calls that can be done by all. + */ + + case SIOCGIFFLAGS: + case SIOCGIFADDR: + case SIOCGIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCSIFHWADDR: + case OLD_SIOCGIFHWADDR: + case SIOCGIFSLAVE: + case SIOCGIFMAP: + return dev_ifsioc(arg, cmd); + + /* + * Ioctl calls requiring the power of a superuser + */ + + case SIOCSIFFLAGS: + case SIOCSIFADDR: + case SIOCSIFDSTADDR: + case SIOCSIFBRDADDR: + case SIOCSIFNETMASK: + case SIOCSIFMETRIC: + case SIOCSIFMTU: + case SIOCSIFMEM: + case SIOCSIFMAP: + case SIOCSIFSLAVE: + case SIOCADDMULTI: + case SIOCDELMULTI: + if (!suser()) + return -EPERM; + return dev_ifsioc(arg, cmd); + + case SIOCSIFLINK: + return -EINVAL; + + /* + * Unknown or private ioctl. + */ + + default: + if((cmd >= SIOCDEVPRIVATE) && + (cmd <= (SIOCDEVPRIVATE + 15))) { + return dev_ifsioc(arg, cmd); + } + return -EINVAL; + } +} + + +/* + * Initialize the DEV module. At boot time this walks the device list and + * unhooks any devices that fail to initialise (normally hardware not + * present) and leaves us with a valid list of present and active devices. + * + * The PCMCIA code may need to change this a little, and add a pair + * of register_inet_device() unregister_inet_device() calls. This will be + * needed for ethernet as modules support. + */ + +void dev_init(void) +{ + struct device *dev, *dev2; + + /* + * Add the devices. + * If the call to dev->init fails, the dev is removed + * from the chain disconnecting the device until the + * next reboot. + */ + + dev2 = NULL; + for (dev = dev_base; dev != NULL; dev=dev->next) + { + if (dev->init && dev->init(dev)) + { + /* + * It failed to come up. Unhook it. + */ + + if (dev2 == NULL) + dev_base = dev->next; + else + dev2->next = dev->next; + } + else + { + dev2 = dev; + } + } +} diff --git a/pfinet/linux-inet/igmp.c b/pfinet/linux-inet/igmp.c new file mode 100644 index 00000000..eebf3b77 --- /dev/null +++ b/pfinet/linux-inet/igmp.c @@ -0,0 +1,390 @@ +/* + * Linux NET3: Internet Gateway Management Protocol [IGMP] + * + * Authors: + * Alan Cox <Alan.Cox@linux.org> + * + * WARNING: + * This is a 'preliminary' implementation... on your own head + * be it. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + + +#include <asm/segment.h> +#include <asm/system.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "ip.h" +#include "protocol.h" +#include "route.h" +#include <linux/skbuff.h> +#include "sock.h" +#include <linux/igmp.h> + +#ifdef CONFIG_IP_MULTICAST + + +/* + * Timer management + */ + + +static void igmp_stop_timer(struct ip_mc_list *im) +{ + del_timer(&im->timer); + im->tm_running=0; +} + +static int random(void) +{ + static unsigned long seed=152L; + seed=seed*69069L+1; + return seed^jiffies; +} + + +static void igmp_start_timer(struct ip_mc_list *im) +{ + int tv; + if(im->tm_running) + return; + tv=random()%(10*HZ); /* Pick a number any number 8) */ + im->timer.expires=tv; + im->tm_running=1; + add_timer(&im->timer); +} + +/* + * Send an IGMP report. + */ + +#define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64) + +static void igmp_send_report(struct device *dev, unsigned long address, int type) +{ + struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC); + int tmp; + struct igmphdr *igh; + + if(skb==NULL) + return; + tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL, + skb->mem_len, 0, 1); + if(tmp<0) + { + kfree_skb(skb, FREE_WRITE); + return; + } + igh=(struct igmphdr *)(skb->data+tmp); + skb->len=tmp+sizeof(*igh); + igh->csum=0; + igh->unused=0; + igh->type=type; + igh->group=address; + igh->csum=ip_compute_csum((void *)igh,sizeof(*igh)); + ip_queue_xmit(NULL,dev,skb,1); +} + + +static void igmp_timer_expire(unsigned long data) +{ + struct ip_mc_list *im=(struct ip_mc_list *)data; + igmp_stop_timer(im); + igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT); +} + +static void igmp_init_timer(struct ip_mc_list *im) +{ + im->tm_running=0; + init_timer(&im->timer); + im->timer.data=(unsigned long)im; + im->timer.function=&igmp_timer_expire; +} + + +static void igmp_heard_report(struct device *dev, unsigned long address) +{ + struct ip_mc_list *im; + for(im=dev->ip_mc_list;im!=NULL;im=im->next) + if(im->multiaddr==address) + igmp_stop_timer(im); +} + +static void igmp_heard_query(struct device *dev) +{ + struct ip_mc_list *im; + for(im=dev->ip_mc_list;im!=NULL;im=im->next) + if(!im->tm_running && im->multiaddr!=IGMP_ALL_HOSTS) + igmp_start_timer(im); +} + +/* + * Map a multicast IP onto multicast MAC for type ethernet. + */ + +static void ip_mc_map(unsigned long addr, char *buf) +{ + addr=ntohl(addr); + buf[0]=0x01; + buf[1]=0x00; + buf[2]=0x5e; + buf[5]=addr&0xFF; + addr>>=8; + buf[4]=addr&0xFF; + addr>>=8; + buf[3]=addr&0x7F; +} + +/* + * Add a filter to a device + */ + +void ip_mc_filter_add(struct device *dev, unsigned long addr) +{ + char buf[6]; + if(dev->type!=ARPHRD_ETHER) + return; /* Only do ethernet now */ + ip_mc_map(addr,buf); + dev_mc_add(dev,buf,ETH_ALEN,0); +} + +/* + * Remove a filter from a device + */ + +void ip_mc_filter_del(struct device *dev, unsigned long addr) +{ + char buf[6]; + if(dev->type!=ARPHRD_ETHER) + return; /* Only do ethernet now */ + ip_mc_map(addr,buf); + dev_mc_delete(dev,buf,ETH_ALEN,0); +} + +static void igmp_group_dropped(struct ip_mc_list *im) +{ + del_timer(&im->timer); + igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE); + ip_mc_filter_del(im->interface, im->multiaddr); +/* printk("Left group %lX\n",im->multiaddr);*/ +} + +static void igmp_group_added(struct ip_mc_list *im) +{ + igmp_init_timer(im); + igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT); + ip_mc_filter_add(im->interface, im->multiaddr); +/* printk("Joined group %lX\n",im->multiaddr);*/ +} + +int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, + unsigned long daddr, unsigned short len, unsigned long saddr, int redo, + struct inet_protocol *protocol) +{ + /* This basically follows the spec line by line -- see RFC1112 */ + struct igmphdr *igh=(struct igmphdr *)skb->h.raw; + + if(skb->ip_hdr->ttl!=1 || ip_compute_csum((void *)igh,sizeof(*igh))) + { + kfree_skb(skb, FREE_READ); + return 0; + } + + if(igh->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS) + igmp_heard_query(dev); + if(igh->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==igh->group) + igmp_heard_report(dev,igh->group); + kfree_skb(skb, FREE_READ); + return 0; +} + +/* + * Multicast list managers + */ + + +/* + * A socket has joined a multicast group on device dev. + */ + +static void ip_mc_inc_group(struct device *dev, unsigned long addr) +{ + struct ip_mc_list *i; + for(i=dev->ip_mc_list;i!=NULL;i=i->next) + { + if(i->multiaddr==addr) + { + i->users++; + return; + } + } + i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL); + if(!i) + return; + i->users=1; + i->interface=dev; + i->multiaddr=addr; + i->next=dev->ip_mc_list; + igmp_group_added(i); + dev->ip_mc_list=i; +} + +/* + * A socket has left a multicast group on device dev + */ + +static void ip_mc_dec_group(struct device *dev, unsigned long addr) +{ + struct ip_mc_list **i; + for(i=&(dev->ip_mc_list);(*i)!=NULL;i=&(*i)->next) + { + if((*i)->multiaddr==addr) + { + if(--((*i)->users)) + return; + else + { + struct ip_mc_list *tmp= *i; + igmp_group_dropped(tmp); + *i=(*i)->next; + kfree_s(tmp,sizeof(*tmp)); + } + } + } +} + +/* + * Device going down: Clean up. + */ + +void ip_mc_drop_device(struct device *dev) +{ + struct ip_mc_list *i; + struct ip_mc_list *j; + for(i=dev->ip_mc_list;i!=NULL;i=j) + { + j=i->next; + kfree_s(i,sizeof(*i)); + } + dev->ip_mc_list=NULL; +} + +/* + * Device going up. Make sure it is in all hosts + */ + +void ip_mc_allhost(struct device *dev) +{ + struct ip_mc_list *i; + for(i=dev->ip_mc_list;i!=NULL;i=i->next) + if(i->multiaddr==IGMP_ALL_HOSTS) + return; + i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL); + if(!i) + return; + i->users=1; + i->interface=dev; + i->multiaddr=IGMP_ALL_HOSTS; + i->next=dev->ip_mc_list; + dev->ip_mc_list=i; + ip_mc_filter_add(i->interface, i->multiaddr); + +} + +/* + * Join a socket to a group + */ + +int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr) +{ + int unused= -1; + int i; + if(!MULTICAST(addr)) + return -EINVAL; + if(!(dev->flags&IFF_MULTICAST)) + return -EADDRNOTAVAIL; + if(sk->ip_mc_list==NULL) + { + if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL) + return -ENOMEM; + memset(sk->ip_mc_list,'\0',sizeof(*sk->ip_mc_list)); + } + for(i=0;i<IP_MAX_MEMBERSHIPS;i++) + { + if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev) + return -EADDRINUSE; + if(sk->ip_mc_list->multidev[i]==NULL) + unused=i; + } + + if(unused==-1) + return -ENOBUFS; + sk->ip_mc_list->multiaddr[unused]=addr; + sk->ip_mc_list->multidev[unused]=dev; + ip_mc_inc_group(dev,addr); + return 0; +} + +/* + * Ask a socket to leave a group. + */ + +int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr) +{ + int i; + if(!MULTICAST(addr)) + return -EINVAL; + if(!(dev->flags&IFF_MULTICAST)) + return -EADDRNOTAVAIL; + if(sk->ip_mc_list==NULL) + return -EADDRNOTAVAIL; + + for(i=0;i<IP_MAX_MEMBERSHIPS;i++) + { + if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev) + { + sk->ip_mc_list->multidev[i]=NULL; + ip_mc_dec_group(dev,addr); + return 0; + } + } + return -EADDRNOTAVAIL; +} + +/* + * A socket is closing. + */ + +void ip_mc_drop_socket(struct sock *sk) +{ + int i; + + if(sk->ip_mc_list==NULL) + return; + + for(i=0;i<IP_MAX_MEMBERSHIPS;i++) + { + if(sk->ip_mc_list->multidev[i]) + { + ip_mc_dec_group(sk->ip_mc_list->multidev[i], sk->ip_mc_list->multiaddr[i]); + sk->ip_mc_list->multidev[i]=NULL; + } + } + kfree_s(sk->ip_mc_list,sizeof(*sk->ip_mc_list)); + sk->ip_mc_list=NULL; +} + +#endif diff --git a/pfinet/linux-inet/proc.c b/pfinet/linux-inet/proc.c new file mode 100644 index 00000000..855dde3f --- /dev/null +++ b/pfinet/linux-inet/proc.c @@ -0,0 +1,251 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * This file implements the various access functions for the + * PROC file system. It is mainly used for debugging and + * statistics. + * + * Version: @(#)proc.c 1.0.5 05/27/93 + * + * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de> + * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de> + * Erik Schoenfelder, <schoenfr@ibr.cs.tu-bs.de> + * + * Fixes: + * Alan Cox : UDP sockets show the rxqueue/txqueue + * using hint flag for the netinfo. + * Pauline Middelink : identd support + * Alan Cox : Make /proc safer. + * Erik Schoenfelder : /proc/net/snmp + * Alan Cox : Handle dead sockets properly. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <asm/system.h> +#include <linux/autoconf.h> +#include <linux/sched.h> +#include <linux/socket.h> +#include <linux/net.h> +#include <linux/un.h> +#include <linux/in.h> +#include <linux/param.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "ip.h" +#include "icmp.h" +#include "protocol.h" +#include "tcp.h" +#include "udp.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "raw.h" + +/* + * Get__netinfo returns the length of that string. + * + * KNOWN BUGS + * As in get_unix_netinfo, the buffer might be too small. If this + * happens, get__netinfo returns only part of the available infos. + */ +static int +get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length) +{ + struct sock **s_array; + struct sock *sp; + int i; + int timer_active; + unsigned long dest, src; + unsigned short destp, srcp; + int len=0; + off_t pos=0; + off_t begin=0; + + s_array = pro->sock_array; + len+=sprintf(buffer, "sl local_address rem_address st tx_queue rx_queue tr tm->when uid\n"); +/* + * This was very pretty but didn't work when a socket is destroyed at the wrong moment + * (eg a syn recv socket getting a reset), or a memory timer destroy. Instead of playing + * with timers we just concede defeat and cli(). + */ + for(i = 0; i < SOCK_ARRAY_SIZE; i++) + { + cli(); + sp = s_array[i]; + while(sp != NULL) + { + dest = sp->daddr; + src = sp->saddr; + destp = sp->dummy_th.dest; + srcp = sp->dummy_th.source; + + /* Since we are Little Endian we need to swap the bytes :-( */ + destp = ntohs(destp); + srcp = ntohs(srcp); + timer_active = del_timer(&sp->timer); + if (!timer_active) + sp->timer.expires = 0; + len+=sprintf(buffer+len, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d %d\n", + i, src, srcp, dest, destp, sp->state, + format==0?sp->write_seq-sp->rcv_ack_seq:sp->rmem_alloc, + format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc, + timer_active, sp->timer.expires, (unsigned) sp->retransmits, + sp->socket?SOCK_INODE(sp->socket)->i_uid:0, + timer_active?sp->timeout:0); + if (timer_active) + add_timer(&sp->timer); + /* + * All sockets with (port mod SOCK_ARRAY_SIZE) = i + * are kept in sock_array[i], so we must follow the + * 'next' link to get them all. + */ + sp = sp->next; + pos=begin+len; + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + sti(); /* We only turn interrupts back on for a moment, but because the interrupt queues anything built up + before this will clear before we jump back and cli, so it's not as bad as it looks */ + if(pos>offset+length) + break; + } + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} + + +int tcp_get_info(char *buffer, char **start, off_t offset, int length) +{ + return get__netinfo(&tcp_prot, buffer,0, start, offset, length); +} + + +int udp_get_info(char *buffer, char **start, off_t offset, int length) +{ + return get__netinfo(&udp_prot, buffer,1, start, offset, length); +} + + +int raw_get_info(char *buffer, char **start, off_t offset, int length) +{ + return get__netinfo(&raw_prot, buffer,1, start, offset, length); +} + + +/* + * Report socket allocation statistics [mea@utu.fi] + */ +int afinet_get_info(char *buffer, char **start, off_t offset, int length) +{ + /* From net/socket.c */ + extern int socket_get_info(char *, char **, off_t, int); + extern struct proto packet_prot; + + int len = socket_get_info(buffer,start,offset,length); + + len += sprintf(buffer+len,"SOCK_ARRAY_SIZE=%d\n",SOCK_ARRAY_SIZE); + len += sprintf(buffer+len,"TCP: inuse %d highest %d\n", + tcp_prot.inuse, tcp_prot.highestinuse); + len += sprintf(buffer+len,"UDP: inuse %d highest %d\n", + udp_prot.inuse, udp_prot.highestinuse); + len += sprintf(buffer+len,"RAW: inuse %d highest %d\n", + raw_prot.inuse, raw_prot.highestinuse); + len += sprintf(buffer+len,"PAC: inuse %d highest %d\n", + packet_prot.inuse, packet_prot.highestinuse); + *start = buffer + offset; + len -= offset; + if (len > length) + len = length; + return len; +} + + +/* + * Called from the PROCfs module. This outputs /proc/net/snmp. + */ + +int snmp_get_info(char *buffer, char **start, off_t offset, int length) +{ + extern struct tcp_mib tcp_statistics; + extern struct udp_mib udp_statistics; + int len; +/* + extern unsigned long tcp_rx_miss, tcp_rx_hit1,tcp_rx_hit2; +*/ + + len = sprintf (buffer, + "Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates\n" + "Ip: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", + ip_statistics.IpForwarding, ip_statistics.IpDefaultTTL, + ip_statistics.IpInReceives, ip_statistics.IpInHdrErrors, + ip_statistics.IpInAddrErrors, ip_statistics.IpForwDatagrams, + ip_statistics.IpInUnknownProtos, ip_statistics.IpInDiscards, + ip_statistics.IpInDelivers, ip_statistics.IpOutRequests, + ip_statistics.IpOutDiscards, ip_statistics.IpOutNoRoutes, + ip_statistics.IpReasmTimeout, ip_statistics.IpReasmReqds, + ip_statistics.IpReasmOKs, ip_statistics.IpReasmFails, + ip_statistics.IpFragOKs, ip_statistics.IpFragFails, + ip_statistics.IpFragCreates); + + len += sprintf (buffer + len, + "Icmp: InMsgs InErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps\n" + "Icmp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", + icmp_statistics.IcmpInMsgs, icmp_statistics.IcmpInErrors, + icmp_statistics.IcmpInDestUnreachs, icmp_statistics.IcmpInTimeExcds, + icmp_statistics.IcmpInParmProbs, icmp_statistics.IcmpInSrcQuenchs, + icmp_statistics.IcmpInRedirects, icmp_statistics.IcmpInEchos, + icmp_statistics.IcmpInEchoReps, icmp_statistics.IcmpInTimestamps, + icmp_statistics.IcmpInTimestampReps, icmp_statistics.IcmpInAddrMasks, + icmp_statistics.IcmpInAddrMaskReps, icmp_statistics.IcmpOutMsgs, + icmp_statistics.IcmpOutErrors, icmp_statistics.IcmpOutDestUnreachs, + icmp_statistics.IcmpOutTimeExcds, icmp_statistics.IcmpOutParmProbs, + icmp_statistics.IcmpOutSrcQuenchs, icmp_statistics.IcmpOutRedirects, + icmp_statistics.IcmpOutEchos, icmp_statistics.IcmpOutEchoReps, + icmp_statistics.IcmpOutTimestamps, icmp_statistics.IcmpOutTimestampReps, + icmp_statistics.IcmpOutAddrMasks, icmp_statistics.IcmpOutAddrMaskReps); + + len += sprintf (buffer + len, + "Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs\n" + "Tcp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", + tcp_statistics.TcpRtoAlgorithm, tcp_statistics.TcpRtoMin, + tcp_statistics.TcpRtoMax, tcp_statistics.TcpMaxConn, + tcp_statistics.TcpActiveOpens, tcp_statistics.TcpPassiveOpens, + tcp_statistics.TcpAttemptFails, tcp_statistics.TcpEstabResets, + tcp_statistics.TcpCurrEstab, tcp_statistics.TcpInSegs, + tcp_statistics.TcpOutSegs, tcp_statistics.TcpRetransSegs); + + len += sprintf (buffer + len, + "Udp: InDatagrams NoPorts InErrors OutDatagrams\nUdp: %lu %lu %lu %lu\n", + udp_statistics.UdpInDatagrams, udp_statistics.UdpNoPorts, + udp_statistics.UdpInErrors, udp_statistics.UdpOutDatagrams); +/* + len += sprintf( buffer + len, + "TCP fast path RX: H2: %ul H1: %ul L: %ul\n", + tcp_rx_hit2,tcp_rx_hit1,tcp_rx_miss); +*/ + + if (offset >= len) + { + *start = buffer; + return 0; + } + *start = buffer + offset; + len -= offset; + if (len > length) + len = length; + return len; +} + diff --git a/pfinet/linux-inet/raw.c b/pfinet/linux-inet/raw.c new file mode 100644 index 00000000..514823c4 --- /dev/null +++ b/pfinet/linux-inet/raw.c @@ -0,0 +1,319 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * RAW - implementation of IP "raw" sockets. + * + * Version: @(#)raw.c 1.0.4 05/25/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * Fixes: + * Alan Cox : verify_area() fixed up + * Alan Cox : ICMP error handling + * Alan Cox : EMSGSIZE if you send too big a packet + * Alan Cox : Now uses generic datagrams and shared skbuff + * library. No more peek crashes, no more backlogs + * Alan Cox : Checks sk->broadcast. + * Alan Cox : Uses skb_free_datagram/skb_copy_datagram + * Alan Cox : Raw passes ip options too + * Alan Cox : Setsocketopt added + * Alan Cox : Fixed error return for broadcasts + * Alan Cox : Removed wake_up calls + * Alan Cox : Use ttl/tos + * Alan Cox : Cleaned up old debugging + * Alan Cox : Use new kernel side addresses + * Arnt Gulbrandsen : Fixed MSG_DONTROUTE in raw sockets. + * Alan Cox : BSD style RAW socket demultiplexing. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <asm/system.h> +#include <asm/segment.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/fcntl.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "ip.h" +#include "protocol.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "icmp.h" +#include "udp.h" + + +static inline unsigned long min(unsigned long a, unsigned long b) +{ + if (a < b) + return(a); + return(b); +} + + +/* raw_err gets called by the icmp module. */ +void raw_err (int err, unsigned char *header, unsigned long daddr, + unsigned long saddr, struct inet_protocol *protocol) +{ + struct sock *sk; + + if (protocol == NULL) + return; + sk = (struct sock *) protocol->data; + if (sk == NULL) + return; + + /* This is meaningless in raw sockets. */ + if (err & 0xff00 == (ICMP_SOURCE_QUENCH << 8)) + { + if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2; + return; + } + + sk->err = icmp_err_convert[err & 0xff].errno; + sk->error_report(sk); + + return; +} + + +/* + * This should be the easiest of all, all we do is + * copy it into a buffer. All demultiplexing is done + * in ip.c + */ + +int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, long saddr, long daddr) +{ + /* Now we need to copy this into memory. */ + skb->sk = sk; + skb->len = ntohs(skb->ip_hdr->tot_len); + skb->h.raw = (unsigned char *) skb->ip_hdr; + skb->dev = dev; + skb->saddr = daddr; + skb->daddr = saddr; + + /* Charge it to the socket. */ + + if(sock_queue_rcv_skb(sk,skb)<0) + { + ip_statistics.IpInDiscards++; + skb->sk=NULL; + kfree_skb(skb, FREE_READ); + return(0); + } + + ip_statistics.IpInDelivers++; + release_sock(sk); + return(0); +} + +/* + * Send a RAW IP packet. + */ + +static int raw_sendto(struct sock *sk, unsigned char *from, + int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) +{ + struct sk_buff *skb; + struct device *dev=NULL; + struct sockaddr_in sin; + int tmp; + int err; + + /* + * Check the flags. Only MSG_DONTROUTE is permitted. + */ + + if (flags & MSG_OOB) /* Mirror BSD error message compatibility */ + return -EOPNOTSUPP; + + if (flags & ~MSG_DONTROUTE) + return(-EINVAL); + /* + * Get and verify the address. + */ + + if (usin) + { + if (addr_len < sizeof(sin)) + return(-EINVAL); + memcpy(&sin, usin, sizeof(sin)); + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EINVAL); + } + else + { + if (sk->state != TCP_ESTABLISHED) + return(-EINVAL); + sin.sin_family = AF_INET; + sin.sin_port = sk->protocol; + sin.sin_addr.s_addr = sk->daddr; + } + if (sin.sin_port == 0) + sin.sin_port = sk->protocol; + + if (sin.sin_addr.s_addr == INADDR_ANY) + sin.sin_addr.s_addr = ip_my_addr(); + + if (sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) + return -EACCES; + + skb=sock_alloc_send_skb(sk, len+sk->prot->max_header, noblock, &err); + if(skb==NULL) + return err; + + skb->sk = sk; + skb->free = 1; + skb->localroute = sk->localroute | (flags&MSG_DONTROUTE); + + tmp = sk->prot->build_header(skb, sk->saddr, + sin.sin_addr.s_addr, &dev, + sk->protocol, sk->opt, skb->mem_len, sk->ip_tos,sk->ip_ttl); + if (tmp < 0) + { + kfree_skb(skb,FREE_WRITE); + release_sock(sk); + return(tmp); + } + + memcpy_fromfs(skb->data + tmp, from, len); + + /* + * If we are using IPPROTO_RAW, we need to fill in the source address in + * the IP header + */ + + if(sk->protocol==IPPROTO_RAW) + { + unsigned char *buff; + struct iphdr *iph; + + buff = skb->data; + buff += tmp; + + iph = (struct iphdr *)buff; + iph->saddr = sk->saddr; + } + + skb->len = tmp + len; + + sk->prot->queue_xmit(sk, dev, skb, 1); + release_sock(sk); + return(len); +} + + +static int raw_write(struct sock *sk, unsigned char *buff, int len, int noblock, + unsigned flags) +{ + return(raw_sendto(sk, buff, len, noblock, flags, NULL, 0)); +} + + +static void raw_close(struct sock *sk, int timeout) +{ + sk->state = TCP_CLOSE; +} + + +static int raw_init(struct sock *sk) +{ + return(0); +} + + +/* + * This should be easy, if there is something there + * we return it, otherwise we block. + */ + +int raw_recvfrom(struct sock *sk, unsigned char *to, int len, + int noblock, unsigned flags, struct sockaddr_in *sin, + int *addr_len) +{ + int copied=0; + struct sk_buff *skb; + int err; + int truesize; + + if (flags & MSG_OOB) + return -EOPNOTSUPP; + + if (sk->shutdown & RCV_SHUTDOWN) + return(0); + + if (addr_len) + *addr_len=sizeof(*sin); + + skb=skb_recv_datagram(sk,flags,noblock,&err); + if(skb==NULL) + return err; + + truesize=skb->len; + copied = min(len, truesize); + + skb_copy_datagram(skb, 0, to, copied); + sk->stamp=skb->stamp; + + /* Copy the address. */ + if (sin) + { + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = skb->daddr; + } + skb_free_datagram(skb); + release_sock(sk); + return (truesize); /* len not copied. BSD returns the true size of the message so you know a bit fell off! */ +} + + +int raw_read (struct sock *sk, unsigned char *buff, int len, int noblock,unsigned flags) +{ + return(raw_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); +} + + +struct proto raw_prot = { + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + raw_close, + raw_read, + raw_write, + raw_sendto, + raw_recvfrom, + ip_build_header, + udp_connect, + NULL, + ip_queue_xmit, + NULL, + NULL, + NULL, + NULL, + datagram_select, + NULL, + raw_init, + NULL, + ip_setsockopt, + ip_getsockopt, + 128, + 0, + {NULL,}, + "RAW", + 0, 0 +}; diff --git a/pfinet/linux-inet/route.c b/pfinet/linux-inet/route.c new file mode 100644 index 00000000..22709020 --- /dev/null +++ b/pfinet/linux-inet/route.c @@ -0,0 +1,672 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * ROUTE - implementation of the IP router. + * + * Version: @(#)route.c 1.0.14 05/31/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * Linus Torvalds, <Linus.Torvalds@helsinki.fi> + * + * Fixes: + * Alan Cox : Verify area fixes. + * Alan Cox : cli() protects routing changes + * Rui Oliveira : ICMP routing table updates + * (rco@di.uminho.pt) Routing table insertion and update + * Linus Torvalds : Rewrote bits to be sensible + * Alan Cox : Added BSD route gw semantics + * Alan Cox : Super /proc >4K + * Alan Cox : MTU in route table + * Alan Cox : MSS actually. Also added the window + * clamper. + * Sam Lantinga : Fixed route matching in rt_del() + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/errno.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "ip.h" +#include "protocol.h" +#include "route.h" +#include "tcp.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "icmp.h" + +/* + * The routing table list + */ + +static struct rtable *rt_base = NULL; + +/* + * Pointer to the loopback route + */ + +static struct rtable *rt_loopback = NULL; + +/* + * Remove a routing table entry. + */ + +static void rt_del(unsigned long dst, char *devname) +{ + struct rtable *r, **rp; + unsigned long flags; + + rp = &rt_base; + + /* + * This must be done with interrupts off because we could take + * an ICMP_REDIRECT. + */ + + save_flags(flags); + cli(); + while((r = *rp) != NULL) + { + /* Make sure both the destination and the device match */ + if ( r->rt_dst != dst || + (devname != NULL && strcmp((r->rt_dev)->name,devname) != 0) ) + { + rp = &r->rt_next; + continue; + } + *rp = r->rt_next; + + /* + * If we delete the loopback route update its pointer. + */ + + if (rt_loopback == r) + rt_loopback = NULL; + kfree_s(r, sizeof(struct rtable)); + } + restore_flags(flags); +} + + +/* + * Remove all routing table entries for a device. This is called when + * a device is downed. + */ + +void ip_rt_flush(struct device *dev) +{ + struct rtable *r; + struct rtable **rp; + unsigned long flags; + + rp = &rt_base; + save_flags(flags); + cli(); + while ((r = *rp) != NULL) { + if (r->rt_dev != dev) { + rp = &r->rt_next; + continue; + } + *rp = r->rt_next; + if (rt_loopback == r) + rt_loopback = NULL; + kfree_s(r, sizeof(struct rtable)); + } + restore_flags(flags); +} + +/* + * Used by 'rt_add()' when we can't get the netmask any other way.. + * + * If the lower byte or two are zero, we guess the mask based on the + * number of zero 8-bit net numbers, otherwise we use the "default" + * masks judging by the destination address and our device netmask. + */ + +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); +} + + +/* + * If no mask is specified then generate a default entry. + */ + +static unsigned long guess_mask(unsigned long dst, struct device * dev) +{ + unsigned long mask; + + if (!dst) + return 0; + mask = default_mask(dst); + if ((dst ^ dev->pa_addr) & mask) + return mask; + return dev->pa_mask; +} + + +/* + * Find the route entry through which our gateway will be reached + */ + +static inline struct device * get_gw_dev(unsigned long gw) +{ + struct rtable * rt; + + for (rt = rt_base ; ; rt = rt->rt_next) + { + if (!rt) + return NULL; + if ((gw ^ rt->rt_dst) & rt->rt_mask) + continue; + /* + * Gateways behind gateways are a no-no + */ + + if (rt->rt_flags & RTF_GATEWAY) + return NULL; + return rt->rt_dev; + } +} + +/* + * Rewrote rt_add(), as the old one was weird - Linus + * + * This routine is used to update the IP routing table, either + * from the kernel (ICMP_REDIRECT) or via an ioctl call issued + * by the superuser. + */ + +void ip_rt_add(short flags, unsigned long dst, unsigned long mask, + unsigned long gw, struct device *dev, unsigned short mtu, unsigned long window) +{ + struct rtable *r, *rt; + struct rtable **rp; + unsigned long cpuflags; + + /* + * A host is a unique machine and has no network bits. + */ + + if (flags & RTF_HOST) + { + mask = 0xffffffff; + } + + /* + * Calculate the network mask + */ + + else if (!mask) + { + if (!((dst ^ dev->pa_addr) & dev->pa_mask)) + { + mask = dev->pa_mask; + flags &= ~RTF_GATEWAY; + if (flags & RTF_DYNAMIC) + { + /*printk("Dynamic route to my own net rejected\n");*/ + return; + } + } + else + mask = guess_mask(dst, dev); + dst &= mask; + } + + /* + * A gateway must be reachable and not a local address + */ + + if (gw == dev->pa_addr) + flags &= ~RTF_GATEWAY; + + if (flags & RTF_GATEWAY) + { + /* + * Don't try to add a gateway we can't reach.. + */ + + if (dev != get_gw_dev(gw)) + return; + + flags |= RTF_GATEWAY; + } + else + gw = 0; + + /* + * Allocate an entry and fill it in. + */ + + rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC); + if (rt == NULL) + { + return; + } + memset(rt, 0, sizeof(struct rtable)); + rt->rt_flags = flags | RTF_UP; + rt->rt_dst = dst; + rt->rt_dev = dev; + rt->rt_gateway = gw; + rt->rt_mask = mask; + rt->rt_mss = dev->mtu - HEADER_SIZE; + rt->rt_window = 0; /* Default is no clamping */ + + /* Are the MSS/Window valid ? */ + + if(rt->rt_flags & RTF_MSS) + rt->rt_mss = mtu; + + if(rt->rt_flags & RTF_WINDOW) + rt->rt_window = window; + + /* + * What we have to do is loop though this until we have + * found the first address which has a higher generality than + * the one in rt. Then we can put rt in right before it. + * The interrupts must be off for this process. + */ + + save_flags(cpuflags); + cli(); + + /* + * Remove old route if we are getting a duplicate. + */ + + rp = &rt_base; + while ((r = *rp) != NULL) + { + if (r->rt_dst != dst || + r->rt_mask != mask) + { + rp = &r->rt_next; + continue; + } + *rp = r->rt_next; + if (rt_loopback == r) + rt_loopback = NULL; + kfree_s(r, sizeof(struct rtable)); + } + + /* + * Add the new route + */ + + rp = &rt_base; + while ((r = *rp) != NULL) { + if ((r->rt_mask & mask) != mask) + break; + rp = &r->rt_next; + } + rt->rt_next = r; + *rp = rt; + + /* + * Update the loopback route + */ + + if ((rt->rt_dev->flags & IFF_LOOPBACK) && !rt_loopback) + rt_loopback = rt; + + /* + * Restore the interrupts and return + */ + + restore_flags(cpuflags); + return; +} + + +/* + * Check if a mask is acceptable. + */ + +static inline int bad_mask(unsigned long mask, unsigned long addr) +{ + if (addr & (mask = ~mask)) + return 1; + mask = ntohl(mask); + if (mask & (mask+1)) + return 1; + return 0; +} + +/* + * Process a route add request from the user + */ + +static int rt_new(struct rtentry *r) +{ + int err; + char * devname; + struct device * dev = NULL; + unsigned long flags, daddr, mask, gw; + + /* + * If a device is specified find it. + */ + + if ((devname = r->rt_dev) != NULL) + { + err = getname(devname, &devname); + if (err) + return err; + dev = dev_get(devname); + putname(devname); + if (!dev) + return -EINVAL; + } + + /* + * If the device isn't INET, don't allow it + */ + + if (r->rt_dst.sa_family != AF_INET) + return -EAFNOSUPPORT; + + /* + * Make local copies of the important bits + */ + + flags = r->rt_flags; + daddr = ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr; + mask = ((struct sockaddr_in *) &r->rt_genmask)->sin_addr.s_addr; + gw = ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr; + + + /* + * BSD emulation: Permits route add someroute gw one-of-my-addresses + * to indicate which iface. Not as clean as the nice Linux dev technique + * but people keep using it... + */ + + if (!dev && (flags & RTF_GATEWAY)) + { + struct device *dev2; + for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next) + { + if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw) + { + flags &= ~RTF_GATEWAY; + dev = dev2; + break; + } + } + } + + /* + * Ignore faulty masks + */ + + if (bad_mask(mask, daddr)) + mask = 0; + + /* + * Set the mask to nothing for host routes. + */ + + if (flags & RTF_HOST) + mask = 0xffffffff; + else if (mask && r->rt_genmask.sa_family != AF_INET) + return -EAFNOSUPPORT; + + /* + * You can only gateway IP via IP.. + */ + + if (flags & RTF_GATEWAY) + { + if (r->rt_gateway.sa_family != AF_INET) + return -EAFNOSUPPORT; + if (!dev) + dev = get_gw_dev(gw); + } + else if (!dev) + dev = ip_dev_check(daddr); + + /* + * Unknown device. + */ + + if (dev == NULL) + return -ENETUNREACH; + + /* + * Add the route + */ + + ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window); + return 0; +} + + +/* + * Remove a route, as requested by the user. + */ + +static int rt_kill(struct rtentry *r) +{ + struct sockaddr_in *trg; + char *devname; + int err; + + trg = (struct sockaddr_in *) &r->rt_dst; + if ((devname = r->rt_dev) != NULL) + { + err = getname(devname, &devname); + if (err) + return err; + } + rt_del(trg->sin_addr.s_addr, devname); + if ( devname != NULL ) + putname(devname); + return 0; +} + + +/* + * Called from the PROCfs module. This outputs /proc/net/route. + */ + +int rt_get_info(char *buffer, char **start, off_t offset, int length) +{ + struct rtable *r; + int len=0; + off_t pos=0; + off_t begin=0; + int size; + + len += sprintf(buffer, + "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\n"); + pos=len; + + /* + * This isn't quite right -- r->rt_dst is a struct! + */ + + for (r = rt_base; r != NULL; r = r->rt_next) + { + size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\n", + r->rt_dev->name, r->rt_dst, r->rt_gateway, + r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric, + r->rt_mask, (int)r->rt_mss, r->rt_window); + len+=size; + pos+=size; + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} + +/* + * This is hackish, but results in better code. Use "-S" to see why. + */ + +#define early_out ({ goto no_route; 1; }) + +/* + * Route a packet. This needs to be fairly quick. Florian & Co. + * suggested a unified ARP and IP routing cache. Done right its + * probably a brilliant idea. I'd actually suggest a unified + * ARP/IP routing/Socket pointer cache. Volunteers welcome + */ + +struct rtable * ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr) +{ + struct rtable *rt; + + for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) + { + if (!((rt->rt_dst ^ daddr) & rt->rt_mask)) + break; + /* + * broadcast addresses can be special cases.. + */ + if (rt->rt_flags & RTF_GATEWAY) + continue; + if ((rt->rt_dev->flags & IFF_BROADCAST) && + (rt->rt_dev->pa_brdaddr == daddr)) + break; + } + + if(src_addr!=NULL) + *src_addr= rt->rt_dev->pa_addr; + + if (daddr == rt->rt_dev->pa_addr) { + if ((rt = rt_loopback) == NULL) + goto no_route; + } + rt->rt_use++; + return rt; +no_route: + return NULL; +} + +struct rtable * ip_rt_local(unsigned long daddr, struct options *opt, unsigned long *src_addr) +{ + struct rtable *rt; + + for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) + { + /* + * No routed addressing. + */ + if (rt->rt_flags&RTF_GATEWAY) + continue; + + if (!((rt->rt_dst ^ daddr) & rt->rt_mask)) + break; + /* + * broadcast addresses can be special cases.. + */ + + if ((rt->rt_dev->flags & IFF_BROADCAST) && + rt->rt_dev->pa_brdaddr == daddr) + break; + } + + if(src_addr!=NULL) + *src_addr= rt->rt_dev->pa_addr; + + if (daddr == rt->rt_dev->pa_addr) { + if ((rt = rt_loopback) == NULL) + goto no_route; + } + rt->rt_use++; + return rt; +no_route: + return NULL; +} + +/* + * Backwards compatibility + */ + +static int ip_get_old_rtent(struct old_rtentry * src, struct rtentry * rt) +{ + int err; + struct old_rtentry tmp; + + err=verify_area(VERIFY_READ, src, sizeof(*src)); + if (err) + return err; + memcpy_fromfs(&tmp, src, sizeof(*src)); + memset(rt, 0, sizeof(*rt)); + rt->rt_dst = tmp.rt_dst; + rt->rt_gateway = tmp.rt_gateway; + rt->rt_genmask.sa_family = AF_INET; + ((struct sockaddr_in *) &rt->rt_genmask)->sin_addr.s_addr = tmp.rt_genmask; + rt->rt_flags = tmp.rt_flags; + rt->rt_dev = tmp.rt_dev; + printk("Warning: obsolete routing request made.\n"); + return 0; +} + +/* + * Handle IP routing ioctl calls. These are used to manipulate the routing tables + */ + +int ip_rt_ioctl(unsigned int cmd, void *arg) +{ + int err; + struct rtentry rt; + + switch(cmd) + { + case SIOCADDRTOLD: /* Old style add route */ + case SIOCDELRTOLD: /* Old style delete route */ + if (!suser()) + return -EPERM; + err = ip_get_old_rtent((struct old_rtentry *) arg, &rt); + if (err) + return err; + return (cmd == SIOCDELRTOLD) ? rt_kill(&rt) : rt_new(&rt); + + case SIOCADDRT: /* Add a route */ + case SIOCDELRT: /* Delete a route */ + if (!suser()) + return -EPERM; + err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry)); + if (err) + return err; + memcpy_fromfs(&rt, arg, sizeof(struct rtentry)); + return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt); + } + + return -EINVAL; +} diff --git a/pfinet/linux-inet/sock.h b/pfinet/linux-inet/sock.h new file mode 100644 index 00000000..4ed353fd --- /dev/null +++ b/pfinet/linux-inet/sock.h @@ -0,0 +1,316 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the AF_INET socket handler. + * + * Version: @(#)sock.h 1.0.4 05/13/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * Florian La Roche <flla@stud.uni-sb.de> + * + * Fixes: + * Alan Cox : Volatiles in skbuff pointers. See + * skbuff comments. May be overdone, + * better to prove they can be removed + * than the reverse. + * Alan Cox : Added a zapped field for tcp to note + * a socket is reset and must stay shut up + * Alan Cox : New fields for options + * Pauline Middelink : identd support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _SOCK_H +#define _SOCK_H + +#include <linux/timer.h> +#include <linux/ip.h> /* struct options */ +#include <linux/tcp.h> /* struct tcphdr */ +#include <linux/config.h> + +#include <linux/skbuff.h> /* struct sk_buff */ +#include "protocol.h" /* struct inet_protocol */ +#ifdef CONFIG_AX25 +#include "ax25.h" +#endif +#ifdef CONFIG_IPX +#include "ipx.h" +#endif +#ifdef CONFIG_ATALK +#include <linux/atalk.h> +#endif + +#include <linux/igmp.h> + +#define SOCK_ARRAY_SIZE 256 /* Think big (also on some systems a byte is faster */ + + +/* + * This structure really needs to be cleaned up. + * Most of it is for TCP, and not used by any of + * the other protocols. + */ +struct sock { + struct options *opt; + volatile unsigned long wmem_alloc; + volatile unsigned long rmem_alloc; + unsigned long write_seq; + unsigned long sent_seq; + unsigned long acked_seq; + unsigned long copied_seq; + unsigned long rcv_ack_seq; + unsigned long window_seq; + unsigned long fin_seq; + unsigned long urg_seq; + unsigned long urg_data; + + /* + * Not all are volatile, but some are, so we + * might as well say they all are. + */ + volatile char inuse, + dead, + urginline, + intr, + blog, + done, + reuse, + keepopen, + linger, + delay_acks, + destroy, + ack_timed, + no_check, + zapped, /* In ax25 & ipx means not linked */ + broadcast, + nonagle; + unsigned long lingertime; + int proc; + struct sock *next; + struct sock *prev; /* Doubly linked chain.. */ + struct sock *pair; + struct sk_buff * volatile send_head; + struct sk_buff * volatile send_tail; + struct sk_buff_head back_log; + struct sk_buff *partial; + struct timer_list partial_timer; + long retransmits; + struct sk_buff_head write_queue, + receive_queue; + struct proto *prot; + struct wait_queue **sleep; + unsigned long daddr; + unsigned long saddr; + unsigned short max_unacked; + unsigned short window; + unsigned short bytes_rcv; +/* mss is min(mtu, max_window) */ + unsigned short mtu; /* mss negotiated in the syn's */ + volatile unsigned short mss; /* current eff. mss - can change */ + volatile unsigned short user_mss; /* mss requested by user in ioctl */ + volatile unsigned short max_window; + unsigned long window_clamp; + unsigned short num; + volatile unsigned short cong_window; + volatile unsigned short cong_count; + volatile unsigned short ssthresh; + volatile unsigned short packets_out; + volatile unsigned short shutdown; + volatile unsigned long rtt; + volatile unsigned long mdev; + volatile unsigned long rto; +/* currently backoff isn't used, but I'm maintaining it in case + * we want to go back to a backoff formula that needs it + */ + volatile unsigned short backoff; + volatile short err; + unsigned char protocol; + volatile unsigned char state; + volatile unsigned char ack_backlog; + unsigned char max_ack_backlog; + unsigned char priority; + unsigned char debug; + unsigned short rcvbuf; + unsigned short sndbuf; + unsigned short type; + unsigned char localroute; /* Route locally only */ +#ifdef CONFIG_IPX + ipx_address ipx_dest_addr; + ipx_interface *ipx_intrfc; + unsigned short ipx_port; + unsigned short ipx_type; +#endif +#ifdef CONFIG_AX25 +/* Really we want to add a per protocol private area */ + ax25_address ax25_source_addr,ax25_dest_addr; + struct sk_buff *volatile ax25_retxq[8]; + char ax25_state,ax25_vs,ax25_vr,ax25_lastrxnr,ax25_lasttxnr; + char ax25_condition; + char ax25_retxcnt; + char ax25_xx; + char ax25_retxqi; + char ax25_rrtimer; + char ax25_timer; + unsigned char ax25_n2; + unsigned short ax25_t1,ax25_t2,ax25_t3; + ax25_digi *ax25_digipeat; +#endif +#ifdef CONFIG_ATALK + struct atalk_sock at; +#endif + +/* IP 'private area' or will be eventually */ + int ip_ttl; /* TTL setting */ + int ip_tos; /* TOS */ + struct tcphdr dummy_th; + struct timer_list keepalive_timer; /* TCP keepalive hack */ + struct timer_list retransmit_timer; /* TCP retransmit timer */ + struct timer_list ack_timer; /* TCP delayed ack timer */ + int ip_xmit_timeout; /* Why the timeout is running */ +#ifdef CONFIG_IP_MULTICAST + int ip_mc_ttl; /* Multicasting TTL */ + int ip_mc_loop; /* Loopback (not implemented yet) */ + char ip_mc_name[MAX_ADDR_LEN]; /* Multicast device name */ + struct ip_mc_socklist *ip_mc_list; /* Group array */ +#endif + + /* This part is used for the timeout functions (timer.c). */ + int timeout; /* What are we waiting for? */ + struct timer_list timer; /* This is the TIME_WAIT/receive timer when we are doing IP */ + struct timeval stamp; + + /* identd */ + struct socket *socket; + + /* Callbacks */ + void (*state_change)(struct sock *sk); + void (*data_ready)(struct sock *sk,int bytes); + void (*write_space)(struct sock *sk); + void (*error_report)(struct sock *sk); + +}; + +struct proto { + struct sk_buff * (*wmalloc)(struct sock *sk, + unsigned long size, int force, + int priority); + struct sk_buff * (*rmalloc)(struct sock *sk, + unsigned long size, int force, + int priority); + void (*wfree)(struct sock *sk, struct sk_buff *skb, + unsigned long size); + void (*rfree)(struct sock *sk, struct sk_buff *skb, + unsigned long size); + unsigned long (*rspace)(struct sock *sk); + unsigned long (*wspace)(struct sock *sk); + void (*close)(struct sock *sk, int timeout); + int (*read)(struct sock *sk, unsigned char *to, + int len, int nonblock, unsigned flags); + int (*write)(struct sock *sk, unsigned char *to, + int len, int nonblock, unsigned flags); + int (*sendto)(struct sock *sk, + unsigned char *from, int len, int noblock, + unsigned flags, struct sockaddr_in *usin, + int addr_len); + int (*recvfrom)(struct sock *sk, + unsigned char *from, int len, int noblock, + unsigned flags, struct sockaddr_in *usin, + int *addr_len); + int (*build_header)(struct sk_buff *skb, + unsigned long saddr, + unsigned long daddr, + struct device **dev, int type, + struct options *opt, int len, int tos, int ttl); + int (*connect)(struct sock *sk, + struct sockaddr_in *usin, int addr_len); + struct sock * (*accept) (struct sock *sk, int flags); + void (*queue_xmit)(struct sock *sk, + struct device *dev, struct sk_buff *skb, + int free); + void (*retransmit)(struct sock *sk, int all); + void (*write_wakeup)(struct sock *sk); + void (*read_wakeup)(struct sock *sk); + int (*rcv)(struct sk_buff *buff, struct device *dev, + struct options *opt, unsigned long daddr, + unsigned short len, unsigned long saddr, + int redo, struct inet_protocol *protocol); + int (*select)(struct sock *sk, int which, + select_table *wait); + int (*ioctl)(struct sock *sk, int cmd, + unsigned long arg); + int (*init)(struct sock *sk); + void (*shutdown)(struct sock *sk, int how); + int (*setsockopt)(struct sock *sk, int level, int optname, + char *optval, int optlen); + int (*getsockopt)(struct sock *sk, int level, int optname, + char *optval, int *option); + unsigned short max_header; + unsigned long retransmits; + struct sock * sock_array[SOCK_ARRAY_SIZE]; + char name[80]; + int inuse, highestinuse; +}; + +#define TIME_WRITE 1 +#define TIME_CLOSE 2 +#define TIME_KEEPOPEN 3 +#define TIME_DESTROY 4 +#define TIME_DONE 5 /* used to absorb those last few packets */ +#define TIME_PROBE0 6 +#define SOCK_DESTROY_TIME 1000 /* about 10 seconds */ + +#define PROT_SOCK 1024 /* Sockets 0-1023 can't be bound too unless you are superuser */ + +#define SHUTDOWN_MASK 3 +#define RCV_SHUTDOWN 1 +#define SEND_SHUTDOWN 2 + + +extern void destroy_sock(struct sock *sk); +extern unsigned short get_new_socknum(struct proto *, unsigned short); +extern void put_sock(unsigned short, struct sock *); +extern void release_sock(struct sock *sk); +extern struct sock *get_sock(struct proto *, unsigned short, + unsigned long, unsigned short, + unsigned long); +extern struct sock *get_sock_mcast(struct sock *, unsigned short, + unsigned long, unsigned short, + unsigned long); +extern struct sock *get_sock_raw(struct sock *, unsigned short, + unsigned long, unsigned long); + +extern struct sk_buff *sock_wmalloc(struct sock *sk, + unsigned long size, int force, + int priority); +extern struct sk_buff *sock_rmalloc(struct sock *sk, + unsigned long size, int force, + int priority); +extern void sock_wfree(struct sock *sk, struct sk_buff *skb, + unsigned long size); +extern void sock_rfree(struct sock *sk, struct sk_buff *skb, + unsigned long size); +extern unsigned long sock_rspace(struct sock *sk); +extern unsigned long sock_wspace(struct sock *sk); + +extern int sock_setsockopt(struct sock *sk,int level,int op,char *optval,int optlen); + +extern int sock_getsockopt(struct sock *sk,int level,int op,char *optval,int *optlen); +extern struct sk_buff *sock_alloc_send_skb(struct sock *skb, unsigned long size, int noblock, int *errcode); +extern int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); + +/* declarations from timer.c */ +extern struct sock *timer_base; + +void delete_timer (struct sock *); +void reset_timer (struct sock *, int, unsigned long); +void net_timer (unsigned long); + + +#endif /* _SOCK_H */ diff --git a/pfinet/linux-inet/tcp.c b/pfinet/linux-inet/tcp.c new file mode 100644 index 00000000..c73ad07f --- /dev/null +++ b/pfinet/linux-inet/tcp.c @@ -0,0 +1,5100 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Implementation of the Transmission Control Protocol(TCP). + * + * Version: @(#)tcp.c 1.0.16 05/25/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * Florian La Roche, <flla@stud.uni-sb.de> + * Charles Hedrick, <hedrick@klinzhai.rutgers.edu> + * Linus Torvalds, <torvalds@cs.helsinki.fi> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * Matthew Dillon, <dillon@apollo.west.oic.com> + * Arnt Gulbrandsen, <agulbra@no.unit.nvg> + * + * Fixes: + * Alan Cox : Numerous verify_area() calls + * Alan Cox : Set the ACK bit on a reset + * Alan Cox : Stopped it crashing if it closed while sk->inuse=1 + * and was trying to connect (tcp_err()). + * Alan Cox : All icmp error handling was broken + * pointers passed where wrong and the + * socket was looked up backwards. Nobody + * tested any icmp error code obviously. + * Alan Cox : tcp_err() now handled properly. It wakes people + * on errors. select behaves and the icmp error race + * has gone by moving it into sock.c + * Alan Cox : tcp_reset() fixed to work for everything not just + * packets for unknown sockets. + * Alan Cox : tcp option processing. + * Alan Cox : Reset tweaked (still not 100%) [Had syn rule wrong] + * Herp Rosmanith : More reset fixes + * Alan Cox : No longer acks invalid rst frames. Acking + * any kind of RST is right out. + * Alan Cox : Sets an ignore me flag on an rst receive + * otherwise odd bits of prattle escape still + * Alan Cox : Fixed another acking RST frame bug. Should stop + * LAN workplace lockups. + * Alan Cox : Some tidyups using the new skb list facilities + * Alan Cox : sk->keepopen now seems to work + * Alan Cox : Pulls options out correctly on accepts + * Alan Cox : Fixed assorted sk->rqueue->next errors + * Alan Cox : PSH doesn't end a TCP read. Switched a bit to skb ops. + * Alan Cox : Tidied tcp_data to avoid a potential nasty. + * Alan Cox : Added some better commenting, as the tcp is hard to follow + * Alan Cox : Removed incorrect check for 20 * psh + * Michael O'Reilly : ack < copied bug fix. + * Johannes Stille : Misc tcp fixes (not all in yet). + * Alan Cox : FIN with no memory -> CRASH + * Alan Cox : Added socket option proto entries. Also added awareness of them to accept. + * Alan Cox : Added TCP options (SOL_TCP) + * Alan Cox : Switched wakeup calls to callbacks, so the kernel can layer network sockets. + * Alan Cox : Use ip_tos/ip_ttl settings. + * Alan Cox : Handle FIN (more) properly (we hope). + * Alan Cox : RST frames sent on unsynchronised state ack error/ + * Alan Cox : Put in missing check for SYN bit. + * Alan Cox : Added tcp_select_window() aka NET2E + * window non shrink trick. + * Alan Cox : Added a couple of small NET2E timer fixes + * Charles Hedrick : TCP fixes + * Toomas Tamm : TCP window fixes + * Alan Cox : Small URG fix to rlogin ^C ack fight + * Charles Hedrick : Rewrote most of it to actually work + * Linus : Rewrote tcp_read() and URG handling + * completely + * Gerhard Koerting: Fixed some missing timer handling + * Matthew Dillon : Reworked TCP machine states as per RFC + * Gerhard Koerting: PC/TCP workarounds + * Adam Caldwell : Assorted timer/timing errors + * Matthew Dillon : Fixed another RST bug + * Alan Cox : Move to kernel side addressing changes. + * Alan Cox : Beginning work on TCP fastpathing (not yet usable) + * Arnt Gulbrandsen: Turbocharged tcp_check() routine. + * Alan Cox : TCP fast path debugging + * Alan Cox : Window clamping + * Michael Riepe : Bug in tcp_check() + * Matt Dillon : More TCP improvements and RST bug fixes + * Matt Dillon : Yet more small nasties remove from the TCP code + * (Be very nice to this man if tcp finally works 100%) 8) + * Alan Cox : BSD accept semantics. + * Alan Cox : Reset on closedown bug. + * Peter De Schrijver : ENOTCONN check missing in tcp_sendto(). + * Michael Pall : Handle select() after URG properly in all cases. + * Michael Pall : Undo the last fix in tcp_read_urg() (multi URG PUSH broke rlogin). + * Michael Pall : Fix the multi URG PUSH problem in tcp_readable(), select() after URG works now. + * Michael Pall : recv(...,MSG_OOB) never blocks in the BSD api. + * Alan Cox : Changed the semantics of sk->socket to + * fix a race and a signal problem with + * accept() and async I/O. + * Alan Cox : Relaxed the rules on tcp_sendto(). + * Yury Shevchuk : Really fixed accept() blocking problem. + * Craig I. Hagan : Allow for BSD compatible TIME_WAIT for + * clients/servers which listen in on + * fixed ports. + * Alan Cox : Cleaned the above up and shrank it to + * a sensible code size. + * Alan Cox : Self connect lockup fix. + * Alan Cox : No connect to multicast. + * Ross Biro : Close unaccepted children on master + * socket close. + * Alan Cox : Reset tracing code. + * Alan Cox : Spurious resets on shutdown. + * Alan Cox : Giant 15 minute/60 second timer error + * Alan Cox : Small whoops in selecting before an accept. + * Alan Cox : Kept the state trace facility since it's + * handy for debugging. + * Alan Cox : More reset handler fixes. + * Alan Cox : Started rewriting the code based on the RFC's + * for other useful protocol references see: + * Comer, KA9Q NOS, and for a reference on the + * difference between specifications and how BSD + * works see the 4.4lite source. + * A.N.Kuznetsov : Don't time wait on completion of tidy + * close. + * Linus Torvalds : Fin/Shutdown & copied_seq changes. + * Linus Torvalds : Fixed BSD port reuse to work first syn + * Alan Cox : Reimplemented timers as per the RFC and using multiple + * timers for sanity. + * Alan Cox : Small bug fixes, and a lot of new + * comments. + * Alan Cox : Fixed dual reader crash by locking + * the buffers (much like datagram.c) + * Alan Cox : Fixed stuck sockets in probe. A probe + * now gets fed up of retrying without + * (even a no space) answer. + * Alan Cox : Extracted closing code better + * Alan Cox : Fixed the closing state machine to + * resemble the RFC. + * Alan Cox : More 'per spec' fixes. + * Alan Cox : tcp_data() doesn't ack illegal PSH + * only frames. At least one pc tcp stack + * generates them. + * + * + * To Fix: + * Fast path the code. Two things here - fix the window calculation + * so it doesn't iterate over the queue, also spot packets with no funny + * options arriving in order and process directly. + * + * Implement RFC 1191 [Path MTU discovery] + * Look at the effect of implementing RFC 1337 suggestions and their impact. + * Rewrite output state machine to use a single queue and do low window + * situations as per the spec (RFC 1122) + * Speed up input assembly algorithm. + * RFC1323 - PAWS and window scaling. PAWS is required for IPv6 so we + * could do with it working on IPv4 + * User settable/learned rtt/max window/mtu + * Cope with MTU/device switches when retransmitting in tcp. + * Fix the window handling to use PR's new code. + * + * Change the fundamental structure to a single send queue maintained + * by TCP (removing the bogus ip stuff [thus fixing mtu drops on + * active routes too]). Cut the queue off in tcp_retransmit/ + * tcp_transmit. + * Change the receive queue to assemble as it goes. This lets us + * dispose of most of tcp_sequence, half of tcp_ack and chunks of + * tcp_data/tcp_read as well as the window shrink crud. + * Separate out duplicated code - tcp_alloc_skb, tcp_build_ack + * tcp_queue_skb seem obvious routines to extract. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or(at your option) any later version. + * + * Description of States: + * + * TCP_SYN_SENT sent a connection request, waiting for ack + * + * TCP_SYN_RECV received a connection request, sent ack, + * waiting for final ack in three-way handshake. + * + * TCP_ESTABLISHED connection established + * + * TCP_FIN_WAIT1 our side has shutdown, waiting to complete + * transmission of remaining buffered data + * + * TCP_FIN_WAIT2 all buffered data sent, waiting for remote + * to shutdown + * + * TCP_CLOSING both sides have shutdown but we still have + * data we have to finish sending + * + * TCP_TIME_WAIT timeout to catch resent junk before entering + * closed, can only be entered from FIN_WAIT2 + * or CLOSING. Required because the other end + * may not have gotten our last ACK causing it + * to retransmit the data packet (which we ignore) + * + * TCP_CLOSE_WAIT remote side has shutdown and is waiting for + * us to finish writing our data and to shutdown + * (we have to close() to move on to LAST_ACK) + * + * TCP_LAST_ACK out side has shutdown after remote has + * shutdown. There may still be data in our + * buffer that we have to finish sending + * + * TCP_CLOSE socket is finished + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/time.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/termios.h> +#include <linux/in.h> +#include <linux/fcntl.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "snmp.h" +#include "ip.h" +#include "protocol.h" +#include "icmp.h" +#include "tcp.h" +#include "arp.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "route.h" +#include <linux/errno.h> +#include <linux/timer.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <linux/mm.h> + +/* + * The MSL timer is the 'normal' timer. + */ + +#define reset_msl_timer(x,y,z) reset_timer(x,y,z) + +#define SEQ_TICK 3 +unsigned long seq_offset; +struct tcp_mib tcp_statistics; + +static void tcp_close(struct sock *sk, int timeout); + + +/* + * The less said about this the better, but it works and will do for 1.2 + */ + +static struct wait_queue *master_select_wakeup; + +static __inline__ int min(unsigned int a, unsigned int b) +{ + if (a < b) + return(a); + return(b); +} + +#undef STATE_TRACE + +#ifdef STATE_TRACE +static char *statename[]={ + "Unused","Established","Syn Sent","Syn Recv", + "Fin Wait 1","Fin Wait 2","Time Wait", "Close", + "Close Wait","Last ACK","Listen","Closing" +}; +#endif + +static __inline__ void tcp_set_state(struct sock *sk, int state) +{ + if(sk->state==TCP_ESTABLISHED) + tcp_statistics.TcpCurrEstab--; +#ifdef STATE_TRACE + if(sk->debug) + printk("TCP sk=%p, State %s -> %s\n",sk, statename[sk->state],statename[state]); +#endif + /* This is a hack but it doesn't occur often and it's going to + be a real to fix nicely */ + + if(state==TCP_ESTABLISHED && sk->state==TCP_SYN_RECV) + { + wake_up_interruptible(&master_select_wakeup); + } + sk->state=state; + if(state==TCP_ESTABLISHED) + tcp_statistics.TcpCurrEstab++; +} + +/* + * This routine picks a TCP windows for a socket based on + * the following constraints + * + * 1. The window can never be shrunk once it is offered (RFC 793) + * 2. We limit memory per socket + * + * For now we use NET2E3's heuristic of offering half the memory + * we have handy. All is not as bad as this seems however because + * of two things. Firstly we will bin packets even within the window + * in order to get the data we are waiting for into the memory limit. + * Secondly we bin common duplicate forms at receive time + * Better heuristics welcome + */ + +int tcp_select_window(struct sock *sk) +{ + int new_window = sk->prot->rspace(sk); + + if(sk->window_clamp) + new_window=min(sk->window_clamp,new_window); + /* + * Two things are going on here. First, we don't ever offer a + * window less than min(sk->mss, MAX_WINDOW/2). This is the + * receiver side of SWS as specified in RFC1122. + * Second, we always give them at least the window they + * had before, in order to avoid retracting window. This + * is technically allowed, but RFC1122 advises against it and + * in practice it causes trouble. + * + * Fixme: This doesn't correctly handle the case where + * new_window > sk->window but not by enough to allow for the + * shift in sequence space. + */ + if (new_window < min(sk->mss, MAX_WINDOW/2) || new_window < sk->window) + return(sk->window); + return(new_window); +} + +/* + * Find someone to 'accept'. Must be called with + * sk->inuse=1 or cli() + */ + +static struct sk_buff *tcp_find_established(struct sock *s) +{ + struct sk_buff *p=skb_peek(&s->receive_queue); + if(p==NULL) + return NULL; + do + { + if(p->sk->state == TCP_ESTABLISHED || p->sk->state >= TCP_FIN_WAIT1) + return p; + p=p->next; + } + while(p!=(struct sk_buff *)&s->receive_queue); + return NULL; +} + +/* + * Remove a completed connection and return it. This is used by + * tcp_accept() to get connections from the queue. + */ + +static struct sk_buff *tcp_dequeue_established(struct sock *s) +{ + struct sk_buff *skb; + unsigned long flags; + save_flags(flags); + cli(); + skb=tcp_find_established(s); + if(skb!=NULL) + skb_unlink(skb); /* Take it off the queue */ + restore_flags(flags); + return skb; +} + +/* + * This routine closes sockets which have been at least partially + * opened, but not yet accepted. Currently it is only called by + * tcp_close, and timeout mirrors the value there. + */ + +static void tcp_close_pending (struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) + { + skb->sk->dead=1; + tcp_close(skb->sk, 0); + kfree_skb(skb, FREE_READ); + } + return; +} + +/* + * Enter the time wait state. + */ + +static void tcp_time_wait(struct sock *sk) +{ + tcp_set_state(sk,TCP_TIME_WAIT); + sk->shutdown = SHUTDOWN_MASK; + if (!sk->dead) + sk->state_change(sk); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); +} + +/* + * A socket has timed out on its send queue and wants to do a + * little retransmitting. Currently this means TCP. + */ + +void tcp_do_retransmit(struct sock *sk, int all) +{ + struct sk_buff * skb; + struct proto *prot; + struct device *dev; + int ct=0; + + prot = sk->prot; + skb = sk->send_head; + + while (skb != NULL) + { + struct tcphdr *th; + struct iphdr *iph; + int size; + + dev = skb->dev; + IS_SKB(skb); + skb->when = jiffies; + + /* + * In general it's OK just to use the old packet. However we + * need to use the current ack and window fields. Urg and + * urg_ptr could possibly stand to be updated as well, but we + * don't keep the necessary data. That shouldn't be a problem, + * if the other end is doing the right thing. Since we're + * changing the packet, we have to issue a new IP identifier. + */ + + iph = (struct iphdr *)(skb->data + dev->hard_header_len); + th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2)); + size = skb->len - (((unsigned char *) th) - skb->data); + + /* + * Note: We ought to check for window limits here but + * currently this is done (less efficiently) elsewhere. + * We do need to check for a route change but can't handle + * that until we have the new 1.3.x buffers in. + * + */ + + iph->id = htons(ip_id_count++); + ip_send_check(iph); + + /* + * This is not the right way to handle this. We have to + * issue an up to date window and ack report with this + * retransmit to keep the odd buggy tcp that relies on + * the fact BSD does this happy. + * We don't however need to recalculate the entire + * checksum, so someone wanting a small problem to play + * with might like to implement RFC1141/RFC1624 and speed + * this up by avoiding a full checksum. + */ + + th->ack_seq = ntohl(sk->acked_seq); + th->window = ntohs(tcp_select_window(sk)); + tcp_send_check(th, sk->saddr, sk->daddr, size, sk); + + /* + * If the interface is (still) up and running, kick it. + */ + + if (dev->flags & IFF_UP) + { + /* + * If the packet is still being sent by the device/protocol + * below then don't retransmit. This is both needed, and good - + * especially with connected mode AX.25 where it stops resends + * occurring of an as yet unsent anyway frame! + * We still add up the counts as the round trip time wants + * adjusting. + */ + if (sk && !skb_device_locked(skb)) + { + /* Remove it from any existing driver queue first! */ + skb_unlink(skb); + /* Now queue it */ + ip_statistics.IpOutRequests++; + dev_queue_xmit(skb, dev, sk->priority); + } + } + + /* + * Count retransmissions + */ + + ct++; + sk->prot->retransmits ++; + + /* + * Only one retransmit requested. + */ + + if (!all) + break; + + /* + * This should cut it off before we send too many packets. + */ + + if (ct >= sk->cong_window) + break; + skb = skb->link3; + } +} + +/* + * Reset the retransmission timer + */ + +static void reset_xmit_timer(struct sock *sk, int why, unsigned long when) +{ + del_timer(&sk->retransmit_timer); + sk->ip_xmit_timeout = why; + if((int)when < 0) + { + when=3; + printk("Error: Negative timer in xmit_timer\n"); + } + sk->retransmit_timer.expires=when; + add_timer(&sk->retransmit_timer); +} + +/* + * This is the normal code called for timeouts. It does the retransmission + * and then does backoff. tcp_do_retransmit is separated out because + * tcp_ack needs to send stuff from the retransmit queue without + * initiating a backoff. + */ + + +void tcp_retransmit_time(struct sock *sk, int all) +{ + tcp_do_retransmit(sk, all); + + /* + * Increase the timeout each time we retransmit. Note that + * we do not increase the rtt estimate. rto is initialized + * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests + * that doubling rto each time is the least we can get away with. + * In KA9Q, Karn uses this for the first few times, and then + * goes to quadratic. netBSD doubles, but only goes up to *64, + * and clamps at 1 to 64 sec afterwards. Note that 120 sec is + * defined in the protocol as the maximum possible RTT. I guess + * we'll have to use something other than TCP to talk to the + * University of Mars. + * + * PAWS allows us longer timeouts and large windows, so once + * implemented ftp to mars will work nicely. We will have to fix + * the 120 second clamps though! + */ + + sk->retransmits++; + sk->backoff++; + sk->rto = min(sk->rto << 1, 120*HZ); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); +} + + +/* + * A timer event has trigger a tcp retransmit timeout. The + * socket xmit queue is ready and set up to send. Because + * the ack receive code keeps the queue straight we do + * nothing clever here. + */ + +static void tcp_retransmit(struct sock *sk, int all) +{ + if (all) + { + tcp_retransmit_time(sk, all); + return; + } + + sk->ssthresh = sk->cong_window >> 1; /* remember window where we lost */ + /* sk->ssthresh in theory can be zero. I guess that's OK */ + sk->cong_count = 0; + + sk->cong_window = 1; + + /* Do the actual retransmit. */ + tcp_retransmit_time(sk, all); +} + +/* + * A write timeout has occurred. Process the after effects. + */ + +static int tcp_write_timeout(struct sock *sk) +{ + /* + * Look for a 'soft' timeout. + */ + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) + { + /* + * Attempt to recover if arp has changed (unlikely!) or + * a route has shifted (not supported prior to 1.3). + */ + arp_destroy (sk->daddr, 0); + ip_route_check (sk->daddr); + } + /* + * Has it gone just too far ? + */ + if (sk->retransmits > TCP_RETR2) + { + sk->err = ETIMEDOUT; + sk->error_report(sk); + del_timer(&sk->retransmit_timer); + /* + * Time wait the socket + */ + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING ) + { + tcp_set_state(sk,TCP_TIME_WAIT); + reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } + else + { + /* + * Clean up time. + */ + tcp_set_state(sk, TCP_CLOSE); + return 0; + } + } + return 1; +} + +/* + * The TCP retransmit timer. This lacks a few small details. + * + * 1. An initial rtt timeout on the probe0 should cause what we can + * of the first write queue buffer to be split and sent. + * 2. On a 'major timeout' as defined by RFC1122 we shouldn't report + * ETIMEDOUT if we know an additional 'soft' error caused this. + * tcp_err should save a 'soft error' for us. + */ + +static void retransmit_timer(unsigned long data) +{ + struct sock *sk = (struct sock*)data; + int why = sk->ip_xmit_timeout; + + /* + * only process if socket is not in use + */ + + cli(); + if (sk->inuse || in_bh) + { + /* Try again in 1 second */ + sk->retransmit_timer.expires = HZ; + add_timer(&sk->retransmit_timer); + sti(); + return; + } + + sk->inuse = 1; + sti(); + + /* Always see if we need to send an ack. */ + + if (sk->ack_backlog && !sk->zapped) + { + sk->prot->read_wakeup (sk); + if (! sk->dead) + sk->data_ready(sk,0); + } + + /* Now we need to figure out why the socket was on the timer. */ + + switch (why) + { + /* Window probing */ + case TIME_PROBE0: + tcp_send_probe0(sk); + tcp_write_timeout(sk); + break; + /* Retransmitting */ + case TIME_WRITE: + /* It could be we got here because we needed to send an ack. + * So we need to check for that. + */ + { + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + skb = sk->send_head; + if (!skb) + { + restore_flags(flags); + } + else + { + /* + * Kicked by a delayed ack. Reset timer + * correctly now + */ + if (jiffies < skb->when + sk->rto) + { + reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies); + restore_flags(flags); + break; + } + restore_flags(flags); + /* + * Retransmission + */ + sk->prot->retransmit (sk, 0); + tcp_write_timeout(sk); + } + break; + } + /* Sending Keepalives */ + case TIME_KEEPOPEN: + /* + * this reset_timer() call is a hack, this is not + * how KEEPOPEN is supposed to work. + */ + reset_xmit_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + + /* Send something to keep the connection open. */ + if (sk->prot->write_wakeup) + sk->prot->write_wakeup (sk); + sk->retransmits++; + tcp_write_timeout(sk); + break; + default: + printk ("rexmit_timer: timer expired - reason unknown\n"); + break; + } + release_sock(sk); +} + +/* + * This routine is called by the ICMP module when it gets some + * sort of error condition. If err < 0 then the socket should + * be closed and the error returned to the user. If err > 0 + * it's just the icmp type << 8 | icmp code. After adjustment + * header points to the first 8 bytes of the tcp header. We need + * to find the appropriate port. + */ + +void tcp_err(int err, unsigned char *header, unsigned long daddr, + unsigned long saddr, struct inet_protocol *protocol) +{ + struct tcphdr *th; + struct sock *sk; + struct iphdr *iph=(struct iphdr *)header; + + header+=4*iph->ihl; + + + th =(struct tcphdr *)header; + sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr); + + if (sk == NULL) + return; + + if(err<0) + { + sk->err = -err; + sk->error_report(sk); + return; + } + + if ((err & 0xff00) == (ICMP_SOURCE_QUENCH << 8)) + { + /* + * FIXME: + * For now we will just trigger a linear backoff. + * The slow start code should cause a real backoff here. + */ + if (sk->cong_window > 4) + sk->cong_window--; + return; + } + +/* sk->err = icmp_err_convert[err & 0xff].errno; -- moved as TCP should hide non fatals internally (and does) */ + + /* + * If we've already connected we will keep trying + * until we time out, or the user gives up. + */ + + if (icmp_err_convert[err & 0xff].fatal || sk->state == TCP_SYN_SENT) + { + if (sk->state == TCP_SYN_SENT) + { + tcp_statistics.TcpAttemptFails++; + tcp_set_state(sk,TCP_CLOSE); + sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ + } + sk->err = icmp_err_convert[err & 0xff].errno; + } + return; +} + + +/* + * Walk down the receive queue counting readable data until we hit the end or we find a gap + * in the received data queue (ie a frame missing that needs sending to us). Not + * sorting using two queues as data arrives makes life so much harder. + */ + +static int tcp_readable(struct sock *sk) +{ + unsigned long counted; + unsigned long amount; + struct sk_buff *skb; + int sum; + unsigned long flags; + + if(sk && sk->debug) + printk("tcp_readable: %p - ",sk); + + save_flags(flags); + cli(); + if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL) + { + restore_flags(flags); + if(sk && sk->debug) + printk("empty\n"); + return(0); + } + + counted = sk->copied_seq; /* Where we are at the moment */ + amount = 0; + + /* + * Do until a push or until we are out of data. + */ + + do + { + if (before(counted, skb->h.th->seq)) /* Found a hole so stops here */ + break; + sum = skb->len -(counted - skb->h.th->seq); /* Length - header but start from where we are up to (avoid overlaps) */ + if (skb->h.th->syn) + sum++; + if (sum > 0) + { /* Add it up, move on */ + amount += sum; + if (skb->h.th->syn) + amount--; + counted += sum; + } + /* + * Don't count urg data ... but do it in the right place! + * Consider: "old_data (ptr is here) URG PUSH data" + * The old code would stop at the first push because + * it counted the urg (amount==1) and then does amount-- + * *after* the loop. This means tcp_readable() always + * returned zero if any URG PUSH was in the queue, even + * though there was normal data available. If we subtract + * the urg data right here, we even get it to work for more + * than one URG PUSH skb without normal data. + * This means that select() finally works now with urg data + * in the queue. Note that rlogin was never affected + * because it doesn't use select(); it uses two processes + * and a blocking read(). And the queue scan in tcp_read() + * was correct. Mike <pall@rz.uni-karlsruhe.de> + */ + if (skb->h.th->urg) + amount--; /* don't count urg data */ + if (amount && skb->h.th->psh) break; + skb = skb->next; + } + while(skb != (struct sk_buff *)&sk->receive_queue); + + restore_flags(flags); + if(sk->debug) + printk("got %lu bytes.\n",amount); + return(amount); +} + +/* + * LISTEN is a special case for select.. + */ +static int tcp_listen_select(struct sock *sk, int sel_type, select_table *wait) +{ + if (sel_type == SEL_IN) { + int retval; + + sk->inuse = 1; + retval = (tcp_find_established(sk) != NULL); + release_sock(sk); + if (!retval) + select_wait(&master_select_wakeup,wait); + return retval; + } + return 0; +} + + +/* + * Wait for a TCP event. + * + * Note that we don't need to set "sk->inuse", as the upper select layers + * take care of normal races (between the test and the event) and we don't + * go look at any of the socket buffers directly. + */ +static int tcp_select(struct sock *sk, int sel_type, select_table *wait) +{ + if (sk->state == TCP_LISTEN) + return tcp_listen_select(sk, sel_type, wait); + + switch(sel_type) { + case SEL_IN: + if (sk->err) + return 1; + if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + break; + + if (sk->shutdown & RCV_SHUTDOWN) + return 1; + + if (sk->acked_seq == sk->copied_seq) + break; + + if (sk->urg_seq != sk->copied_seq || + sk->acked_seq != sk->copied_seq+1 || + sk->urginline || !sk->urg_data) + return 1; + break; + + case SEL_OUT: + if (sk->shutdown & SEND_SHUTDOWN) + return 0; + if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + break; + /* + * This is now right thanks to a small fix + * by Matt Dillon. + */ + + if (sk->prot->wspace(sk) < sk->mtu+128+sk->prot->max_header) + break; + return 1; + + case SEL_EX: + if (sk->err || sk->urg_data) + return 1; + break; + } + select_wait(sk->sleep, wait); + return 0; +} + +int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + int err; + switch(cmd) + { + + case TIOCINQ: +#ifdef FIXME /* FIXME: */ + case FIONREAD: +#endif + { + unsigned long amount; + + if (sk->state == TCP_LISTEN) + return(-EINVAL); + + sk->inuse = 1; + amount = tcp_readable(sk); + release_sock(sk); + err=verify_area(VERIFY_WRITE,(void *)arg, + sizeof(unsigned long)); + 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; + + 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 = ip_my_addr(); + +/* + * stupid, gcc complains when I use just one __asm__ block, + * something about too many reloads, but this is just two + * instructions longer than what I want + */ + __asm__(" + addl %%ecx, %%ebx + adcl %%edx, %%ebx + adcl $0, %%ebx + " + : "=b"(sum) + : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_TCP*256) + : "bx", "cx", "dx" ); + __asm__(" + movl %%ecx, %%edx + cld + cmpl $32, %%ecx + jb 2f + shrl $5, %%ecx + clc +1: lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + lodsl + adcl %%eax, %%ebx + loop 1b + adcl $0, %%ebx + movl %%edx, %%ecx +2: andl $28, %%ecx + je 4f + shrl $2, %%ecx + clc +3: lodsl + adcl %%eax, %%ebx + loop 3b + adcl $0, %%ebx +4: movl $0, %%eax + testw $2, %%dx + je 5f + lodsw + addl %%eax, %%ebx + adcl $0, %%ebx + movw $0, %%ax +5: test $1, %%edx + je 6f + lodsb + addl %%eax, %%ebx + adcl $0, %%ebx +6: movl %%ebx, %%eax + shrl $16, %%eax + addw %%ax, %%bx + adcw $0, %%bx + " + : "=b"(sum) + : "0"(sum), "c"(len), "S"(th) + : "ax", "bx", "cx", "dx", "si" ); + + /* 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; +} + +/* + * This is the main buffer sending routine. We queue the buffer + * having checked it is sane seeming. + */ + +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.. (these crash a few + * tcp stacks if ack is not set) + */ + + if (size == sizeof(struct tcphdr)) + { + /* If it's got a syn or fin it's 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; + } + } + + /* + * Actual processing. + */ + + tcp_statistics.TcpOutSegs++; + skb->h.seq = ntohl(th->seq) + size - 4*th->doff; + + /* + * We must queue if + * + * a) The right edge of this frame exceeds the window + * b) We are retransmitting (Nagle's rule) + * c) We have too many packets 'in flight' + */ + + if (after(skb->h.seq, sk->window_seq) || + (sk->retransmits && sk->ip_xmit_timeout == TIME_WRITE) || + sk->packets_out >= sk->cong_window) + { + /* checksum will be supplied by tcp_write_xmit. So + * we shouldn't need to set it at all. I'm being paranoid */ + th->check = 0; + if (skb->next != NULL) + { + printk("tcp_send_partial: next != NULL\n"); + skb_unlink(skb); + } + skb_queue_tail(&sk->write_queue, skb); + + /* + * If we don't fit we have to start the zero window + * probes. This is broken - we really need to do a partial + * send _first_ (This is what causes the Cisco and PC/TCP + * grief). + */ + + if (before(sk->window_seq, sk->write_queue.next->h.seq) && + sk->send_head == NULL && sk->ack_backlog == 0) + reset_xmit_timer(sk, TIME_PROBE0, sk->rto); + } + else + { + /* + * This is going straight out + */ + + th->ack_seq = ntohl(sk->acked_seq); + th->window = ntohs(tcp_select_window(sk)); + + tcp_send_check(th, sk->saddr, sk->daddr, size, sk); + + sk->sent_seq = sk->write_seq; + + /* + * This is mad. The tcp retransmit queue is put together + * by the ip layer. This causes half the problems with + * unroutable FIN's and other things. + */ + + sk->prot->queue_xmit(sk, skb->dev, skb, 0); + + /* + * Set for next retransmit based on expected ACK time. + * FIXME: We set this every time which means our + * retransmits are really about a window behind. + */ + + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + } +} + +/* + * Locking problems lead us to a messy situation where we can have + * multiple partially complete buffers queued up. This is really bad + * as we don't want to be sending partial buffers. Fix this with + * a semaphore or similar to lock tcp_write per socket. + * + * These routines are pretty self descriptive. + */ + +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; +} + +/* + * Empty the partial queue + */ + +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); +} + +/* + * Queue a partial frame + */ + +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; + init_timer(&sk->partial_timer); + /* + * Wait up to 1 second for the buffer to fill. + */ + 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. We don't have to do this + * (ACK is unreliable) but it's much better use of + * bandwidth on slow links to send a spare ack than + * resend packets. + */ + + sk->ack_backlog++; + if (sk->ip_xmit_timeout != TIME_WRITE && tcp_connected(sk->state)) + { + reset_xmit_timer(sk, TIME_WRITE, HZ); + } + return; + } + + /* + * Assemble a suitable TCP frame + */ + + buff->len = sizeof(struct tcphdr); + buff->sk = sk; + buff->localroute = sk->localroute; + 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); + return; + } + buff->len += tmp; + t1 =(struct tcphdr *)((char *)t1 +tmp); + + memcpy(t1, th, sizeof(*t1)); + + /* + * 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); + 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 we have nothing queued for transmit and the transmit timer + * is on we are just doing an ACK timeout and need to switch + * to a keepalive. + */ + + if (ack == sk->acked_seq) + { + sk->ack_backlog = 0; + sk->bytes_rcv = 0; + sk->ack_timed = 0; + if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL + && sk->ip_xmit_timeout == TIME_WRITE) + { + if(sk->keepopen) { + reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN); + } else { + delete_timer(sk); + } + } + } + + /* + * Fill in the packet and send it + */ + + 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); + tcp_statistics.TcpOutSegs++; + sk->prot->queue_xmit(sk, dev, buff, 1); +} + + +/* + * This routine builds a generic TCP header. + */ + +extern __inline int tcp_build_header(struct tcphdr *th, struct sock *sk, int push) +{ + + 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); + 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; + + 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); + 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); + 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(); + 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 different 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 in case 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, 0, GFP_KERNEL); + sk->inuse = 1; + send_tmp = skb; + } + else + { + /* + * We will release the socket in case we sleep here. + */ + release_sock(sk); + skb = prot->wmalloc(sk, copy + prot->max_header , 0, GFP_KERNEL); + sk->inuse = 1; + } + + /* + * If we didn't get any memory, we need to sleep. + */ + + if (skb == NULL) + { + sk->socket->flags |= SO_NOSPACE; + if (nonblock) + { + release_sock(sk); + 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) + { + sk->socket->flags &= ~SO_NOSPACE; + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + if (copied) + return(copied); + return(-ERESTARTSYS); + } + } + sk->inuse = 1; + sti(); + continue; + } + + skb->len = 0; + skb->sk = sk; + skb->free = 0; + skb->localroute = sk->localroute|(flags&MSG_DONTROUTE); + + buff = skb->data; + + /* + * FIXME: we need to optimize this. + * Perhaps some hints here would be good. + */ + + tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl); + if (tmp < 0 ) + { + prot->wfree(sk, skb->mem_addr, skb->mem_len); + release_sock(sk); + if (copied) + return(copied); + return(tmp); + } + skb->len += tmp; + skb->dev = dev; + buff += tmp; + skb->h.th =(struct tcphdr *) buff; + tmp = tcp_build_header((struct tcphdr *)buff, sk, len-copy); + if (tmp < 0) + { + prot->wfree(sk, skb->mem_addr, skb->mem_len); + release_sock(sk); + if (copied) + return(copied); + return(tmp); + } + + if (flags & MSG_OOB) + { + ((struct tcphdr *)buff)->urg = 1; + ((struct tcphdr *)buff)->urg_ptr = ntohs(copy); + } + skb->len += tmp; + memcpy_fromfs(buff+tmp, from, copy); + + from += copy; + copied += copy; + len -= copy; + skb->len += copy; + skb->free = 0; + sk->write_seq += copy; + + if (send_tmp != NULL && sk->packets_out) + { + tcp_enqueue_partial(send_tmp, sk); + continue; + } + tcp_send_skb(sk, skb); + } + sk->err = 0; + +/* + * Nagle's rule. Turn Nagle off with TCP_NODELAY for highly + * interactive fast network servers. It's meant to be on and + * it really improves the throughput though not the echo time + * on my slow slip link - Alan + */ + +/* + * Avoid possible race on send_tmp - c/o Johannes Stille + */ + + if(sk->partial && ((!sk->packets_out) + /* If not nagling we can send on the before case too.. */ + || (sk->nonagle && before(sk->write_seq , sk->window_seq)) + )) + tcp_send_partial(sk); + + release_sock(sk); + return(copied); +} + +/* + * This is just a wrapper. + */ + +static int tcp_sendto(struct sock *sk, unsigned char *from, + int len, int nonblock, unsigned flags, + struct sockaddr_in *addr, int addr_len) +{ + if (flags & ~(MSG_OOB|MSG_DONTROUTE)) + return -EINVAL; + if (sk->state == TCP_CLOSE) + return -ENOTCONN; + if (addr_len < sizeof(*addr)) + return -EINVAL; + if (addr->sin_family && addr->sin_family != AF_INET) + return -EINVAL; + if (addr->sin_port != sk->dummy_th.dest) + return -EISCONN; + if (addr->sin_addr.s_addr != sk->daddr) + return -EISCONN; + return tcp_write(sk, from, len, nonblock, flags); +} + + +/* + * Send an ack if one is backlogged at this point. Ought to merge + * this with tcp_send_ack(). + */ + +static void tcp_read_wakeup(struct sock *sk) +{ + int tmp; + struct device *dev = NULL; + struct tcphdr *t1; + struct sk_buff *buff; + + if (!sk->ack_backlog) + return; + + /* + * FIXME: we need to put code here to prevent this routine from + * being called. Being called once in a while is ok, so only check + * if this is the second time in a row. + */ + + /* + * 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) + { + /* Try again real soon. */ + reset_xmit_timer(sk, TIME_WRITE, HZ); + return; + } + + buff->len = sizeof(struct tcphdr); + buff->sk = sk; + buff->localroute = sk->localroute; + + /* + * Put in the IP header and routing stuff. + */ + + tmp = sk->prot->build_header(buff, sk->saddr, sk->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); + return; + } + + buff->len += tmp; + t1 =(struct tcphdr *)(buff->data +tmp); + + memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); + t1->seq = htonl(sk->sent_seq); + t1->ack = 1; + t1->res1 = 0; + t1->res2 = 0; + t1->rst = 0; + t1->urg = 0; + t1->syn = 0; + t1->psh = 0; + sk->ack_backlog = 0; + sk->bytes_rcv = 0; + sk->window = tcp_select_window(sk); + t1->window = ntohs(sk->window); + t1->ack_seq = ntohl(sk->acked_seq); + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + sk->prot->queue_xmit(sk, dev, buff, 1); + tcp_statistics.TcpOutSegs++; +} + + +/* + * FIXME: + * This routine frees used buffers. + * It should consider sending an ACK to let the + * other end know we now have a bigger window. + */ + +static void cleanup_rbuf(struct sock *sk) +{ + unsigned long flags; + unsigned long left; + struct sk_buff *skb; + unsigned long rspace; + + if(sk->debug) + printk("cleaning rbuf for sk=%p\n", sk); + + save_flags(flags); + cli(); + + left = sk->prot->rspace(sk); + + /* + * We have to loop through all the buffer headers, + * and try to free up all the space we can. + */ + + while((skb=skb_peek(&sk->receive_queue)) != NULL) + { + if (!skb->used || skb->users) + break; + skb_unlink(skb); + skb->sk = sk; + kfree_skb(skb, FREE_READ); + } + + restore_flags(flags); + + /* + * FIXME: + * At this point we should send an ack if the difference + * in the window, and the amount of space is bigger than + * TCP_WINDOW_DIFF. + */ + + if(sk->debug) + printk("sk->rspace = %lu, was %lu\n", sk->prot->rspace(sk), + left); + if ((rspace=sk->prot->rspace(sk)) != left) + { + /* + * This area has caused the most trouble. The current strategy + * is to simply do nothing if the other end has room to send at + * least 3 full packets, because the ack from those will auto- + * matically update the window. If the other end doesn't think + * we have much space left, but we have room for at least 1 more + * complete packet than it thinks we do, we will send an ack + * immediately. Otherwise we will wait up to .5 seconds in case + * the user reads some more. + */ + sk->ack_backlog++; + /* + * It's unclear whether to use sk->mtu or sk->mss here. They differ only + * if the other end is offering a window smaller than the agreed on MSS + * (called sk->mtu here). In theory there's no connection between send + * and receive, and so no reason to think that they're going to send + * small packets. For the moment I'm using the hack of reducing the mss + * only on the send side, so I'm putting mtu here. + */ + + if (rspace > (sk->window - sk->bytes_rcv + sk->mtu)) + { + /* Send an ack right now. */ + tcp_read_wakeup(sk); + } + else + { + /* Force it to send an ack soon. */ + int was_active = del_timer(&sk->retransmit_timer); + if (!was_active || TCP_ACK_TIME < sk->timer.expires) + { + reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME); + } + else + add_timer(&sk->retransmit_timer); + } + } +} + + +/* + * Handle reading urgent data. BSD has very simple semantics for + * this, no blocking and very strange errors 8) + */ + +static int tcp_read_urg(struct sock * sk, int nonblock, + unsigned char *to, int len, unsigned flags) +{ + /* + * No URG data to read + */ + if (sk->urginline || !sk->urg_data || sk->urg_data == URG_READ) + return -EINVAL; /* Yes this is right ! */ + + if (sk->err) + { + int tmp = -sk->err; + sk->err = 0; + return tmp; + } + + if (sk->state == TCP_CLOSE || sk->done) + { + if (!sk->done) { + sk->done = 1; + return 0; + } + return -ENOTCONN; + } + + if (sk->shutdown & RCV_SHUTDOWN) + { + sk->done = 1; + return 0; + } + sk->inuse = 1; + if (sk->urg_data & URG_VALID) + { + char c = sk->urg_data; + if (!(flags & MSG_PEEK)) + sk->urg_data = URG_READ; + put_fs_byte(c, to); + release_sock(sk); + return 1; + } + release_sock(sk); + + /* + * Fixed the recv(..., MSG_OOB) behaviour. BSD docs and + * the available implementations agree in this case: + * this call should never block, independent of the + * blocking state of the socket. + * Mike <pall@rz.uni-karlsruhe.de> + */ + return -EAGAIN; +} + + +/* + * This routine copies from a sock struct into the user buffer. + */ + +static int tcp_read(struct sock *sk, unsigned char *to, + int len, int nonblock, unsigned flags) +{ + struct wait_queue wait = { current, NULL }; + int copied = 0; + unsigned long peek_seq; + volatile unsigned long *seq; /* So gcc doesn't overoptimise */ + unsigned long used; + + /* + * This error should be checked. + */ + + if (sk->state == TCP_LISTEN) + return -ENOTCONN; + + /* + * Urgent data needs to be handled specially. + */ + + if (flags & MSG_OOB) + return tcp_read_urg(sk, nonblock, to, len, flags); + + /* + * Copying sequence to update. This is volatile to handle + * the multi-reader case neatly (memcpy_to/fromfs might be + * inline and thus not flush cached variables otherwise). + */ + + peek_seq = sk->copied_seq; + seq = &sk->copied_seq; + if (flags & MSG_PEEK) + seq = &peek_seq; + + add_wait_queue(sk->sleep, &wait); + sk->inuse = 1; + while (len > 0) + { + struct sk_buff * skb; + unsigned long offset; + + /* + * Are we at urgent data? Stop if we have read anything. + */ + + if (copied && sk->urg_data && sk->urg_seq == *seq) + break; + + /* + * Next get a buffer. + */ + + current->state = TASK_INTERRUPTIBLE; + + skb = skb_peek(&sk->receive_queue); + do + { + if (!skb) + break; + if (before(*seq, skb->h.th->seq)) + break; + offset = *seq - skb->h.th->seq; + if (skb->h.th->syn) + offset--; + if (offset < skb->len) + goto found_ok_skb; + if (skb->h.th->fin) + goto found_fin_ok; + if (!(flags & MSG_PEEK)) + skb->used = 1; + skb = skb->next; + } + while (skb != (struct sk_buff *)&sk->receive_queue); + + if (copied) + break; + + if (sk->err) + { + copied = -sk->err; + sk->err = 0; + break; + } + + if (sk->state == TCP_CLOSE) + { + if (!sk->done) + { + sk->done = 1; + break; + } + copied = -ENOTCONN; + break; + } + + if (sk->shutdown & RCV_SHUTDOWN) + { + sk->done = 1; + break; + } + + if (nonblock) + { + copied = -EAGAIN; + break; + } + + cleanup_rbuf(sk); + release_sock(sk); + sk->socket->flags |= SO_WAITDATA; + schedule(); + sk->socket->flags &= ~SO_WAITDATA; + sk->inuse = 1; + + if (current->signal & ~current->blocked) + { + copied = -ERESTARTSYS; + break; + } + continue; + + found_ok_skb: + /* + * Lock the buffer. We can be fairly relaxed as + * an interrupt will never steal a buffer we are + * using unless I've missed something serious in + * tcp_data. + */ + + skb->users++; + + /* + * Ok so how much can we use ? + */ + + used = skb->len - offset; + if (len < used) + used = len; + /* + * Do we have urgent data here? + */ + + if (sk->urg_data) + { + unsigned long urg_offset = sk->urg_seq - *seq; + if (urg_offset < used) + { + if (!urg_offset) + { + if (!sk->urginline) + { + ++*seq; + offset++; + used--; + } + } + else + used = urg_offset; + } + } + + /* + * Copy it - We _MUST_ update *seq first so that we + * don't ever double read when we have dual readers + */ + + *seq += used; + + /* + * This memcpy_tofs can sleep. If it sleeps and we + * do a second read it relies on the skb->users to avoid + * a crash when cleanup_rbuf() gets called. + */ + + memcpy_tofs(to,((unsigned char *)skb->h.th) + + skb->h.th->doff*4 + offset, used); + copied += used; + len -= used; + to += used; + + /* + * We now will not sleep again until we are finished + * with skb. Sorry if you are doing the SMP port + * but you'll just have to fix it neatly ;) + */ + + skb->users --; + + if (after(sk->copied_seq,sk->urg_seq)) + sk->urg_data = 0; + if (used + offset < skb->len) + continue; + + /* + * Process the FIN. + */ + + if (skb->h.th->fin) + goto found_fin_ok; + if (flags & MSG_PEEK) + continue; + skb->used = 1; + continue; + + found_fin_ok: + ++*seq; + if (flags & MSG_PEEK) + break; + + /* + * All is done + */ + + skb->used = 1; + sk->shutdown |= RCV_SHUTDOWN; + break; + + } + remove_wait_queue(sk->sleep, &wait); + current->state = TASK_RUNNING; + + /* Clean up data we have read: This will do ACK frames */ + cleanup_rbuf(sk); + release_sock(sk); + return copied; +} + +/* + * State processing on a close. This implements the state shift for + * sending our FIN frame. Note that we only send a FIN for some + * states. A shutdown() may have already sent the FIN, or we may be + * closed. + */ + +static int tcp_close_state(struct sock *sk, int dead) +{ + int ns=TCP_CLOSE; + int send_fin=0; + switch(sk->state) + { + case TCP_SYN_SENT: /* No SYN back, no FIN needed */ + break; + case TCP_SYN_RECV: + case TCP_ESTABLISHED: /* Closedown begin */ + ns=TCP_FIN_WAIT1; + send_fin=1; + break; + case TCP_FIN_WAIT1: /* Already closing, or FIN sent: no change */ + case TCP_FIN_WAIT2: + case TCP_CLOSING: + ns=sk->state; + break; + case TCP_CLOSE: + case TCP_LISTEN: + break; + case TCP_CLOSE_WAIT: /* They have FIN'd us. We send our FIN and + wait only for the ACK */ + ns=TCP_LAST_ACK; + send_fin=1; + } + + tcp_set_state(sk,ns); + + /* + * This is a (useful) BSD violating of the RFC. There is a + * problem with TCP as specified in that the other end could + * keep a socket open forever with no application left this end. + * We use a 3 minute timeout (about the same as BSD) then kill + * our end. If they send after that then tough - BUT: long enough + * that we won't make the old 4*rto = almost no time - whoops + * reset mistake. + */ + if(dead && ns==TCP_FIN_WAIT2) + { + int timer_active=del_timer(&sk->timer); + if(timer_active) + add_timer(&sk->timer); + else + reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT); + } + + return send_fin; +} + +/* + * Send a fin. + */ + +static void tcp_send_fin(struct sock *sk) +{ + struct proto *prot =(struct proto *)sk->prot; + struct tcphdr *th =(struct tcphdr *)&sk->dummy_th; + struct tcphdr *t1; + struct sk_buff *buff; + struct device *dev=NULL; + int tmp; + + release_sock(sk); /* in case the malloc sleeps. */ + + buff = prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL); + sk->inuse = 1; + + if (buff == NULL) + { + /* This is a disaster if it occurs */ + printk("tcp_send_fin: Impossible malloc failure"); + return; + } + + /* + * Administrivia + */ + + buff->sk = sk; + buff->len = sizeof(*t1); + buff->localroute = sk->localroute; + 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) + { + int t; + /* + * Finish anyway, treat this as a send that got lost. + * (Not good). + */ + + buff->free = 1; + prot->wfree(sk,buff->mem_addr, buff->mem_len); + sk->write_seq++; + t=del_timer(&sk->timer); + if(t) + add_timer(&sk->timer); + else + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + return; + } + + /* + * We ought to check if the end of the queue is a buffer and + * if so simply add the fin to that buffer, not send it ahead. + */ + + 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)); + t1->fin = 1; + t1->rst = 0; + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + + /* + * If there is data in the write queue, the fin must be appended to + * the write queue. + */ + + if (skb_peek(&sk->write_queue) != NULL) + { + buff->free = 0; + if (buff->next != NULL) + { + printk("tcp_send_fin: next != NULL\n"); + skb_unlink(buff); + } + skb_queue_tail(&sk->write_queue, buff); + } + else + { + sk->sent_seq = sk->write_seq; + sk->prot->queue_xmit(sk, dev, buff, 0); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + } +} + +/* + * Shutdown the sending side of a connection. Much like close except + * that we don't receive shut down or set sk->dead=1. + */ + +void tcp_shutdown(struct sock *sk, int how) +{ + /* + * We need to grab some memory, and put together a FIN, + * and then put it into the queue to be sent. + * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92. + */ + + if (!(how & SEND_SHUTDOWN)) + return; + + /* + * If we've already sent a FIN, or it's a closed state + */ + + if (sk->state == TCP_FIN_WAIT1 || + sk->state == TCP_FIN_WAIT2 || + sk->state == TCP_CLOSING || + sk->state == TCP_LAST_ACK || + sk->state == TCP_TIME_WAIT || + sk->state == TCP_CLOSE || + sk->state == TCP_LISTEN + ) + { + return; + } + sk->inuse = 1; + + /* + * flag that the sender has shutdown + */ + + sk->shutdown |= SEND_SHUTDOWN; + + /* + * Clear out any half completed packets. + */ + + if (sk->partial) + tcp_send_partial(sk); + + /* + * FIN if needed + */ + + if(tcp_close_state(sk,0)) + tcp_send_fin(sk); + + 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) +{ + 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 + */ + + if(addr_len) + *addr_len = sizeof(*addr); + result=tcp_read(sk, to, to_len, nonblock, flags); + + if (result < 0) + return(result); + + if(addr) + { + addr->sin_family = AF_INET; + addr->sin_port = sk->dummy_th.dest; + addr->sin_addr.s_addr = sk->daddr; + } + 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; + struct device *ndev=NULL; + + /* + * Cannot reset a reset (Think about it). + */ + + if(th->rst) + return; + + /* + * 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; + + buff->len = sizeof(*t1); + buff->sk = NULL; + buff->dev = dev; + buff->localroute = 0; + + t1 =(struct tcphdr *) buff->data; + + /* + * Put in the IP header and routing stuff. + */ + + tmp = prot->build_header(buff, saddr, daddr, &ndev, 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, ndev, buff, 1); + tcp_statistics.TcpOutSegs++; +} + + +/* + * 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. + * + * We need at minimum to add PAWS support here. Possibly large windows + * as Linux gets deployed on 100Mb/sec networks. + */ + +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: /* Ref: RFC 793 section 3.1 */ + length--; + ptr--; /* the opsize=*ptr++ above was a mistake */ + 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 */ + } +#ifdef CONFIG_INET_PCTCP + sk->mss = min(sk->max_window >> 1, sk->mtu); +#else + sk->mss = min(sk->max_window, sk->mtu); +#endif +} + +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); +} + +/* + * Default sequence number picking algorithm. + * As close as possible to RFC 793, which + * suggests using a 250kHz clock. + * Further reading shows this assumes 2MB/s networks. + * For 10MB/s ethernet, a 1MHz clock is appropriate. + * That's funny, Linux has one built in! Use it! + */ + +extern inline unsigned long tcp_init_seq(void) +{ + struct timeval tv; + do_gettimeofday(&tv); + return tv.tv_usec+tv.tv_sec*1000000; +} + +/* + * 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, unsigned long seq) +{ + struct sk_buff *buff; + struct tcphdr *t1; + unsigned char *ptr; + struct sock *newsk; + struct tcphdr *th; + struct device *ndev=NULL; + int tmp; + struct rtable *rt; + + th = skb->h.th; + + /* If the socket is dead, don't accept the connection. */ + if (!sk->dead) + { + sk->data_ready(sk,0); + } + else + { + if(sk->debug) + printk("Reset on %p: Connect on dead socket.\n",sk); + tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl); + tcp_statistics.TcpAttemptFails++; + 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) + { + tcp_statistics.TcpAttemptFails++; + 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. */ + tcp_statistics.TcpAttemptFails++; + kfree_skb(skb, FREE_READ); + return; + } + + memcpy(newsk, sk, sizeof(*newsk)); + skb_queue_head_init(&newsk->write_queue); + skb_queue_head_init(&newsk->receive_queue); + newsk->send_head = NULL; + newsk->send_tail = NULL; + skb_queue_head_init(&newsk->back_log); + newsk->rtt = 0; /*TCP_CONNECT_TIME<<3*/ + newsk->rto = TCP_TIMEOUT_INIT; + 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->localroute = sk->localroute; + + 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->copied_seq = skb->h.th->seq+1; + newsk->fin_seq = skb->h.th->seq; + newsk->state = TCP_SYN_RECV; + newsk->timeout = 0; + newsk->ip_xmit_timeout = 0; + newsk->write_seq = seq; + newsk->window_seq = newsk->write_seq; + newsk->rcv_ack_seq = newsk->write_seq; + newsk->urg_data = 0; + newsk->retransmits = 0; + newsk->linger=0; + newsk->destroy = 0; + init_timer(&newsk->timer); + newsk->timer.data = (unsigned long)newsk; + newsk->timer.function = &net_timer; + init_timer(&newsk->retransmit_timer); + newsk->retransmit_timer.data = (unsigned long)newsk; + newsk->retransmit_timer.function=&retransmit_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 + 1; + newsk->socket = NULL; + + /* + * 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 + */ + + rt=ip_rt_route(saddr, NULL,NULL); + + if(rt!=NULL && (rt->rt_flags&RTF_WINDOW)) + newsk->window_clamp = rt->rt_window; + else + newsk->window_clamp = 0; + + if (sk->user_mss) + newsk->mtu = sk->user_mss; + else if(rt!=NULL && (rt->rt_flags&RTF_MSS)) + newsk->mtu = rt->rt_mss - HEADER_SIZE; + else + { +#ifdef CONFIG_INET_SNARL /* Sub Nets Are Local */ + 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; + newsk->state = TCP_CLOSE; + /* And this will destroy it */ + release_sock(newsk); + kfree_skb(skb, FREE_READ); + tcp_statistics.TcpAttemptFails++; + return; + } + + buff->len = sizeof(struct tcphdr)+4; + buff->sk = newsk; + buff->localroute = newsk->localroute; + + t1 =(struct tcphdr *) buff->data; + + /* + * Put in the IP header and routing stuff. + */ + + tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev, + 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; + newsk->state = TCP_CLOSE; + release_sock(newsk); + skb->sk = sk; + kfree_skb(skb, FREE_READ); + tcp_statistics.TcpAttemptFails++; + 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->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, ndev, buff, 0); + reset_xmit_timer(newsk, TIME_WRITE , TCP_TIMEOUT_INIT); + 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->receive_queue,skb); + sk->ack_backlog++; + release_sock(newsk); + tcp_statistics.TcpOutSegs++; +} + + +static void tcp_close(struct sock *sk, int timeout) +{ + /* + * We need to grab some memory, and put together a FIN, + * and then put it into the queue to be sent. + */ + + sk->inuse = 1; + + if(sk->state == TCP_LISTEN) + { + /* Special case */ + tcp_set_state(sk, TCP_CLOSE); + tcp_close_pending(sk); + release_sock(sk); + return; + } + + sk->keepopen = 1; + sk->shutdown = SHUTDOWN_MASK; + + if (!sk->dead) + sk->state_change(sk); + + if (timeout == 0) + { + struct sk_buff *skb; + + /* + * We need to flush the recv. buffs. We do this only on the + * descriptor close, not protocol-sourced closes, because the + * reader process may not have drained the data yet! + */ + + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + kfree_skb(skb, FREE_READ); + /* + * Get rid off any half-completed packets. + */ + + if (sk->partial) + tcp_send_partial(sk); + } + + + /* + * Timeout is not the same thing - however the code likes + * to send both the same way (sigh). + */ + + if(timeout) + { + tcp_set_state(sk, TCP_CLOSE); /* Dead */ + } + else + { + if(tcp_close_state(sk,1)==1) + { + tcp_send_fin(sk); + } + } + release_sock(sk); +} + + +/* + * This routine takes stuff off of the write queue, + * and puts it in the xmit queue. This happens as incoming acks + * open up the remote window for us. + */ + +static void tcp_write_xmit(struct sock *sk) +{ + struct sk_buff *skb; + + /* + * The bytes will have to remain here. In time closedown will + * empty the write queue and all will be happy + */ + + if(sk->zapped) + return; + + /* + * Anything on the transmit queue that fits the window can + * be added providing we are not + * + * a) retransmitting (Nagle's rule) + * b) exceeding our congestion window. + */ + + while((skb = skb_peek(&sk->write_queue)) != NULL && + before(skb->h.seq, sk->window_seq + 1) && + (sk->retransmits == 0 || + sk->ip_xmit_timeout != TIME_WRITE || + before(skb->h.seq, sk->rcv_ack_seq + 1)) + && sk->packets_out < sk->cong_window) + { + IS_SKB(skb); + skb_unlink(skb); + + /* + * See if we really need to send the packet. + */ + + if (before(skb->h.seq, sk->rcv_ack_seq +1)) + { + /* + * This is acked data. We can discard it. This + * cannot currently occur. + */ + + sk->retransmits = 0; + kfree_skb(skb, FREE_WRITE); + if (!sk->dead) + sk->write_space(sk); + } + else + { + struct tcphdr *th; + struct iphdr *iph; + int size; +/* + * put in the ack seq and window at this point rather than earlier, + * in order to keep them monotonic. We really want to avoid taking + * back window allocations. That's legal, but RFC1122 says it's frowned on. + * Ack and window will in general have changed since this packet was put + * on the write queue. + */ + iph = (struct iphdr *)(skb->data + + skb->dev->hard_header_len); + th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2)); + size = skb->len - (((unsigned char *) th) - skb->data); + + th->ack_seq = ntohl(sk->acked_seq); + th->window = ntohs(tcp_select_window(sk)); + + tcp_send_check(th, sk->saddr, sk->daddr, size, sk); + + sk->sent_seq = skb->h.seq; + + /* + * IP manages our queue for some crazy reason + */ + + sk->prot->queue_xmit(sk, skb->dev, skb, skb->free); + + /* + * Again we slide the timer wrongly + */ + + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + } + } +} + + +/* + * This routine deals with incoming acks, but not outgoing ones. + */ + +extern __inline__ int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) +{ + unsigned long ack; + int flag = 0; + + /* + * 1 - there was data in packet as well as ack or new data is sent or + * in shutdown state + * 2 - data from retransmit queue was acked and removed + * 4 - window shrunk or data from retransmit queue was acked and removed + */ + + if(sk->zapped) + return(1); /* Dead, cant ack any more so why bother */ + + /* + * Have we discovered a larger window + */ + + ack = ntohl(th->ack_seq); + + if (ntohs(th->window) > sk->max_window) + { + sk->max_window = ntohs(th->window); +#ifdef CONFIG_INET_PCTCP + /* Hack because we don't send partial packets to non SWS + handling hosts */ + sk->mss = min(sk->max_window>>1, sk->mtu); +#else + sk->mss = min(sk->max_window, sk->mtu); +#endif + } + + /* + * We have dropped back to keepalive timeouts. Thus we have + * no retransmits pending. + */ + + if (sk->retransmits && sk->ip_xmit_timeout == TIME_KEEPOPEN) + sk->retransmits = 0; + + /* + * If the ack is newer than sent or older than previous acks + * then we can probably ignore it. + */ + + if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) + { + if(sk->debug) + printk("Ack ignored %lu %lu\n",ack,sk->sent_seq); + + /* + * Keepalive processing. + */ + + if (after(ack, sk->sent_seq)) + { + return(0); + } + + /* + * Restart the keepalive timer. + */ + + if (sk->keepopen) + { + if(sk->ip_xmit_timeout==TIME_KEEPOPEN) + reset_xmit_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + } + return(1); + } + + /* + * If there is data set flag 1 + */ + + if (len != th->doff*4) + flag |= 1; + + /* + * See if our window has been shrunk. + */ + + if (after(sk->window_seq, ack+ntohs(th->window))) + { + /* + * We may need to move packets from the send queue + * to the write queue, if the window has been shrunk on us. + * The RFC says you are not allowed to shrink your window + * like this, but if the other end does, you must be able + * to deal with it. + */ + struct sk_buff *skb; + struct sk_buff *skb2; + struct sk_buff *wskb = NULL; + + skb2 = sk->send_head; + sk->send_head = NULL; + sk->send_tail = NULL; + + /* + * This is an artifact of a flawed concept. We want one + * queue and a smarter send routine when we send all. + */ + + flag |= 4; /* Window changed */ + + sk->window_seq = ack + ntohs(th->window); + cli(); + while (skb2 != NULL) + { + skb = skb2; + skb2 = skb->link3; + skb->link3 = NULL; + if (after(skb->h.seq, sk->window_seq)) + { + if (sk->packets_out > 0) + sk->packets_out--; + /* We may need to remove this from the dev send list. */ + if (skb->next != NULL) + { + skb_unlink(skb); + } + /* Now add it to the write_queue. */ + if (wskb == NULL) + skb_queue_head(&sk->write_queue,skb); + else + skb_append(wskb,skb); + wskb = skb; + } + else + { + if (sk->send_head == NULL) + { + sk->send_head = skb; + sk->send_tail = skb; + } + else + { + sk->send_tail->link3 = skb; + sk->send_tail = skb; + } + skb->link3 = NULL; + } + } + sti(); + } + + /* + * Pipe has emptied + */ + + if (sk->send_tail == NULL || sk->send_head == NULL) + { + sk->send_head = NULL; + sk->send_tail = NULL; + sk->packets_out= 0; + } + + /* + * Update the right hand window edge of the host + */ + + sk->window_seq = ack + ntohs(th->window); + + /* + * We don't want too many packets out there. + */ + + if (sk->ip_xmit_timeout == TIME_WRITE && + sk->cong_window < 2048 && after(ack, sk->rcv_ack_seq)) + { + /* + * This is Jacobson's slow start and congestion avoidance. + * SIGCOMM '88, p. 328. Because we keep cong_window in integral + * mss's, we can't do cwnd += 1 / cwnd. Instead, maintain a + * counter and increment it once every cwnd times. It's possible + * that this should be done only if sk->retransmits == 0. I'm + * interpreting "new data is acked" as including data that has + * been retransmitted but is just now being acked. + */ + if (sk->cong_window < sk->ssthresh) + /* + * In "safe" area, increase + */ + sk->cong_window++; + else + { + /* + * In dangerous area, increase slowly. In theory this is + * sk->cong_window += 1 / sk->cong_window + */ + if (sk->cong_count >= sk->cong_window) + { + sk->cong_window++; + sk->cong_count = 0; + } + else + sk->cong_count++; + } + } + + /* + * Remember the highest ack received. + */ + + sk->rcv_ack_seq = ack; + + /* + * If this ack opens up a zero window, clear backoff. It was + * being used to time the probes, and is probably far higher than + * it needs to be for normal retransmission. + */ + + if (sk->ip_xmit_timeout == TIME_PROBE0) + { + sk->retransmits = 0; /* Our probe was answered */ + + /* + * Was it a usable window open ? + */ + + if (skb_peek(&sk->write_queue) != NULL && /* should always be non-null */ + ! before (sk->window_seq, sk->write_queue.next->h.seq)) + { + sk->backoff = 0; + + /* + * Recompute rto from rtt. this eliminates any backoff. + */ + + sk->rto = ((sk->rtt >> 2) + sk->mdev) >> 1; + if (sk->rto > 120*HZ) + sk->rto = 120*HZ; + if (sk->rto < 20) /* Was 1*HZ, then 1 - turns out we must allow about + .2 of a second because of BSD delayed acks - on a 100Mb/sec link + .2 of a second is going to need huge windows (SIGH) */ + sk->rto = 20; + } + } + + /* + * See if we can take anything off of the retransmit queue. + */ + + while(sk->send_head != NULL) + { + /* Check for a bug. */ + if (sk->send_head->link3 && + after(sk->send_head->h.seq, sk->send_head->link3->h.seq)) + printk("INET: tcp.c: *** bug send_list out of order.\n"); + + /* + * If our packet is before the ack sequence we can + * discard it as it's confirmed to have arrived the other end. + */ + + if (before(sk->send_head->h.seq, ack+1)) + { + struct sk_buff *oskb; + if (sk->retransmits) + { + /* + * We were retransmitting. don't count this in RTT est + */ + flag |= 2; + + /* + * even though we've gotten an ack, we're still + * retransmitting as long as we're sending from + * the retransmit queue. Keeping retransmits non-zero + * prevents us from getting new data interspersed with + * retransmissions. + */ + + if (sk->send_head->link3) /* Any more queued retransmits? */ + sk->retransmits = 1; + else + sk->retransmits = 0; + } + /* + * Note that we only reset backoff and rto in the + * rtt recomputation code. And that doesn't happen + * if there were retransmissions in effect. So the + * first new packet after the retransmissions is + * sent with the backoff still in effect. Not until + * we get an ack from a non-retransmitted packet do + * we reset the backoff and rto. This allows us to deal + * with a situation where the network delay has increased + * suddenly. I.e. Karn's algorithm. (SIGCOMM '87, p5.) + */ + + /* + * We have one less packet out there. + */ + + if (sk->packets_out > 0) + sk->packets_out --; + /* + * Wake up the process, it can probably write more. + */ + if (!sk->dead) + sk->write_space(sk); + oskb = sk->send_head; + + if (!(flag&2)) /* Not retransmitting */ + { + long m; + + /* + * The following amusing code comes from Jacobson's + * article in SIGCOMM '88. Note that rtt and mdev + * are scaled versions of rtt and mean deviation. + * This is designed to be as fast as possible + * m stands for "measurement". + */ + + m = jiffies - oskb->when; /* RTT */ + if(m<=0) + m=1; /* IS THIS RIGHT FOR <0 ??? */ + m -= (sk->rtt >> 3); /* m is now error in rtt est */ + sk->rtt += m; /* rtt = 7/8 rtt + 1/8 new */ + if (m < 0) + m = -m; /* m is now abs(error) */ + m -= (sk->mdev >> 2); /* similar update on mdev */ + sk->mdev += m; /* mdev = 3/4 mdev + 1/4 new */ + + /* + * Now update timeout. Note that this removes any backoff. + */ + + sk->rto = ((sk->rtt >> 2) + sk->mdev) >> 1; + if (sk->rto > 120*HZ) + sk->rto = 120*HZ; + if (sk->rto < 20) /* Was 1*HZ - keep .2 as minimum cos of the BSD delayed acks */ + sk->rto = 20; + sk->backoff = 0; + } + flag |= (2|4); /* 2 is really more like 'don't adjust the rtt + In this case as we just set it up */ + cli(); + oskb = sk->send_head; + IS_SKB(oskb); + sk->send_head = oskb->link3; + if (sk->send_head == NULL) + { + sk->send_tail = NULL; + } + + /* + * We may need to remove this from the dev send list. + */ + + if (oskb->next) + skb_unlink(oskb); + sti(); + kfree_skb(oskb, FREE_WRITE); /* write. */ + if (!sk->dead) + sk->write_space(sk); + } + else + { + break; + } + } + + /* + * XXX someone ought to look at this too.. at the moment, if skb_peek() + * returns non-NULL, we complete ignore the timer stuff in the else + * clause. We ought to organize the code so that else clause can + * (should) be executed regardless, possibly moving the PROBE timer + * reset over. The skb_peek() thing should only move stuff to the + * write queue, NOT also manage the timer functions. + */ + + /* + * Maybe we can take some stuff off of the write queue, + * and put it onto the xmit queue. + */ + if (skb_peek(&sk->write_queue) != NULL) + { + if (after (sk->window_seq+1, sk->write_queue.next->h.seq) && + (sk->retransmits == 0 || + sk->ip_xmit_timeout != TIME_WRITE || + before(sk->write_queue.next->h.seq, sk->rcv_ack_seq + 1)) + && sk->packets_out < sk->cong_window) + { + /* + * Add more data to the send queue. + */ + flag |= 1; + tcp_write_xmit(sk); + } + else if (before(sk->window_seq, sk->write_queue.next->h.seq) && + sk->send_head == NULL && + sk->ack_backlog == 0 && + sk->state != TCP_TIME_WAIT) + { + /* + * Data to queue but no room. + */ + reset_xmit_timer(sk, TIME_PROBE0, sk->rto); + } + } + else + { + /* + * from TIME_WAIT we stay in TIME_WAIT as long as we rx packets + * from TCP_CLOSE we don't do anything + * + * from anything else, if there is write data (or fin) pending, + * we use a TIME_WRITE timeout, else if keepalive we reset to + * a KEEPALIVE timeout, else we delete the timer. + * + * We do not set flag for nominal write data, otherwise we may + * force a state where we start to write itsy bitsy tidbits + * of data. + */ + + switch(sk->state) { + case TCP_TIME_WAIT: + /* + * keep us in TIME_WAIT until we stop getting packets, + * reset the timeout. + */ + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + break; + case TCP_CLOSE: + /* + * don't touch the timer. + */ + break; + default: + /* + * Must check send_head, write_queue, and ack_backlog + * to determine which timeout to use. + */ + if (sk->send_head || skb_peek(&sk->write_queue) != NULL || sk->ack_backlog) { + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + } else if (sk->keepopen) { + reset_xmit_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + } else { + del_timer(&sk->retransmit_timer); + sk->ip_xmit_timeout = 0; + } + break; + } + } + + /* + * We have nothing queued but space to send. Send any partial + * packets immediately (end of Nagle rule application). + */ + + if (sk->packets_out == 0 && sk->partial != NULL && + skb_peek(&sk->write_queue) == NULL && sk->send_head == NULL) + { + flag |= 1; + tcp_send_partial(sk); + } + + /* + * In the LAST_ACK case, the other end FIN'd us. We then FIN'd them, and + * we are now waiting for an acknowledge to our FIN. The other end is + * already in TIME_WAIT. + * + * Move to TCP_CLOSE on success. + */ + + if (sk->state == TCP_LAST_ACK) + { + if (!sk->dead) + sk->state_change(sk); + if(sk->debug) + printk("rcv_ack_seq: %lX==%lX, acked_seq: %lX==%lX\n", + sk->rcv_ack_seq,sk->write_seq,sk->acked_seq,sk->fin_seq); + if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) + { + flag |= 1; + tcp_set_state(sk,TCP_CLOSE); + sk->shutdown = SHUTDOWN_MASK; + } + } + + /* + * Incoming ACK to a FIN we sent in the case of our initiating the close. + * + * Move to FIN_WAIT2 to await a FIN from the other end. Set + * SEND_SHUTDOWN but not RCV_SHUTDOWN as data can still be coming in. + */ + + if (sk->state == TCP_FIN_WAIT1) + { + + if (!sk->dead) + sk->state_change(sk); + if (sk->rcv_ack_seq == sk->write_seq) + { + flag |= 1; + sk->shutdown |= SEND_SHUTDOWN; + tcp_set_state(sk, TCP_FIN_WAIT2); + } + } + + /* + * Incoming ACK to a FIN we sent in the case of a simultaneous close. + * + * Move to TIME_WAIT + */ + + if (sk->state == TCP_CLOSING) + { + + if (!sk->dead) + sk->state_change(sk); + if (sk->rcv_ack_seq == sk->write_seq) + { + flag |= 1; + tcp_time_wait(sk); + } + } + + /* + * Final ack of a three way shake + */ + + if(sk->state==TCP_SYN_RECV) + { + tcp_set_state(sk, TCP_ESTABLISHED); + tcp_options(sk,th); + sk->dummy_th.dest=th->source; + sk->copied_seq = sk->acked_seq; + if(!sk->dead) + sk->state_change(sk); + if(sk->max_window==0) + { + sk->max_window=32; /* Sanity check */ + sk->mss=min(sk->max_window,sk->mtu); + } + } + + /* + * I make no guarantees about the first clause in the following + * test, i.e. "(!flag) || (flag&4)". I'm not entirely sure under + * what conditions "!flag" would be true. However I think the rest + * of the conditions would prevent that from causing any + * unnecessary retransmission. + * Clearly if the first packet has expired it should be + * retransmitted. The other alternative, "flag&2 && retransmits", is + * harder to explain: You have to look carefully at how and when the + * timer is set and with what timeout. The most recent transmission always + * sets the timer. So in general if the most recent thing has timed + * out, everything before it has as well. So we want to go ahead and + * retransmit some more. If we didn't explicitly test for this + * condition with "flag&2 && retransmits", chances are "when + rto < jiffies" + * would not be true. If you look at the pattern of timing, you can + * show that rto is increased fast enough that the next packet would + * almost never be retransmitted immediately. Then you'd end up + * waiting for a timeout to send each packet on the retransmission + * queue. With my implementation of the Karn sampling algorithm, + * the timeout would double each time. The net result is that it would + * take a hideous amount of time to recover from a single dropped packet. + * It's possible that there should also be a test for TIME_WRITE, but + * I think as long as "send_head != NULL" and "retransmit" is on, we've + * got to be in real retransmission mode. + * Note that tcp_do_retransmit is called with all==1. Setting cong_window + * back to 1 at the timeout will cause us to send 1, then 2, etc. packets. + * As long as no further losses occur, this seems reasonable. + */ + + if (((!flag) || (flag&4)) && sk->send_head != NULL && + (((flag&2) && sk->retransmits) || + (sk->send_head->when + sk->rto < jiffies))) + { + if(sk->send_head->when + sk->rto < jiffies) + tcp_retransmit(sk,0); + else + { + tcp_do_retransmit(sk, 1); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + } + } + + return(1); +} + + +/* + * Process the FIN bit. This now behaves as it is supposed to work + * and the FIN takes effect when it is validly part of sequence + * space. Not before when we get holes. + * + * If we are ESTABLISHED, a received fin moves us to CLOSE-WAIT + * (and thence onto LAST-ACK and finally, CLOSE, we never enter + * TIME-WAIT) + * + * If we are in FINWAIT-1, a received FIN indicates simultaneous + * close and we go into CLOSING (and later onto TIME-WAIT) + * + * If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT. + * + */ + +static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) +{ + sk->fin_seq = th->seq + skb->len + th->syn + th->fin; + + if (!sk->dead) + { + sk->state_change(sk); + sock_wake_async(sk->socket, 1); + } + + switch(sk->state) + { + case TCP_SYN_RECV: + case TCP_SYN_SENT: + case TCP_ESTABLISHED: + /* + * move to CLOSE_WAIT, tcp_data() already handled + * sending the ack. + */ + tcp_set_state(sk,TCP_CLOSE_WAIT); + if (th->rst) + sk->shutdown = SHUTDOWN_MASK; + break; + + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + /* + * received a retransmission of the FIN, do + * nothing. + */ + break; + case TCP_TIME_WAIT: + /* + * received a retransmission of the FIN, + * restart the TIME_WAIT timer. + */ + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + return(0); + case TCP_FIN_WAIT1: + /* + * This case occurs when a simultaneous close + * happens, we must ack the received FIN and + * enter the CLOSING state. + * + * This causes a WRITE timeout, which will either + * move on to TIME_WAIT when we timeout, or resend + * the FIN properly (maybe we get rid of that annoying + * FIN lost hang). The TIME_WRITE code is already correct + * for handling this timeout. + */ + + if(sk->ip_xmit_timeout != TIME_WRITE) + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + tcp_set_state(sk,TCP_CLOSING); + break; + case TCP_FIN_WAIT2: + /* + * received a FIN -- send ACK and enter TIME_WAIT + */ + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + sk->shutdown|=SHUTDOWN_MASK; + tcp_set_state(sk,TCP_TIME_WAIT); + break; + case TCP_CLOSE: + /* + * already in CLOSE + */ + break; + default: + tcp_set_state(sk,TCP_LAST_ACK); + + /* Start the timers. */ + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + return(0); + } + + return(0); +} + + + +/* + * This routine handles the data. If there is room in the buffer, + * it will be have already been moved into it. If there is no + * room, then we will just have to discard the packet. + */ + +extern __inline__ int tcp_data(struct sk_buff *skb, struct sock *sk, + unsigned long saddr, unsigned short len) +{ + struct sk_buff *skb1, *skb2; + struct tcphdr *th; + int dup_dumped=0; + unsigned long new_seq; + unsigned long shut_seq; + + th = skb->h.th; + skb->len = len -(th->doff*4); + + /* + * The bytes in the receive read/assembly queue has increased. Needed for the + * low memory discard algorithm + */ + + sk->bytes_rcv += skb->len; + + if (skb->len == 0 && !th->fin) + { + /* + * Don't want to keep passing ack's back and forth. + * (someone sent us dataless, boring frame) + */ + if (!th->ack) + tcp_send_ack(sk->sent_seq, sk->acked_seq,sk, th, saddr); + kfree_skb(skb, FREE_READ); + return(0); + } + + /* + * We no longer have anyone receiving data on this connection. + */ + +#ifndef TCP_DONT_RST_SHUTDOWN + + if(sk->shutdown & RCV_SHUTDOWN) + { + /* + * FIXME: BSD has some magic to avoid sending resets to + * broken 4.2 BSD keepalives. Much to my surprise a few non + * BSD stacks still have broken keepalives so we want to + * cope with it. + */ + + if(skb->len) /* We don't care if it's just an ack or + a keepalive/window probe */ + { + new_seq= th->seq + skb->len + th->syn; /* Right edge of _data_ part of frame */ + + /* Do this the way 4.4BSD treats it. Not what I'd + regard as the meaning of the spec but it's what BSD + does and clearly they know everything 8) */ + + /* + * This is valid because of two things + * + * a) The way tcp_data behaves at the bottom. + * b) A fin takes effect when read not when received. + */ + + shut_seq=sk->acked_seq+1; /* Last byte */ + + if(after(new_seq,shut_seq)) + { + if(sk->debug) + printk("Data arrived on %p after close [Data right edge %lX, Socket shut on %lX] %d\n", + sk, new_seq, shut_seq, sk->blog); + if(sk->dead) + { + sk->acked_seq = new_seq + th->fin; + tcp_reset(sk->saddr, sk->daddr, skb->h.th, + sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl); + tcp_statistics.TcpEstabResets++; + tcp_set_state(sk,TCP_CLOSE); + sk->err = EPIPE; + sk->shutdown = SHUTDOWN_MASK; + kfree_skb(skb, FREE_READ); + return 0; + } + } + } + } + +#endif + + /* + * Now we have to walk the chain, and figure out where this one + * goes into it. This is set up so that the last packet we received + * will be the first one we look at, that way if everything comes + * in order, there will be no performance loss, and if they come + * out of order we will be able to fit things in nicely. + * + * [AC: This is wrong. We should assume in order first and then walk + * forwards from the first hole based upon real traffic patterns.] + * + */ + + if (skb_peek(&sk->receive_queue) == NULL) /* Empty queue is easy case */ + { + skb_queue_head(&sk->receive_queue,skb); + skb1= NULL; + } + else + { + for(skb1=sk->receive_queue.prev; ; skb1 = skb1->prev) + { + if(sk->debug) + { + printk("skb1=%p :", skb1); + printk("skb1->h.th->seq = %ld: ", skb1->h.th->seq); + printk("skb->h.th->seq = %ld\n",skb->h.th->seq); + printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq, + sk->acked_seq); + } + + /* + * Optimisation: Duplicate frame or extension of previous frame from + * same sequence point (lost ack case). + * The frame contains duplicate data or replaces a previous frame + * discard the previous frame (safe as sk->inuse is set) and put + * the new one in its place. + */ + + if (th->seq==skb1->h.th->seq && skb->len>= skb1->len) + { + skb_append(skb1,skb); + skb_unlink(skb1); + kfree_skb(skb1,FREE_READ); + dup_dumped=1; + skb1=NULL; + break; + } + + /* + * Found where it fits + */ + + if (after(th->seq+1, skb1->h.th->seq)) + { + skb_append(skb1,skb); + break; + } + + /* + * See if we've hit the start. If so insert. + */ + if (skb1 == skb_peek(&sk->receive_queue)) + { + skb_queue_head(&sk->receive_queue, skb); + break; + } + } + } + + /* + * Figure out what the ack value for this frame is + */ + + th->ack_seq = th->seq + skb->len; + if (th->syn) + th->ack_seq++; + if (th->fin) + th->ack_seq++; + + if (before(sk->acked_seq, sk->copied_seq)) + { + printk("*** tcp.c:tcp_data bug acked < copied\n"); + sk->acked_seq = sk->copied_seq; + } + + /* + * Now figure out if we can ack anything. This is very messy because we really want two + * receive queues, a completed and an assembly queue. We also want only one transmit + * queue. + */ + + if ((!dup_dumped && (skb1 == NULL || skb1->acked)) || before(th->seq, sk->acked_seq+1)) + { + if (before(th->seq, sk->acked_seq+1)) + { + int newwindow; + + if (after(th->ack_seq, sk->acked_seq)) + { + newwindow = sk->window-(th->ack_seq - sk->acked_seq); + if (newwindow < 0) + newwindow = 0; + sk->window = newwindow; + sk->acked_seq = th->ack_seq; + } + skb->acked = 1; + + /* + * When we ack the fin, we do the FIN + * processing. + */ + + if (skb->h.th->fin) + { + tcp_fin(skb,sk,skb->h.th); + } + + for(skb2 = skb->next; + skb2 != (struct sk_buff *)&sk->receive_queue; + skb2 = skb2->next) + { + if (before(skb2->h.th->seq, sk->acked_seq+1)) + { + if (after(skb2->h.th->ack_seq, sk->acked_seq)) + { + newwindow = sk->window - + (skb2->h.th->ack_seq - sk->acked_seq); + if (newwindow < 0) + newwindow = 0; + sk->window = newwindow; + sk->acked_seq = skb2->h.th->ack_seq; + } + skb2->acked = 1; + /* + * When we ack the fin, we do + * the fin handling. + */ + if (skb2->h.th->fin) + { + tcp_fin(skb,sk,skb->h.th); + } + + /* + * Force an immediate ack. + */ + + sk->ack_backlog = sk->max_ack_backlog; + } + else + { + break; + } + } + + /* + * This also takes care of updating the window. + * This if statement needs to be simplified. + */ + if (!sk->delay_acks || + sk->ack_backlog >= sk->max_ack_backlog || + sk->bytes_rcv > sk->max_unacked || th->fin) { + /* tcp_send_ack(sk->sent_seq, sk->acked_seq,sk,th, saddr); */ + } + else + { + sk->ack_backlog++; + if(sk->debug) + printk("Ack queued.\n"); + reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME); + } + } + } + + /* + * If we've missed a packet, send an ack. + * Also start a timer to send another. + */ + + if (!skb->acked) + { + + /* + * This is important. If we don't have much room left, + * we need to throw out a few packets so we have a good + * window. Note that mtu is used, not mss, because mss is really + * for the send side. He could be sending us stuff as large as mtu. + */ + + while (sk->prot->rspace(sk) < sk->mtu) + { + skb1 = skb_peek(&sk->receive_queue); + if (skb1 == NULL) + { + printk("INET: tcp.c:tcp_data memory leak detected.\n"); + break; + } + + /* + * Don't throw out something that has been acked. + */ + + if (skb1->acked) + { + break; + } + + skb_unlink(skb1); + kfree_skb(skb1, FREE_READ); + } + tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr); + sk->ack_backlog++; + reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME); + } + else + { + tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr); + } + + /* + * Now tell the user we may have some data. + */ + + if (!sk->dead) + { + if(sk->debug) + printk("Data wakeup.\n"); + sk->data_ready(sk,0); + } + return(0); +} + + +/* + * This routine is only called when we have urgent data + * signalled. Its the 'slow' part of tcp_urg. It could be + * moved inline now as tcp_urg is only called from one + * place. We handle URGent data wrong. We have to - as + * BSD still doesn't use the correction from RFC961. + */ + +static void tcp_check_urg(struct sock * sk, struct tcphdr * th) +{ + unsigned long ptr = ntohs(th->urg_ptr); + + if (ptr) + ptr--; + ptr += th->seq; + + /* ignore urgent data that we've already seen and read */ + if (after(sk->copied_seq, ptr)) + return; + + /* do we already have a newer (or duplicate) urgent pointer? */ + if (sk->urg_data && !after(ptr, sk->urg_seq)) + return; + + /* tell the world about our new urgent pointer */ + if (sk->proc != 0) { + if (sk->proc > 0) { + kill_proc(sk->proc, SIGURG, 1); + } else { + kill_pg(-sk->proc, SIGURG, 1); + } + } + sk->urg_data = URG_NOTYET; + sk->urg_seq = ptr; +} + +/* + * This is the 'fast' part of urgent handling. + */ + +extern __inline__ int tcp_urg(struct sock *sk, struct tcphdr *th, + unsigned long saddr, unsigned long len) +{ + unsigned long ptr; + + /* + * Check if we get a new urgent pointer - normally not + */ + + if (th->urg) + tcp_check_urg(sk,th); + + /* + * Do we wait for any urgent data? - normally not + */ + + if (sk->urg_data != URG_NOTYET) + return 0; + + /* + * Is the urgent pointer pointing into this packet? + */ + + ptr = sk->urg_seq - th->seq + th->doff*4; + if (ptr >= len) + return 0; + + /* + * Ok, got the correct packet, update info + */ + + sk->urg_data = URG_VALID | *(ptr + (unsigned char *) th); + if (!sk->dead) + sk->data_ready(sk,0); + return 0; +} + +/* + * This will accept the next outstanding connection. + */ + +static struct sock *tcp_accept(struct sock *sk, int flags) +{ + struct sock *newsk; + struct sk_buff *skb; + + /* + * We need to make sure that this socket is listening, + * and that it has something pending. + */ + + if (sk->state != TCP_LISTEN) + { + sk->err = EINVAL; + return(NULL); + } + + /* Avoid the race. */ + cli(); + sk->inuse = 1; + + while((skb = tcp_dequeue_established(sk)) == NULL) + { + if (flags & O_NONBLOCK) + { + sti(); + release_sock(sk); + sk->err = EAGAIN; + return(NULL); + } + + release_sock(sk); + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + sk->err = ERESTARTSYS; + return(NULL); + } + sk->inuse = 1; + } + sti(); + + /* + * Now all we need to do is return skb->sk. + */ + + newsk = skb->sk; + + kfree_skb(skb, FREE_READ); + sk->ack_backlog--; + release_sock(sk); + return(newsk); +} + + +/* + * This will initiate an outgoing connection. + */ + +static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) +{ + struct sk_buff *buff; + struct device *dev=NULL; + unsigned char *ptr; + int tmp; + int atype; + struct tcphdr *t1; + struct rtable *rt; + + if (sk->state != TCP_CLOSE) + { + return(-EISCONN); + } + + if (addr_len < 8) + return(-EINVAL); + + if (usin->sin_family && usin->sin_family != AF_INET) + return(-EAFNOSUPPORT); + + /* + * connect() to INADDR_ANY means loopback (BSD'ism). + */ + + if(usin->sin_addr.s_addr==INADDR_ANY) + usin->sin_addr.s_addr=ip_my_addr(); + + /* + * Don't want a TCP connection going to a broadcast address + */ + + if ((atype=ip_chk_addr(usin->sin_addr.s_addr)) == IS_BROADCAST || atype==IS_MULTICAST) + return -ENETUNREACH; + + sk->inuse = 1; + sk->daddr = usin->sin_addr.s_addr; + sk->write_seq = tcp_init_seq(); + sk->window_seq = sk->write_seq; + sk->rcv_ack_seq = sk->write_seq -1; + sk->err = 0; + sk->dummy_th.dest = usin->sin_port; + release_sock(sk); + + buff = sk->prot->wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL); + if (buff == NULL) + { + return(-ENOMEM); + } + sk->inuse = 1; + buff->len = 24; + buff->sk = sk; + buff->free = 0; + buff->localroute = sk->localroute; + + t1 = (struct tcphdr *) buff->data; + + /* + * Put in the IP header and routing stuff. + */ + + rt=ip_rt_route(sk->daddr, NULL, NULL); + + + /* + * We need to build the routing stuff from the things saved in skb. + */ + + tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl); + if (tmp < 0) + { + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + release_sock(sk); + return(-ENETUNREACH); + } + + buff->len += tmp; + t1 = (struct tcphdr *)((char *)t1 +tmp); + + memcpy(t1,(void *)&(sk->dummy_th), sizeof(*t1)); + t1->seq = ntohl(sk->write_seq++); + sk->sent_seq = sk->write_seq; + buff->h.seq = sk->write_seq; + t1->ack = 0; + t1->window = 2; + t1->res1=0; + t1->res2=0; + t1->rst = 0; + t1->urg = 0; + t1->psh = 0; + t1->syn = 1; + t1->urg_ptr = 0; + t1->doff = 6; + /* use 512 or whatever user asked for */ + + if(rt!=NULL && (rt->rt_flags&RTF_WINDOW)) + sk->window_clamp=rt->rt_window; + else + sk->window_clamp=0; + + if (sk->user_mss) + sk->mtu = sk->user_mss; + else if(rt!=NULL && (rt->rt_flags&RTF_MTU)) + sk->mtu = rt->rt_mss; + else + { +#ifdef CONFIG_INET_SNARL + if ((sk->saddr ^ sk->daddr) & default_mask(sk->saddr)) +#else + if ((sk->saddr ^ sk->daddr) & dev->pa_mask) +#endif + sk->mtu = 576 - HEADER_SIZE; + else + sk->mtu = MAX_WINDOW; + } + /* + * but not bigger than device MTU + */ + + if(sk->mtu <32) + sk->mtu = 32; /* Sanity limit */ + + sk->mtu = min(sk->mtu, dev->mtu - HEADER_SIZE); + + /* + * Put in the TCP options to say MTU. + */ + + ptr = (unsigned char *)(t1+1); + ptr[0] = 2; + ptr[1] = 4; + ptr[2] = (sk->mtu) >> 8; + ptr[3] = (sk->mtu) & 0xff; + tcp_send_check(t1, sk->saddr, sk->daddr, + sizeof(struct tcphdr) + 4, sk); + + /* + * This must go first otherwise a really quick response will get reset. + */ + + tcp_set_state(sk,TCP_SYN_SENT); + sk->rto = TCP_TIMEOUT_INIT; +#if 0 /* we already did this */ + init_timer(&sk->retransmit_timer); +#endif + sk->retransmit_timer.function=&retransmit_timer; + sk->retransmit_timer.data = (unsigned long)sk; + reset_xmit_timer(sk, TIME_WRITE, sk->rto); /* Timer for repeating the SYN until an answer */ + sk->retransmits = TCP_SYN_RETRIES; + + sk->prot->queue_xmit(sk, dev, buff, 0); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); + tcp_statistics.TcpActiveOpens++; + tcp_statistics.TcpOutSegs++; + + release_sock(sk); + return(0); +} + + +/* This functions checks to see if the tcp header is actually acceptable. */ +extern __inline__ int tcp_sequence(struct sock *sk, struct tcphdr *th, short len, + struct options *opt, unsigned long saddr, struct device *dev) +{ + unsigned long next_seq; + + next_seq = len - 4*th->doff; + if (th->fin) + next_seq++; + /* if we have a zero window, we can't have any data in the packet.. */ + if (next_seq && !sk->window) + goto ignore_it; + next_seq += th->seq; + + /* + * This isn't quite right. sk->acked_seq could be more recent + * than sk->window. This is however close enough. We will accept + * slightly more packets than we should, but it should not cause + * problems unless someone is trying to forge packets. + */ + + /* have we already seen all of this packet? */ + if (!after(next_seq+1, sk->acked_seq)) + goto ignore_it; + /* or does it start beyond the window? */ + if (!before(th->seq, sk->acked_seq + sk->window + 1)) + goto ignore_it; + + /* ok, at least part of this packet would seem interesting.. */ + return 1; + +ignore_it: + if (th->rst) + return 0; + + /* + * Send a reset if we get something not ours and we are + * unsynchronized. Note: We don't do anything to our end. We + * are just killing the bogus remote connection then we will + * connect again and it will work (with luck). + */ + + if (sk->state==TCP_SYN_SENT || sk->state==TCP_SYN_RECV) + { + tcp_reset(sk->saddr,sk->daddr,th,sk->prot,NULL,dev, sk->ip_tos,sk->ip_ttl); + return 1; + } + + /* Try to resync things. */ + tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr); + return 0; +} + +/* + * When we get a reset we do this. + */ + +static int tcp_std_reset(struct sock *sk, struct sk_buff *skb) +{ + sk->zapped = 1; + sk->err = ECONNRESET; + if (sk->state == TCP_SYN_SENT) + sk->err = ECONNREFUSED; + if (sk->state == TCP_CLOSE_WAIT) + sk->err = EPIPE; +#ifdef TCP_DO_RFC1337 + /* + * Time wait assassination protection [RFC1337] + */ + if(sk->state!=TCP_TIME_WAIT) + { + tcp_set_state(sk,TCP_CLOSE); + sk->shutdown = SHUTDOWN_MASK; + } +#else + tcp_set_state(sk,TCP_CLOSE); + sk->shutdown = SHUTDOWN_MASK; +#endif + if (!sk->dead) + sk->state_change(sk); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); +} + +/* + * A TCP packet has arrived. + */ + +int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, + unsigned long daddr, unsigned short len, + unsigned long saddr, int redo, struct inet_protocol * protocol) +{ + struct tcphdr *th; + struct sock *sk; + int syn_ok=0; + + if (!skb) + { + printk("IMPOSSIBLE 1\n"); + return(0); + } + + if (!dev) + { + printk("IMPOSSIBLE 2\n"); + return(0); + } + + tcp_statistics.TcpInSegs++; + + if(skb->pkt_type!=PACKET_HOST) + { + kfree_skb(skb,FREE_READ); + return(0); + } + + th = skb->h.th; + + /* + * Find the socket. + */ + + sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr); + + /* + * If this socket has got a reset it's to all intents and purposes + * really dead. Count closed sockets as dead. + * + * Note: BSD appears to have a bug here. A 'closed' TCP in BSD + * simply drops data. This seems incorrect as a 'closed' TCP doesn't + * exist so should cause resets as if the port was unreachable. + */ + + if (sk!=NULL && (sk->zapped || sk->state==TCP_CLOSE)) + sk=NULL; + + if (!redo) + { + if (tcp_check(th, len, saddr, daddr )) + { + skb->sk = NULL; + kfree_skb(skb,FREE_READ); + /* + * We don't release the socket because it was + * never marked in use. + */ + return(0); + } + th->seq = ntohl(th->seq); + + /* See if we know about the socket. */ + if (sk == NULL) + { + /* + * No such TCB. If th->rst is 0 send a reset (checked in tcp_reset) + */ + tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255); + skb->sk = NULL; + /* + * Discard frame + */ + kfree_skb(skb, FREE_READ); + return(0); + } + + skb->len = len; + skb->acked = 0; + skb->used = 0; + skb->free = 0; + skb->saddr = daddr; + skb->daddr = saddr; + + /* We may need to add it to the backlog here. */ + cli(); + if (sk->inuse) + { + skb_queue_tail(&sk->back_log, skb); + sti(); + return(0); + } + sk->inuse = 1; + sti(); + } + else + { + if (sk==NULL) + { + tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255); + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return(0); + } + } + + + if (!sk->prot) + { + printk("IMPOSSIBLE 3\n"); + return(0); + } + + + /* + * Charge the memory to the socket. + */ + + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + + skb->sk=sk; + sk->rmem_alloc += skb->mem_len; + + /* + * This basically follows the flow suggested by RFC793, with the corrections in RFC1122. We + * don't implement precedence and we process URG incorrectly (deliberately so) for BSD bug + * compatibility. We also set up variables more thoroughly [Karn notes in the + * KA9Q code the RFC793 incoming segment rules don't initialise the variables for all paths]. + */ + + if(sk->state!=TCP_ESTABLISHED) /* Skip this lot for normal flow */ + { + + /* + * Now deal with unusual cases. + */ + + if(sk->state==TCP_LISTEN) + { + if(th->ack) /* These use the socket TOS.. might want to be the received TOS */ + tcp_reset(daddr,saddr,th,sk->prot,opt,dev,sk->ip_tos, sk->ip_ttl); + + /* + * We don't care for RST, and non SYN are absorbed (old segments) + * Broadcast/multicast SYN isn't allowed. Note - bug if you change the + * netmask on a running connection it can go broadcast. Even Sun's have + * this problem so I'm ignoring it + */ + + if(th->rst || !th->syn || th->ack || ip_chk_addr(daddr)!=IS_MYADDR) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + + /* + * Guess we need to make a new socket up + */ + + tcp_conn_request(sk, skb, daddr, saddr, opt, dev, tcp_init_seq()); + + /* + * Now we have several options: In theory there is nothing else + * in the frame. KA9Q has an option to send data with the syn, + * BSD accepts data with the syn up to the [to be] advertised window + * and Solaris 2.1 gives you a protocol error. For now we just ignore + * it, that fits the spec precisely and avoids incompatibilities. It + * would be nice in future to drop through and process the data. + */ + + release_sock(sk); + return 0; + } + + /* retransmitted SYN? */ + if (sk->state == TCP_SYN_RECV && th->syn && th->seq+1 == sk->acked_seq) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + + /* + * SYN sent means we have to look for a suitable ack and either reset + * for bad matches or go to connected + */ + + if(sk->state==TCP_SYN_SENT) + { + /* Crossed SYN or previous junk segment */ + if(th->ack) + { + /* We got an ack, but it's not a good ack */ + if(!tcp_ack(sk,th,saddr,len)) + { + /* Reset the ack - its an ack from a + different connection [ th->rst is checked in tcp_reset()] */ + tcp_statistics.TcpAttemptFails++; + tcp_reset(daddr, saddr, th, + sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + if(th->rst) + return tcp_std_reset(sk,skb); + if(!th->syn) + { + /* A valid ack from a different connection + start. Shouldn't happen but cover it */ + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + /* + * Ok.. it's good. Set up sequence numbers and + * move to established. + */ + syn_ok=1; /* Don't reset this connection for the syn */ + sk->acked_seq=th->seq+1; + sk->fin_seq=th->seq; + tcp_send_ack(sk->sent_seq,sk->acked_seq,sk,th,sk->daddr); + tcp_set_state(sk, TCP_ESTABLISHED); + tcp_options(sk,th); + sk->dummy_th.dest=th->source; + sk->copied_seq = sk->acked_seq; + if(!sk->dead) + { + sk->state_change(sk); + sock_wake_async(sk->socket, 0); + } + if(sk->max_window==0) + { + sk->max_window = 32; + sk->mss = min(sk->max_window, sk->mtu); + } + } + else + { + /* See if SYN's cross. Drop if boring */ + if(th->syn && !th->rst) + { + /* Crossed SYN's are fine - but talking to + yourself is right out... */ + if(sk->saddr==saddr && sk->daddr==daddr && + sk->dummy_th.source==th->source && + sk->dummy_th.dest==th->dest) + { + tcp_statistics.TcpAttemptFails++; + return tcp_std_reset(sk,skb); + } + tcp_set_state(sk,TCP_SYN_RECV); + + /* + * FIXME: + * Must send SYN|ACK here + */ + } + /* Discard junk segment */ + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + /* + * SYN_RECV with data maybe.. drop through + */ + goto rfc_step6; + } + + /* + * BSD has a funny hack with TIME_WAIT and fast reuse of a port. There is + * a more complex suggestion for fixing these reuse issues in RFC1644 + * but not yet ready for general use. Also see RFC1379. + */ + +#define BSD_TIME_WAIT +#ifdef BSD_TIME_WAIT + if (sk->state == TCP_TIME_WAIT && th->syn && sk->dead && + after(th->seq, sk->acked_seq) && !th->rst) + { + long seq=sk->write_seq; + if(sk->debug) + printk("Doing a BSD time wait\n"); + tcp_statistics.TcpEstabResets++; + sk->rmem_alloc -= skb->mem_len; + skb->sk = NULL; + sk->err=ECONNRESET; + tcp_set_state(sk, TCP_CLOSE); + sk->shutdown = SHUTDOWN_MASK; + release_sock(sk); + sk=get_sock(&tcp_prot, th->dest, saddr, th->source, daddr); + if (sk && sk->state==TCP_LISTEN) + { + sk->inuse=1; + skb->sk = sk; + sk->rmem_alloc += skb->mem_len; + tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+128000); + release_sock(sk); + return 0; + } + kfree_skb(skb, FREE_READ); + return 0; + } +#endif + } + + /* + * We are now in normal data flow (see the step list in the RFC) + * Note most of these are inline now. I'll inline the lot when + * I have time to test it hard and look at what gcc outputs + */ + + if(!tcp_sequence(sk,th,len,opt,saddr,dev)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + + if(th->rst) + return tcp_std_reset(sk,skb); + + /* + * !syn_ok is effectively the state test in RFC793. + */ + + if(th->syn && !syn_ok) + { + tcp_reset(daddr,saddr,th, &tcp_prot, opt, dev, skb->ip_hdr->tos, 255); + return tcp_std_reset(sk,skb); + } + + /* + * Process the ACK + */ + + + if(th->ack && !tcp_ack(sk,th,saddr,len)) + { + /* + * Our three way handshake failed. + */ + + if(sk->state==TCP_SYN_RECV) + { + tcp_reset(daddr, saddr, th,sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl); + } + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + +rfc_step6: /* I'll clean this up later */ + + /* + * Process urgent data + */ + + if(tcp_urg(sk, th, saddr, len)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + + + /* + * Process the encapsulated data + */ + + if(tcp_data(skb,sk, saddr, len)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return 0; + } + + /* + * And done + */ + + release_sock(sk); + return 0; +} + +/* + * This routine sends a packet with an out of date sequence + * number. It assumes the other end will try to ack it. + */ + +static void tcp_write_wakeup(struct sock *sk) +{ + struct sk_buff *buff; + struct tcphdr *t1; + struct device *dev=NULL; + int tmp; + + if (sk->zapped) + return; /* After a valid reset we can send no more */ + + /* + * Write data can still be transmitted/retransmitted in the + * following states. If any other state is encountered, return. + * [listen/close will never occur here anyway] + */ + + if (sk->state != TCP_ESTABLISHED && + sk->state != TCP_CLOSE_WAIT && + sk->state != TCP_FIN_WAIT1 && + sk->state != TCP_LAST_ACK && + sk->state != TCP_CLOSING + ) + { + return; + } + + buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); + if (buff == NULL) + return; + + buff->len = sizeof(struct tcphdr); + buff->free = 1; + buff->sk = sk; + buff->localroute = sk->localroute; + + t1 = (struct tcphdr *) buff->data; + + /* Put in the IP header and routing stuff. */ + tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); + if (tmp < 0) + { + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + return; + } + + buff->len += tmp; + t1 = (struct tcphdr *)((char *)t1 +tmp); + + memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); + + /* + * Use a previous sequence. + * This should cause the other end to send an ack. + */ + + t1->seq = htonl(sk->sent_seq-1); + t1->ack = 1; + t1->res1= 0; + t1->res2= 0; + t1->rst = 0; + t1->urg = 0; + t1->psh = 0; + t1->fin = 0; /* We are sending a 'previous' sequence, and 0 bytes of data - thus no FIN bit */ + t1->syn = 0; + t1->ack_seq = ntohl(sk->acked_seq); + t1->window = ntohs(tcp_select_window(sk)); + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + /* + * Send it and free it. + * This will prevent the timer from automatically being restarted. + */ + sk->prot->queue_xmit(sk, dev, buff, 1); + tcp_statistics.TcpOutSegs++; +} + +/* + * A window probe timeout has occurred. + */ + +void tcp_send_probe0(struct sock *sk) +{ + if (sk->zapped) + return; /* After a valid reset we can send no more */ + + tcp_write_wakeup(sk); + + sk->backoff++; + sk->rto = min(sk->rto << 1, 120*HZ); + reset_xmit_timer (sk, TIME_PROBE0, sk->rto); + sk->retransmits++; + sk->prot->retransmits ++; +} + +/* + * Socket option code for TCP. + */ + +int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) +{ + int val,err; + + if(level!=SOL_TCP) + return ip_setsockopt(sk,level,optname,optval,optlen); + + if (optval == NULL) + return(-EINVAL); + + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + + switch(optname) + { + case TCP_MAXSEG: +/* + * values greater than interface MTU won't take effect. however at + * the point when this call is done we typically don't yet know + * which interface is going to be used + */ + if(val<1||val>MAX_WINDOW) + return -EINVAL; + sk->user_mss=val; + return 0; + case TCP_NODELAY: + sk->nonagle=(val==0)?0:1; + return 0; + default: + return(-ENOPROTOOPT); + } +} + +int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) +{ + int val,err; + + if(level!=SOL_TCP) + return ip_getsockopt(sk,level,optname,optval,optlen); + + switch(optname) + { + case TCP_MAXSEG: + val=sk->user_mss; + break; + case TCP_NODELAY: + val=sk->nonagle; + break; + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); + + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); + + return(0); +} + + +struct proto tcp_prot = { + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + tcp_close, + tcp_read, + tcp_write, + tcp_sendto, + tcp_recvfrom, + ip_build_header, + tcp_connect, + tcp_accept, + ip_queue_xmit, + tcp_retransmit, + tcp_write_wakeup, + tcp_read_wakeup, + tcp_rcv, + tcp_select, + tcp_ioctl, + NULL, + tcp_shutdown, + tcp_setsockopt, + tcp_getsockopt, + 128, + 0, + {NULL,}, + "TCP", + 0, 0 +}; diff --git a/pfinet/linux-inet/udp.c b/pfinet/linux-inet/udp.c new file mode 100644 index 00000000..74912cbd --- /dev/null +++ b/pfinet/linux-inet/udp.c @@ -0,0 +1,735 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * The User Datagram Protocol (UDP). + * + * Version: @(#)udp.c 1.0.13 06/02/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * Fixes: + * Alan Cox : verify_area() calls + * Alan Cox : stopped close while in use off icmp + * messages. Not a fix but a botch that + * for udp at least is 'valid'. + * Alan Cox : Fixed icmp handling properly + * Alan Cox : Correct error for oversized datagrams + * Alan Cox : Tidied select() semantics. + * Alan Cox : udp_err() fixed properly, also now + * select and read wake correctly on errors + * Alan Cox : udp_send verify_area moved to avoid mem leak + * Alan Cox : UDP can count its memory + * Alan Cox : send to an unknown connection causes + * an ECONNREFUSED off the icmp, but + * does NOT close. + * Alan Cox : Switched to new sk_buff handlers. No more backlog! + * Alan Cox : Using generic datagram code. Even smaller and the PEEK + * bug no longer crashes it. + * Fred Van Kempen : Net2e support for sk->broadcast. + * Alan Cox : Uses skb_free_datagram + * Alan Cox : Added get/set sockopt support. + * Alan Cox : Broadcasting without option set returns EACCES. + * Alan Cox : No wakeup calls. Instead we now use the callbacks. + * Alan Cox : Use ip_tos and ip_ttl + * Alan Cox : SNMP Mibs + * Alan Cox : MSG_DONTROUTE, and 0.0.0.0 support. + * Matt Dillon : UDP length checks. + * Alan Cox : Smarter af_inet used properly. + * Alan Cox : Use new kernel side addressing. + * Alan Cox : Incorrect return on truncated datagram receive. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/system.h> +#include <asm/segment.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/fcntl.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/termios.h> +#include <linux/mm.h> +#include <linux/config.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include "snmp.h" +#include "ip.h" +#include "protocol.h" +#include "tcp.h" +#include <linux/skbuff.h> +#include "sock.h" +#include "udp.h" +#include "icmp.h" +#include "route.h" + +/* + * SNMP MIB for the UDP layer + */ + +struct udp_mib udp_statistics; + + +static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb, struct device *dev, long saddr, long daddr, int len); + +#define min(a,b) ((a)<(b)?(a):(b)) + + +/* + * This routine is called by the ICMP module when it gets some + * sort of error condition. If err < 0 then the socket should + * be closed and the error returned to the user. If err > 0 + * it's just the icmp type << 8 | icmp code. + * Header points to the ip header of the error packet. We move + * on past this. Then (as it used to claim before adjustment) + * header points to the first 8 bytes of the udp header. We need + * to find the appropriate port. + */ + +void udp_err(int err, unsigned char *header, unsigned long daddr, + unsigned long saddr, struct inet_protocol *protocol) +{ + struct udphdr *th; + struct sock *sk; + struct iphdr *ip=(struct iphdr *)header; + + header += 4*ip->ihl; + + /* + * Find the 8 bytes of post IP header ICMP included for us + */ + + th = (struct udphdr *)header; + + sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr); + + if (sk == NULL) + return; /* No socket for error */ + + if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) + { /* Slow down! */ + if (sk->cong_window > 1) + sk->cong_window = sk->cong_window/2; + return; + } + + /* + * Various people wanted BSD UDP semantics. Well they've come + * back out because they slow down response to stuff like dead + * or unreachable name servers and they screw term users something + * chronic. Oh and it violates RFC1122. So basically fix your + * client code people. + */ + +#ifdef CONFIG_I_AM_A_BROKEN_BSD_WEENIE + /* + * It's only fatal if we have connected to them. I'm not happy + * with this code. Some BSD comparisons need doing. + */ + + if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) + { + sk->err = icmp_err_convert[err & 0xff].errno; + sk->error_report(sk); + } +#else + if (icmp_err_convert[err & 0xff].fatal) + { + sk->err = icmp_err_convert[err & 0xff].errno; + sk->error_report(sk); + } +#endif +} + + +static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr) +{ + unsigned long sum; + + __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_UDP*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"(uh) + : "0"(sum), "c"(len/4) ,"1"(uh) + : "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"(uh) + : "0"(sum) ,"1"(uh) + : "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"(uh) + : "si", "ax", "bx"); + } + + /* + * We only want the bottom 16 bits, but we never cleared the top 16. + */ + + return((~sum) & 0xffff); +} + +/* + * Generate UDP checksums. These may be disabled, eg for fast NFS over ethernet + * We default them enabled.. if you turn them off you either know what you are + * doing or get burned... + */ + +static void udp_send_check(struct udphdr *uh, unsigned long saddr, + unsigned long daddr, int len, struct sock *sk) +{ + uh->check = 0; + if (sk && sk->no_check) + return; + uh->check = udp_check(uh, len, saddr, daddr); + + /* + * FFFF and 0 are the same, pick the right one as 0 in the + * actual field means no checksum. + */ + + if (uh->check == 0) + uh->check = 0xffff; +} + + +static int udp_send(struct sock *sk, struct sockaddr_in *sin, + unsigned char *from, int len, int rt) +{ + struct sk_buff *skb; + struct device *dev; + struct udphdr *uh; + unsigned char *buff; + unsigned long saddr; + int size, tmp; + int ttl; + + /* + * Allocate an sk_buff copy of the packet. + */ + + size = sk->prot->max_header + len; + skb = sock_alloc_send_skb(sk, size, 0, &tmp); + + + if (skb == NULL) + return tmp; + + skb->sk = NULL; /* to avoid changing sk->saddr */ + skb->free = 1; + skb->localroute = sk->localroute|(rt&MSG_DONTROUTE); + + /* + * Now build the IP and MAC header. + */ + + buff = skb->data; + saddr = sk->saddr; + dev = NULL; + ttl = sk->ip_ttl; +#ifdef CONFIG_IP_MULTICAST + if (MULTICAST(sin->sin_addr.s_addr)) + ttl = sk->ip_mc_ttl; +#endif + tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr, + &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl); + + skb->sk=sk; /* So memory is freed correctly */ + + /* + * Unable to put a header on the packet. + */ + + if (tmp < 0 ) + { + sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); + return(tmp); + } + + buff += tmp; + saddr = skb->saddr; /*dev->pa_addr;*/ + skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */ + skb->dev = dev; + + /* + * Fill in the UDP header. + */ + + uh = (struct udphdr *) buff; + uh->len = htons(len + sizeof(struct udphdr)); + uh->source = sk->dummy_th.source; + uh->dest = sin->sin_port; + buff = (unsigned char *) (uh + 1); + + /* + * Copy the user data. + */ + + memcpy_fromfs(buff, from, len); + + /* + * Set up the UDP checksum. + */ + + udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk); + + /* + * Send the datagram to the interface. + */ + + udp_statistics.UdpOutDatagrams++; + + sk->prot->queue_xmit(sk, dev, skb, 1); + return(len); +} + + +static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock, + unsigned flags, struct sockaddr_in *usin, int addr_len) +{ + struct sockaddr_in sin; + int tmp; + + /* + * Check the flags. We support no flags for UDP sending + */ + if (flags&~MSG_DONTROUTE) + return(-EINVAL); + /* + * Get and verify the address. + */ + + if (usin) + { + if (addr_len < sizeof(sin)) + return(-EINVAL); + memcpy(&sin,usin,sizeof(sin)); + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EINVAL); + if (sin.sin_port == 0) + return(-EINVAL); + } + else + { + if (sk->state != TCP_ESTABLISHED) + return(-EINVAL); + sin.sin_family = AF_INET; + sin.sin_port = sk->dummy_th.dest; + sin.sin_addr.s_addr = sk->daddr; + } + + /* + * BSD socket semantics. You must set SO_BROADCAST to permit + * broadcasting of data. + */ + + if(sin.sin_addr.s_addr==INADDR_ANY) + sin.sin_addr.s_addr=ip_my_addr(); + + if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) + return -EACCES; /* Must turn broadcast on first */ + + sk->inuse = 1; + + /* Send the packet. */ + tmp = udp_send(sk, &sin, from, len, flags); + + /* The datagram has been sent off. Release the socket. */ + release_sock(sk); + return(tmp); +} + +/* + * In BSD SOCK_DGRAM a write is just like a send. + */ + +static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock, + unsigned flags) +{ + return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0)); +} + + +/* + * IOCTL requests applicable to the UDP protocol + */ + +int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + int err; + switch(cmd) + { + case TIOCOUTQ: + { + unsigned long amount; + + if (sk->state == TCP_LISTEN) return(-EINVAL); + amount = sk->prot->wspace(sk)/*/2*/; + err=verify_area(VERIFY_WRITE,(void *)arg, + sizeof(unsigned long)); + if(err) + return(err); + put_fs_long(amount,(unsigned long *)arg); + return(0); + } + + case TIOCINQ: + { + struct sk_buff *skb; + unsigned long amount; + + if (sk->state == TCP_LISTEN) return(-EINVAL); + amount = 0; + skb = skb_peek(&sk->receive_queue); + if (skb != NULL) { + /* + * We will only return the amount + * of this packet since that is all + * that will be read. + */ + amount = skb->len; + } + 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); + } + return(0); +} + + +/* + * This should be easy, if there is something there we\ + * return it, otherwise we block. + */ + +int udp_recvfrom(struct sock *sk, unsigned char *to, int len, + int noblock, unsigned flags, struct sockaddr_in *sin, + int *addr_len) +{ + int copied = 0; + int truesize; + struct sk_buff *skb; + int er; + + /* + * Check any passed addresses + */ + + if (addr_len) + *addr_len=sizeof(*sin); + + /* + * From here the generic datagram does a lot of the work. Come + * the finished NET3, it will do _ALL_ the work! + */ + + skb=skb_recv_datagram(sk,flags,noblock,&er); + if(skb==NULL) + return er; + + truesize = skb->len; + copied = min(len, truesize); + + /* + * FIXME : should use udp header size info value + */ + + skb_copy_datagram(skb,sizeof(struct udphdr),to,copied); + sk->stamp=skb->stamp; + + /* Copy the address. */ + if (sin) + { + sin->sin_family = AF_INET; + sin->sin_port = skb->h.uh->source; + sin->sin_addr.s_addr = skb->daddr; + } + + skb_free_datagram(skb); + release_sock(sk); + return(truesize); +} + +/* + * Read has the same semantics as recv in SOCK_DGRAM + */ + +int udp_read(struct sock *sk, unsigned char *buff, int len, int noblock, + unsigned flags) +{ + return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); +} + + +int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) +{ + struct rtable *rt; + unsigned long sa; + if (addr_len < sizeof(*usin)) + return(-EINVAL); + + if (usin->sin_family && usin->sin_family != AF_INET) + return(-EAFNOSUPPORT); + if (usin->sin_addr.s_addr==INADDR_ANY) + usin->sin_addr.s_addr=ip_my_addr(); + + if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST) + return -EACCES; /* Must turn broadcast on first */ + + rt=ip_rt_route(usin->sin_addr.s_addr, NULL, &sa); + if(rt==NULL) + return -ENETUNREACH; + sk->saddr = sa; /* Update source address */ + sk->daddr = usin->sin_addr.s_addr; + sk->dummy_th.dest = usin->sin_port; + sk->state = TCP_ESTABLISHED; + return(0); +} + + +static void udp_close(struct sock *sk, int timeout) +{ + sk->inuse = 1; + sk->state = TCP_CLOSE; + if (sk->dead) + destroy_sock(sk); + else + release_sock(sk); +} + + +/* + * All we need to do is get the socket, and then do a checksum. + */ + +int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, + unsigned long daddr, unsigned short len, + unsigned long saddr, int redo, struct inet_protocol *protocol) +{ + struct sock *sk; + struct udphdr *uh; + unsigned short ulen; + int addr_type = IS_MYADDR; + + if(!dev || dev->pa_addr!=daddr) + addr_type=ip_chk_addr(daddr); + + /* + * Get the header. + */ + uh = (struct udphdr *) skb->h.uh; + + ip_statistics.IpInDelivers++; + + /* + * Validate the packet and the UDP length. + */ + + ulen = ntohs(uh->len); + + if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) + { + printk("UDP: short packet: %d/%d\n", ulen, len); + udp_statistics.UdpInErrors++; + kfree_skb(skb, FREE_WRITE); + return(0); + } + + if (uh->check && udp_check(uh, len, saddr, daddr)) + { + /* <mea@utu.fi> wants to know, who sent it, to + go and stomp on the garbage sender... */ + printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n", + ntohl(saddr),ntohs(uh->source), + ntohl(daddr),ntohs(uh->dest), + ulen); + udp_statistics.UdpInErrors++; + kfree_skb(skb, FREE_WRITE); + return(0); + } + + + len=ulen; + +#ifdef CONFIG_IP_MULTICAST + if (addr_type!=IS_MYADDR) + { + /* + * Multicasts and broadcasts go to each listener. + */ + struct sock *sknext=NULL; + sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest, + saddr, uh->source, daddr); + if(sk) + { + do + { + struct sk_buff *skb1; + + sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr); + if(sknext) + skb1=skb_clone(skb,GFP_ATOMIC); + else + skb1=skb; + if(skb1) + udp_deliver(sk, uh, skb1, dev,saddr,daddr,len); + sk=sknext; + } + while(sknext!=NULL); + } + else + kfree_skb(skb, FREE_READ); + return 0; + } +#endif + sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr); + if (sk == NULL) + { + udp_statistics.UdpNoPorts++; + if (addr_type == IS_MYADDR) + { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev); + } + /* + * Hmm. We got an UDP broadcast to a port to which we + * don't wanna listen. Ignore it. + */ + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + return(0); + } + + return udp_deliver(sk,uh,skb,dev, saddr, daddr, len); +} + +static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb, struct device *dev, long saddr, long daddr, int len) +{ + skb->sk = sk; + skb->dev = dev; + skb->len = len; + + /* + * These are supposed to be switched. + */ + + skb->daddr = saddr; + skb->saddr = daddr; + + + /* + * Charge it to the socket, dropping if the queue is full. + */ + + skb->len = len - sizeof(*uh); + + if (sock_queue_rcv_skb(sk,skb)<0) + { + udp_statistics.UdpInErrors++; + ip_statistics.IpInDiscards++; + ip_statistics.IpInDelivers--; + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + release_sock(sk); + return(0); + } + udp_statistics.UdpInDatagrams++; + release_sock(sk); + return(0); +} + + +struct proto udp_prot = { + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + udp_close, + udp_read, + udp_write, + udp_sendto, + udp_recvfrom, + ip_build_header, + udp_connect, + NULL, + ip_queue_xmit, + NULL, + NULL, + NULL, + udp_rcv, + datagram_select, + udp_ioctl, + NULL, + NULL, + ip_setsockopt, + ip_getsockopt, + 128, + 0, + {NULL,}, + "UDP", + 0, 0 +}; + |