diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/Makefile | 51 | ||||
-rw-r--r-- | net/ipv4/README.TCP | 39 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 1583 | ||||
-rw-r--r-- | net/ipv4/arp.c | 1392 | ||||
-rw-r--r-- | net/ipv4/checksum.c | 276 | ||||
-rw-r--r-- | net/ipv4/devinet.c | 215 | ||||
-rw-r--r-- | net/ipv4/icmp.c | 787 | ||||
-rw-r--r-- | net/ipv4/igmp.c | 402 | ||||
-rw-r--r-- | net/ipv4/ip.c | 2702 | ||||
-rw-r--r-- | net/ipv4/ip_fw.c | 1514 | ||||
-rw-r--r-- | net/ipv4/ipip.c | 95 | ||||
-rw-r--r-- | net/ipv4/packet.c | 410 | ||||
-rw-r--r-- | net/ipv4/proc.c | 268 | ||||
-rw-r--r-- | net/ipv4/protocol.c | 200 | ||||
-rw-r--r-- | net/ipv4/rarp.c | 491 | ||||
-rw-r--r-- | net/ipv4/raw.c | 304 | ||||
-rw-r--r-- | net/ipv4/route.c | 654 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 5169 | ||||
-rw-r--r-- | net/ipv4/timer.c | 163 | ||||
-rw-r--r-- | net/ipv4/udp.c | 690 | ||||
-rw-r--r-- | net/ipv4/utils.c | 91 |
21 files changed, 17496 insertions, 0 deletions
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile new file mode 100644 index 000000000..296c4d114 --- /dev/null +++ b/net/ipv4/Makefile @@ -0,0 +1,51 @@ +# +# Makefile for the Linux TCP/IP (INET) layer. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S $< + + +OBJS := utils.o route.o proc.o timer.o protocol.o packet.o \ + arp.o ip.o raw.o icmp.o tcp.o udp.o devinet.o af_inet.o \ + igmp.o ip_fw.o checksum.o ipip.o + +ifdef CONFIG_INET_RARP + +OBJS := $(OBJS) rarp.o + +endif + +ifdef CONFIG_INET + +ipv4.o: $(OBJS) + $(LD) -r -o ipv4.o $(OBJS) + +else + +ipv4.o: + $(AR) rcs ipv4.o + +endif + +dep: + $(CPP) -M *.c > .depend + +tar: + tar -cvf /dev/f1 . + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/net/ipv4/README.TCP b/net/ipv4/README.TCP new file mode 100644 index 000000000..f18963f88 --- /dev/null +++ b/net/ipv4/README.TCP @@ -0,0 +1,39 @@ +How the new TCP output machine [nyi] works. + + +Data is kept on a single queue. The skb->users flag tells us if the frame is +one that has been queued already. To add a frame we throw it on the end. Ack +walks down the list from the start. + +We keep a set of control flags + + + sk->tcp_pend_event + + TCP_PEND_ACK Ack needed + TCP_ACK_NOW Needed now + TCP_WINDOW Window update check + TCP_WINZERO Zero probing + + + sk->transmit_queue The transmission frame begin + sk->transmit_new First new frame pointer + sk->transmit_end Where to add frames + + sk->tcp_last_tx_ack Last ack seen + sk->tcp_dup_ack Dup ack count for fast retransmit + + +Frames are queued for output by tcp_write. We do our best to send the frames +off immediately if possible, but otherwise queue and compute the body +checksum in the copy. + +When a write is done we try to clear any pending events and piggy back them. +If the window is full we queue full sized frames. On the firs timeout in +zero window we split this. + +On a timer we walk the retransmit list to send any retransmits, update the +backoff timers etc. A change of route table stamp causes a change of header +and recompute. We add any new tcp level headers and refinish the checksum +before sending. + diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c new file mode 100644 index 000000000..fbfc44bb2 --- /dev/null +++ b/net/ipv4/af_inet.c @@ -0,0 +1,1583 @@ +/* + * 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... + * Alan Cox : routing cache support + * Alan Cox : memzero the socket structure for compactness. + * Matt Day : nonblock connect error handler + * + * 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 <net/ip.h> +#include <net/protocol.h> +#include <net/arp.h> +#include <net/rarp.h> +#include <net/route.h> +#include <net/tcp.h> +#include <net/udp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/raw.h> +#include <net/icmp.h> +#include <linux/ip_fw.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; + + if(sk->type==SOCK_PACKET) + return; + + 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->type==SOCK_PACKET) + return; + + if (!sk1->prot) + { + NETDEBUG(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); + udp_cache_zap(); + tcp_cache_zap(); + 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); + memset(sk,0,sizeof(*sk)); /* Efficient way to set most fields to zero */ +/* 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->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->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->protocol = protocol; + sk->sndbuf = SK_WMEM_MAX; + sk->rcvbuf = SK_RMEM_MAX; + sk->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/ + sk->cong_window = 1; /* start with only sending one packet at a time. */ + sk->priority = 1; + sk->state = TCP_CLOSE; +#ifdef WHAT_WE_DO_THE_MEMZERO_INSTEAD_OF + sk->stamp.tv_sec=0; + sk->wmem_alloc = 0; + sk->rmem_alloc = 0; + 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->mdev = 0; + sk->backoff = 0; + sk->packets_out = 0; + sk->cong_count = 0; + sk->ssthresh = 0; + sk->max_window = 0; + sk->urginline = 0; + sk->intr = 0; + sk->linger = 0; + sk->destroy = 0; + sk->shutdown = 0; + sk->keepopen = 0; + sk->zapped = 0; + sk->done = 0; + sk->ack_backlog = 0; + sk->window = 0; + sk->bytes_rcv = 0; + sk->dead = 0; + sk->ack_timed = 0; + sk->partial = NULL; + sk->user_mss = 0; + sk->debug = 0; + /* 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; + 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; + sk->blog = 0; + 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_route_cache=NULL; + sk->ip_hcache_ver= 0; + sk->ip_option_len=0; + sk->ip_option_flen=0; + sk->ip_opt_next_hop=0; + sk->ip_opt_ptr[0]=NULL; + sk->ip_opt_ptr[1]=NULL; +#endif + + /* this is how many unacked bytes we will accept for this socket. */ + sk->max_unacked = 2048; /* needs to be at most 2 full packets. */ + + skb_queue_head_init(&sk->write_queue); + skb_queue_head_init(&sk->receive_queue); + sk->mtu = 576; + sk->prot = prot; + sk->sleep = sock->wait; + 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); + sock->data =(void *) sk; + sk->dummy_th.doff = sizeof(sk->dummy_th)/4; + 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. Otherwise 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. */ + sock->data = NULL; + release_sock(sk); + sk->socket = NULL; + return(0); +} + + +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); + +#ifdef CONFIG_IP_MASQUERADE + /* + * The kernel masquerader needs some ports + */ + if(snum>=PORT_MASQ_BEGIN && snum<=PORT_MASQ_END) + return -EADDRINUSE; +#endif + + 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); + if(sock->type==SOCK_DGRAM) + udp_cache_zap(); + if(sock->type==SOCK_STREAM) + tcp_cache_zap(); + put_sock(snum, sk); + sk->dummy_th.source = ntohs(sk->num); + sk->daddr = 0; + sk->dummy_th.dest = 0; + } + sk->ip_route_cache=NULL; + 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)) + { + if(sk->err!=0) + { + err=sk->err; + sk->err=0; + return -err; + } + 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) + { + 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 SIOCDELRT: + 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: + case SIOCADDMULTI: + case SIOCDELMULTI: + 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.029 (Snapshot #6)\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/net/ipv4/arp.c b/net/ipv4/arp.c new file mode 100644 index 000000000..64bc060b9 --- /dev/null +++ b/net/ipv4/arp.c @@ -0,0 +1,1392 @@ +/* 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! + * Jonathan Naylor : Added support for NET/ROM. + */ + +#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 <linux/trdevice.h> +#include <net/ip.h> +#include <net/route.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/arp.h> +#ifdef CONFIG_AX25 +#include <net/ax25.h> +#ifdef CONFIG_NETROM +#include <net/netrom.h> +#endif +#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, +}; + +unsigned long arp_cache_stamp; + + +/* + * 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 */ + arp_cache_stamp++; + 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 */ + } + } + arp_cache_stamp++; + 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 +#ifdef CONFIG_NETROM + arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP); +#else + arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP); +#endif +#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); + arp_cache_stamp++; + 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 +#ifdef CONFIG_NETROM + case ARPHRD_NETROM: + 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; + + case ARPHRD_IEEE802: + 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->next = arp_tables[hash]; + entry->last_used = jiffies; + entry->flags = 0; + entry->ip = paddr; + entry->mask = DEF_ARP_NETMASK; + memset(entry->ha, 0, dev->addr_len); + entry->hlen = dev->addr_len; + entry->htype = dev->type; + entry->dev = dev; + init_timer(&entry->timer); + entry->timer.function = arp_expire_request; + entry->timer.data = (unsigned long)entry; + entry->timer.expires = ARP_RES_TIME; + 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 +#ifdef CONFIG_NETROM + if (entry->htype == ARPHRD_AX25 || entry->htype == ARPHRD_NETROM) + strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); + else { +#else + if(entry->htype==ARPHRD_AX25) + strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); + else { +#endif +#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; +} + + +int arp_find_cache(unsigned char *dp, unsigned long daddr, struct device *dev) +{ + /* + * We need the broadcast/multicast awareness here and the find routine split up. + */ + struct arp_table *entry; +#ifdef CONFIG_IP_MULTICAST + unsigned long taddr; +#endif + + switch (ip_chk_addr(daddr)) + { + case IS_MYADDR: + printk("ARP: arp called for own IP address\n"); + memcpy(dp, dev->dev_addr, dev->addr_len); + return 1; +#ifdef CONFIG_IP_MULTICAST + case IS_MULTICAST: + if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) + { + dp[0]=0x01; + dp[1]=0x00; + dp[2]=0x5e; + taddr=ntohl(daddr); + dp[5]=taddr&0xff; + taddr=taddr>>8; + dp[4]=taddr&0xff; + taddr=taddr>>8; + dp[3]=taddr&0x7f; + return 1; + } + /* + * If a device does not support multicast broadcast the stuff (eg AX.25 for now) + */ +#endif + + case IS_BROADCAST: + memcpy(dp, dev->broadcast, dev->addr_len); + return 1; + + default: + entry=arp_lookup(daddr, PROXY_NONE); + if(entry) + { + memcpy(dp,entry->ha, ETH_ALEN); + return 1; + } + } + return 0; +} + +/* + * 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 +#ifdef CONFIG_NETROM + case ARPHRD_NETROM: + htype = ARPHRD_NETROM; + hlen = 7; + break; +#endif + case ARPHRD_IEEE802: + htype = ARPHRD_IEEE802; + hlen = TR_ALEN; + break; + 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; + arp_cache_stamp++; + 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/net/ipv4/checksum.c b/net/ipv4/checksum.c new file mode 100644 index 000000000..59355e967 --- /dev/null +++ b/net/ipv4/checksum.c @@ -0,0 +1,276 @@ +/* + * 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. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, <jorge@laser.satlink.net> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * 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 <net/checksum.h> +#ifdef __mips__ +#include <asm/string.h> +#endif + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum) { +#ifdef __i386__ + __asm__(" + movl %%ecx, %%edx + cld + shrl $5, %%ecx + jz 2f + orl %%ecx, %%ecx +1: movl (%%esi), %%eax + adcl %%eax, %%ebx + movl 4(%%esi), %%eax + adcl %%eax, %%ebx + movl 8(%%esi), %%eax + adcl %%eax, %%ebx + movl 12(%%esi), %%eax + adcl %%eax, %%ebx + movl 16(%%esi), %%eax + adcl %%eax, %%ebx + movl 20(%%esi), %%eax + adcl %%eax, %%ebx + movl 24(%%esi), %%eax + adcl %%eax, %%ebx + movl 28(%%esi), %%eax + adcl %%eax, %%ebx + lea 32(%%esi), %%esi + dec %%ecx + jne 1b + adcl $0, %%ebx +2: movl %%edx, %%ecx + andl $28, %%ecx + je 4f + shrl $2, %%ecx + orl %%ecx, %%ecx +3: adcl (%%esi), %%ebx + lea 4(%%esi), %%esi + dec %%ecx + jne 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: " + : "=b"(sum) + : "0"(sum), "c"(len), "S"(buff) + : "ax", "bx", "cx", "dx", "si" ); +#elif defined (__mips__) + unsigned long scratch1; + unsigned long scratch2; + + __asm__(" + .set noreorder + .set noat + move %1,%4 + srl %1,%1,5 + beqz %1,2f + sll %1,%1,5 # delay slot + + addu %1,%5 +1: lw %2,0(%5) + addu %5,32 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-28(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-24(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-20(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-16(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-12(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-8(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + lw %2,-4(%5) + addu %0,$1 + addu %0,%2 + sltu $1,%0,%2 + + bne %5,%1,1b + addu %0,$1 # delay slot + +2: srl %1,%4,2 + bnez %1,4f + addu %1,%5 # delay slot +3: lw %2,0(%5) + addu %5,4 + addu %0,%2 + sltu $1,%0,%2 + bne %5,%1,3b + addu %0,$1 # delay slot + +4: andi $1,%4,2 + beqz %4,5f + lhu %2,0(%5) # delay slot + addu %5,2 + addu %0,%2 + sltu $1,%0,%2 + addu %0,$1 # delay slot + +5: andi $1,%4,1 + beqz %4,6f + lbu %2,0(%5) # delay slot + addu %0,%2 + sltu $1,%0,%2 + addu %0,$1 # delay slot +6: .set at + .set reorder" + : "=r"(sum), "=r" (scratch1), "=r" (scratch2) + : "0"(sum), "r"(len), "r"(buff) + : "$1"); +#else +#error Not implemented for this CPU +#endif + return(sum); +} + + + +/* + * copy from fs while checksumming, otherwise like csum_partial + */ + +unsigned int csum_partial_copyffs( char *src, char *dst, + int len, int sum) { +#ifdef __i386__ + __asm__(" + push %%ds + push %%es + movw %%ds, %%dx + movw %%dx, %%es + movw %%fs, %%dx + movw %%dx, %%ds + cld + cmpl $32, %%ecx + jb 2f + pushl %%ecx + shrl $5, %%ecx + orl %%ecx, %%ecx +1: movl (%%esi), %%eax + movl 4(%%esi), %%edx + adcl %%eax, %%ebx + movl %%eax, %%es:(%%edi) + adcl %%edx, %%ebx + movl %%edx, %%es:4(%%edi) + + movl 8(%%esi), %%eax + movl 12(%%esi), %%edx + adcl %%eax, %%ebx + movl %%eax, %%es:8(%%edi) + adcl %%edx, %%ebx + movl %%edx, %%es:12(%%edi) + + movl 16(%%esi), %%eax + movl 20(%%esi), %%edx + adcl %%eax, %%ebx + movl %%eax, %%es:16(%%edi) + adcl %%edx, %%ebx + movl %%edx, %%es:20(%%edi) + + movl 24(%%esi), %%eax + movl 28(%%esi), %%edx + adcl %%eax, %%ebx + movl %%eax, %%es:24(%%edi) + adcl %%edx, %%ebx + movl %%edx, %%es:28(%%edi) + + lea 32(%%esi), %%esi + lea 32(%%edi), %%edi + dec %%ecx + jne 1b + adcl $0, %%ebx + popl %%ecx +2: movl %%ecx, %%edx + andl $28, %%ecx + je 4f + shrl $2, %%ecx + orl %%ecx, %%ecx +3: movl (%%esi), %%eax + adcl %%eax, %%ebx + movl %%eax, %%es:(%%edi) + lea 4(%%esi), %%esi + lea 4(%%edi), %%edi + dec %%ecx + jne 3b + adcl $0, %%ebx +4: movl $0, %%eax + testl $2, %%edx + je 5f + lodsw + stosw + addl %%eax, %%ebx + movw $0, %%ax + adcl %%eax, %%ebx +5: test $1, %%edx + je 6f + lodsb + stosb + addl %%eax, %%ebx + adcl $0, %%ebx +6: pop %%es + pop %%ds + " + : "=b"(sum) + : "0"(sum), "c"(len), "S"(src), "D"(dst) + : "ax", "bx", "cx", "dx", "si", "di" ); +#elif defined (__mips__) + /* + * It's 2:30 am and I don't feel like doing it real ... + * This is lots slower than the real thing (tm) + */ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); +#else +#error Not implemented for this CPU +#endif + return(sum); +} + + + diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c new file mode 100644 index 000000000..794a7e897 --- /dev/null +++ b/net/ipv4/devinet.c @@ -0,0 +1,215 @@ +/* + * NET3 IP 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 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: + * Alan Cox, <gw4pts@gw4pts.ampr.org> + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/bitops.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 <net/ip.h> +#include <net/route.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/arp.h> + +/* + * Determine a default network mask, based on the IP address. + */ + +unsigned long ip_get_mask(unsigned long addr) +{ + unsigned long dst; + + if (addr == 0L) + return(0L); /* special case */ + + dst = ntohl(addr); + if (IN_CLASSA(dst)) + return(htonl(IN_CLASSA_NET)); + if (IN_CLASSB(dst)) + return(htonl(IN_CLASSB_NET)); + if (IN_CLASSC(dst)) + return(htonl(IN_CLASSC_NET)); + + /* + * Something else, probably a multicast. + */ + + return(0); +} + +/* + * Check the address for our address, broadcasts, etc. + * + * I intend to fix this to at the very least cache the last + * resolved entry. + */ + +int ip_chk_addr(unsigned long addr) +{ + struct device *dev; + unsigned long mask; + + /* + * Accept both `all ones' and `all zeros' as BROADCAST. + * (Support old BSD in other words). This old BSD + * support will go very soon as it messes other things + * up. + * Also accept `loopback broadcast' as BROADCAST. + */ + + if (addr == INADDR_ANY || addr == INADDR_BROADCAST || + addr == htonl(0x7FFFFFFFL)) + return IS_BROADCAST; + + mask = ip_get_mask(addr); + + /* + * Accept all of the `loopback' class A net. + */ + + if ((addr & mask) == htonl(0x7F000000L)) + return IS_MYADDR; + + /* + * OK, now check the interface addresses. We could + * speed this by keeping a dev and a dev_up chain. + */ + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (!(dev->flags & IFF_UP)) + continue; + /* + * If the protocol address of the device is 0 this is special + * and means we are address hunting (eg bootp). + */ + + if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/) + return IS_MYADDR; + /* + * Is it the exact IP address? + */ + + if (addr == dev->pa_addr) + return IS_MYADDR; + /* + * Is it our broadcast address? + */ + + if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr) + return IS_BROADCAST; + /* + * Nope. Check for a subnetwork broadcast. + */ + + if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0) + { + if ((addr & ~dev->pa_mask) == 0) + return IS_BROADCAST; + if ((addr & ~dev->pa_mask) == ~dev->pa_mask) + return IS_BROADCAST; + } + + /* + * Nope. Check for Network broadcast. + */ + + if (((addr ^ dev->pa_addr) & mask) == 0) + { + if ((addr & ~mask) == 0) + return IS_BROADCAST; + if ((addr & ~mask) == ~mask) + return IS_BROADCAST; + } + } + if(IN_MULTICAST(ntohl(addr))) + return IS_MULTICAST; + return 0; /* no match at all */ +} + + +/* + * Retrieve our own address. + * + * Because the loopback address (127.0.0.1) is already recognized + * automatically, we can use the loopback interface's address as + * our "primary" interface. This is the address used by IP et + * al when it doesn't know which address to use (i.e. it does not + * yet know from or to which interface to go...). + */ + +unsigned long ip_my_addr(void) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (dev->flags & IFF_LOOPBACK) + return(dev->pa_addr); + } + return(0); +} + +/* + * Find an interface that can handle addresses for a certain address. + * + * This needs optimising, since it's relatively trivial to collapse + * the two loops into one. + */ + +struct device * ip_dev_check(unsigned long addr) +{ + struct device *dev; + + for (dev = dev_base; dev; dev = dev->next) + { + if (!(dev->flags & IFF_UP)) + continue; + if (!(dev->flags & IFF_POINTOPOINT)) + continue; + if (addr != dev->pa_dstaddr) + continue; + return dev; + } + for (dev = dev_base; dev; dev = dev->next) + { + if (!(dev->flags & IFF_UP)) + continue; + if (dev->flags & IFF_POINTOPOINT) + continue; + if (dev->pa_mask & (addr ^ dev->pa_addr)) + continue; + return dev; + } + return NULL; +} + diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c new file mode 100644 index 000000000..7c1eea15d --- /dev/null +++ b/net/ipv4/icmp.c @@ -0,0 +1,787 @@ +/* + * 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. + * + * Internet Control Message Protocol (ICMP) + * + * Version: @(#)icmp.c 1.0.11 06/02/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * Stefan Becker, <stefanb@yello.ping.de> + * + * Fixes: + * Alan Cox : Generic queue usage. + * Gerhard Koerting: ICMP addressing corrected + * Alan Cox : Use tos/ttl settings + * Alan Cox : Protocol violations + * Alan Cox : SNMP Statistics + * Alan Cox : Routing errors + * Alan Cox : Changes for newer routing code + * Alan Cox : Removed old debugging junk + * Alan Cox : Fixed the ICMP error status of net/host unreachable + * Gerhard Koerting : Fixed broadcast ping properly + * Ulrich Kunitz : Fixed ICMP timestamp reply + * A.N.Kuznetsov : Multihoming fixes. + * Laco Rusnak : Multihoming fixes. + * Alan Cox : Tightened up icmp_send(). + * Alan Cox : Multicasts. + * Stefan Becker : ICMP redirects in icmp_send(). + * Peter Belding : Tightened up ICMP redirect handling + * Alan Cox : Tightened even more. + * + * + * + * 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/types.h> +#include <linux/sched.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 <linux/string.h> +#include <net/snmp.h> +#include <net/ip.h> +#include <net/route.h> +#include <net/protocol.h> +#include <net/icmp.h> +#include <net/tcp.h> +#include <net/snmp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <net/checksum.h> + + +#define min(a,b) ((a)<(b)?(a):(b)) + + +/* + * Statistics + */ + +struct icmp_mib icmp_statistics={0,}; + + +/* An array of errno for error messages from dest unreach. */ +struct icmp_err icmp_err_convert[] = { + { ENETUNREACH, 0 }, /* ICMP_NET_UNREACH */ + { EHOSTUNREACH, 0 }, /* ICMP_HOST_UNREACH */ + { ENOPROTOOPT, 1 }, /* ICMP_PROT_UNREACH */ + { ECONNREFUSED, 1 }, /* ICMP_PORT_UNREACH */ + { EOPNOTSUPP, 0 }, /* ICMP_FRAG_NEEDED */ + { EOPNOTSUPP, 0 }, /* ICMP_SR_FAILED */ + { ENETUNREACH, 1 }, /* ICMP_NET_UNKNOWN */ + { EHOSTDOWN, 1 }, /* ICMP_HOST_UNKNOWN */ + { ENONET, 1 }, /* ICMP_HOST_ISOLATED */ + { ENETUNREACH, 1 }, /* ICMP_NET_ANO */ + { EHOSTUNREACH, 1 }, /* ICMP_HOST_ANO */ + { EOPNOTSUPP, 0 }, /* ICMP_NET_UNR_TOS */ + { EOPNOTSUPP, 0 } /* ICMP_HOST_UNR_TOS */ +}; + + +/* + * Send an ICMP message in response to a situation + */ + +void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev) +{ + struct sk_buff *skb; + struct iphdr *iph; + int offset; + struct icmphdr *icmph; + int len; + struct device *ndev=NULL; /* Make this =dev to force replies on the same interface */ + unsigned long our_addr; + int atype; + + /* + * Find the original IP header. + */ + + iph = (struct iphdr *) (skb_in->data + dev->hard_header_len); + + /* + * No replies to MAC multicast + */ + + if(skb_in->pkt_type!=PACKET_HOST) + return; + + /* + * No replies to IP multicasting + */ + + atype=ip_chk_addr(iph->daddr); + if(atype==IS_BROADCAST || IN_MULTICAST(iph->daddr)) + return; + + /* + * Only reply to first fragment. + */ + + if(ntohs(iph->frag_off)&IP_OFFSET) + return; + + /* + * We must NEVER NEVER send an ICMP error to an ICMP error message + */ + + if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED) + { + + /* + * Is the original packet an ICMP packet? + */ + + if(iph->protocol==IPPROTO_ICMP) + { + icmph = (struct icmphdr *) ((char *) iph + + 4 * iph->ihl); + /* + * Check for ICMP error packets (Must never reply to + * an ICMP error). + */ + + if (icmph->type == ICMP_DEST_UNREACH || + icmph->type == ICMP_SOURCE_QUENCH || + icmph->type == ICMP_REDIRECT || + icmph->type == ICMP_TIME_EXCEEDED || + icmph->type == ICMP_PARAMETERPROB) + return; + } + } + icmp_statistics.IcmpOutMsgs++; + + /* + * This needs a tidy. + */ + + switch(type) + { + case ICMP_DEST_UNREACH: + icmp_statistics.IcmpOutDestUnreachs++; + break; + case ICMP_SOURCE_QUENCH: + icmp_statistics.IcmpOutSrcQuenchs++; + break; + case ICMP_REDIRECT: + icmp_statistics.IcmpOutRedirects++; + break; + case ICMP_ECHO: + icmp_statistics.IcmpOutEchos++; + break; + case ICMP_ECHOREPLY: + icmp_statistics.IcmpOutEchoReps++; + break; + case ICMP_TIME_EXCEEDED: + icmp_statistics.IcmpOutTimeExcds++; + break; + case ICMP_PARAMETERPROB: + icmp_statistics.IcmpOutParmProbs++; + break; + case ICMP_TIMESTAMP: + icmp_statistics.IcmpOutTimestamps++; + break; + case ICMP_TIMESTAMPREPLY: + icmp_statistics.IcmpOutTimestampReps++; + break; + case ICMP_ADDRESS: + icmp_statistics.IcmpOutAddrMasks++; + break; + case ICMP_ADDRESSREPLY: + icmp_statistics.IcmpOutAddrMaskReps++; + break; + } + /* + * Get some memory for the reply. + */ + + len = dev->hard_header_len + sizeof(struct iphdr) + sizeof(struct icmphdr) + + sizeof(struct iphdr) + 32; /* amount of header to return */ + + skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC); + if (skb == NULL) + { + icmp_statistics.IcmpOutErrors++; + return; + } + skb->free = 1; + + /* + * Build Layer 2-3 headers for message back to source. + */ + + our_addr = dev->pa_addr; + if (iph->daddr != our_addr && ip_chk_addr(iph->daddr) == IS_MYADDR) + our_addr = iph->daddr; + offset = ip_build_header(skb, our_addr, iph->saddr, + &ndev, IPPROTO_ICMP, NULL, len, + skb_in->ip_hdr->tos,255); + if (offset < 0) + { + icmp_statistics.IcmpOutErrors++; + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return; + } + + /* + * Re-adjust length according to actual IP header size. + */ + + skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8; + + /* + * Fill in the frame + */ + + icmph = (struct icmphdr *) (skb->data + offset); + icmph->type = type; + icmph->code = code; + icmph->checksum = 0; + icmph->un.gateway = info; /* This might not be meant for + this form of the union but it will + be right anyway */ + memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8); + + icmph->checksum = ip_compute_csum((unsigned char *)icmph, + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8); + + /* + * Send it and free it once sent. + */ + ip_queue_xmit(NULL, ndev, skb, 1); +} + + +/* + * Handle ICMP_UNREACH and ICMP_QUENCH. + */ + +static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb) +{ + struct inet_protocol *ipprot; + struct iphdr *iph; + unsigned char hash; + int err; + + err = (icmph->type << 8) | icmph->code; + iph = (struct iphdr *) (icmph + 1); + + switch(icmph->code & 7) + { + case ICMP_NET_UNREACH: + break; + case ICMP_HOST_UNREACH: + break; + case ICMP_PROT_UNREACH: +#ifdef CONFIG_NET_DEBUG + printk("ICMP: %s:%d: protocol unreachable.\n", + in_ntoa(iph->daddr), ntohs(iph->protocol)); +#endif + break; + case ICMP_PORT_UNREACH: + break; + case ICMP_FRAG_NEEDED: +#ifdef CONFIG_NET_DEBUG + printk("ICMP: %s: fragmentation needed and DF set.\n", + in_ntoa(iph->daddr)); +#endif + break; + case ICMP_SR_FAILED: +#ifdef CONFIG_NET_DEBUG + printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr)); +#endif + break; + default: + break; + } + + /* + * Get the protocol(s). + */ + + hash = iph->protocol & (MAX_INET_PROTOS -1); + + /* + * This can't change while we are doing it. + */ + + ipprot = (struct inet_protocol *) inet_protos[hash]; + while(ipprot != NULL) + { + struct inet_protocol *nextip; + + nextip = (struct inet_protocol *) ipprot->next; + + /* + * Pass it off to everyone who wants it. + */ + if (iph->protocol == ipprot->protocol && ipprot->err_handler) + { + ipprot->err_handler(err, (unsigned char *)(icmph + 1), + iph->daddr, iph->saddr, ipprot); + } + + ipprot = nextip; + } + kfree_skb(skb, FREE_READ); +} + + +/* + * Handle ICMP_REDIRECT. + */ + +static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, + struct device *dev, unsigned long source) +{ + struct rtable *rt; + struct iphdr *iph; + unsigned long ip; + + /* + * Get the copied header of the packet that caused the redirect + */ + + iph = (struct iphdr *) (icmph + 1); + ip = iph->daddr; + +#ifdef CONFIG_IP_FORWARD + /* + * We are a router. Routers should not respond to ICMP_REDIRECT messages. + */ + printk("icmp: ICMP redirect from %s on %s ignored.\n", in_ntoa(source), dev->name); +#else + switch(icmph->code & 7) + { + case ICMP_REDIR_NET: + /* + * This causes a problem with subnetted networks. What we should do + * is use ICMP_ADDRESS to get the subnet mask of the problem route + * and set both. But we don't.. + */ +#ifdef not_a_good_idea + ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY), + ip, 0, icmph->un.gateway, dev,0, 0); + break; +#endif + case ICMP_REDIR_HOST: + /* + * Add better route to host. + * But first check that the redirect + * comes from the old gateway.. + * And make sure it's an ok host address + * (not some confused thing sending our + * address) + */ + rt = ip_rt_route(ip, NULL, NULL); + if (!rt) + break; + if (rt->rt_gateway != source || + ((icmph->un.gateway^dev->pa_addr)&dev->pa_mask) || + ip_chk_addr(icmph->un.gateway)) + break; + printk("ICMP redirect from %s\n", in_ntoa(source)); + ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY), + ip, 0, icmph->un.gateway, dev,0, 0, 0); + break; + case ICMP_REDIR_NETTOS: + case ICMP_REDIR_HOSTTOS: + printk("ICMP: cannot handle TOS redirects yet!\n"); + break; + default: + break; + } +#endif + /* + * Discard the original packet + */ + + kfree_skb(skb, FREE_READ); +} + + +/* + * Handle ICMP_ECHO ("ping") requests. + */ + +static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, + unsigned long saddr, unsigned long daddr, int len, + struct options *opt) +{ + struct icmphdr *icmphr; + struct sk_buff *skb2; + struct device *ndev=NULL; + int size, offset; + + icmp_statistics.IcmpOutEchoReps++; + icmp_statistics.IcmpOutMsgs++; + + size = dev->hard_header_len + 64 + len; + skb2 = alloc_skb(size, GFP_ATOMIC); + + if (skb2 == NULL) + { + icmp_statistics.IcmpOutErrors++; + kfree_skb(skb, FREE_READ); + return; + } + skb2->free = 1; + + /* Build Layer 2-3 headers for message back to source */ + offset = ip_build_header(skb2, daddr, saddr, &ndev, + IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255); + if (offset < 0) + { + icmp_statistics.IcmpOutErrors++; + printk("ICMP: Could not build IP Header for ICMP ECHO Response\n"); + kfree_skb(skb2,FREE_WRITE); + kfree_skb(skb, FREE_READ); + return; + } + + /* + * Re-adjust length according to actual IP header size. + */ + + skb2->len = offset + len; + + /* + * Build ICMP_ECHO Response message. + */ + icmphr = (struct icmphdr *) (skb2->data + offset); + memcpy((char *) icmphr, (char *) icmph, len); + icmphr->type = ICMP_ECHOREPLY; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len); + + /* + * Ship it out - free it when done + */ + ip_queue_xmit((struct sock *)NULL, ndev, skb2, 1); + + /* + * Free the received frame + */ + + kfree_skb(skb, FREE_READ); +} + +/* + * Handle ICMP Timestamp requests. + */ + +static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, + unsigned long saddr, unsigned long daddr, int len, + struct options *opt) +{ + struct icmphdr *icmphr; + struct sk_buff *skb2; + int size, offset; + unsigned long *timeptr, midtime; + struct device *ndev=NULL; + + if (len != 20) + { + printk( + "ICMP: Size (%d) of ICMP_TIMESTAMP request should be 20!\n", + len); + icmp_statistics.IcmpInErrors++; + /* correct answers are possible for everything >= 12 */ + if (len < 12) + return; + } + + size = dev->hard_header_len + 84; + + if (! (skb2 = alloc_skb(size, GFP_ATOMIC))) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + icmp_statistics.IcmpOutErrors++; + return; + } + skb2->free = 1; + +/* + * Build Layer 2-3 headers for message back to source + */ + + offset = ip_build_header(skb2, daddr, saddr, &ndev, IPPROTO_ICMP, opt, len, + skb->ip_hdr->tos, 255); + if (offset < 0) + { + printk("ICMP: Could not build IP Header for ICMP TIMESTAMP Response\n"); + kfree_skb(skb2, FREE_WRITE); + kfree_skb(skb, FREE_READ); + icmp_statistics.IcmpOutErrors++; + return; + } + + /* + * Re-adjust length according to actual IP header size. + */ + skb2->len = offset + 20; + + /* + * Build ICMP_TIMESTAMP Response message. + */ + + icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset); + memcpy((char *) icmphr, (char *) icmph, 12); + icmphr->type = ICMP_TIMESTAMPREPLY; + icmphr->code = icmphr->checksum = 0; + + /* fill in the current time as ms since midnight UT: */ + midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000; + timeptr = (unsigned long *) (icmphr + 1); + /* + * the originate timestamp (timeptr [0]) is still in the copy: + */ + timeptr [1] = timeptr [2] = htonl(midtime); + + icmphr->checksum = ip_compute_csum((unsigned char *) icmphr, 20); + + /* + * Ship it out - free it when done + */ + + ip_queue_xmit((struct sock *) NULL, ndev, skb2, 1); + icmp_statistics.IcmpOutTimestampReps++; + kfree_skb(skb, FREE_READ); +} + + + + +/* + * Handle the ICMP INFORMATION REQUEST. + */ + +static void icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, + unsigned long saddr, unsigned long daddr, int len, + struct options *opt) +{ + /* Obsolete */ + kfree_skb(skb, FREE_READ); +} + + +/* + * Handle ICMP_ADDRESS_MASK requests. + */ + +static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, + unsigned long saddr, unsigned long daddr, int len, + struct options *opt) +{ + struct icmphdr *icmphr; + struct sk_buff *skb2; + int size, offset; + struct device *ndev=NULL; + + icmp_statistics.IcmpOutMsgs++; + icmp_statistics.IcmpOutAddrMaskReps++; + + size = dev->hard_header_len + 64 + len; + skb2 = alloc_skb(size, GFP_ATOMIC); + if (skb2 == NULL) + { + icmp_statistics.IcmpOutErrors++; + kfree_skb(skb, FREE_READ); + return; + } + skb2->free = 1; + + /* + * Build Layer 2-3 headers for message back to source + */ + + offset = ip_build_header(skb2, daddr, saddr, &ndev, + IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255); + if (offset < 0) + { + icmp_statistics.IcmpOutErrors++; + printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n"); + kfree_skb(skb2,FREE_WRITE); + kfree_skb(skb, FREE_READ); + return; + } + + /* + * Re-adjust length according to actual IP header size. + */ + + skb2->len = offset + len; + + /* + * Build ICMP ADDRESS MASK Response message. + */ + + icmphr = (struct icmphdr *) (skb2->data + offset); + icmphr->type = ICMP_ADDRESSREPLY; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->un.echo.id = icmph->un.echo.id; + icmphr->un.echo.sequence = icmph->un.echo.sequence; + memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask)); + + icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len); + + /* Ship it out - free it when done */ + ip_queue_xmit((struct sock *)NULL, ndev, skb2, 1); + + skb->sk = NULL; + kfree_skb(skb, FREE_READ); +} + + +/* + * Deal with incoming ICMP packets. + */ + +int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt, + unsigned long daddr, unsigned short len, + unsigned long saddr, int redo, struct inet_protocol *protocol) +{ + struct icmphdr *icmph; + unsigned char *buff; + + /* + * Drop broadcast packets. IP has done a broadcast check and ought one day + * to pass on that information. + */ + + icmp_statistics.IcmpInMsgs++; + + + /* + * Grab the packet as an icmp object + */ + + buff = skb1->h.raw; + icmph = (struct icmphdr *) buff; + + /* + * Validate the packet first + */ + + if (ip_compute_csum((unsigned char *) icmph, len)) + { + /* Failed checksum! */ + icmp_statistics.IcmpInErrors++; + printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr)); + kfree_skb(skb1, FREE_READ); + return(0); + } + + /* + * Parse the ICMP message + */ + + if (ip_chk_addr(daddr) != IS_MYADDR) + { + if (icmph->type != ICMP_ECHO) + { + icmp_statistics.IcmpInErrors++; + kfree_skb(skb1, FREE_READ); + return(0); + } + daddr=dev->pa_addr; + } + + switch(icmph->type) + { + case ICMP_TIME_EXCEEDED: + icmp_statistics.IcmpInTimeExcds++; + icmp_unreach(icmph, skb1); + return 0; + case ICMP_DEST_UNREACH: + icmp_statistics.IcmpInDestUnreachs++; + icmp_unreach(icmph, skb1); + return 0; + case ICMP_SOURCE_QUENCH: + icmp_statistics.IcmpInSrcQuenchs++; + icmp_unreach(icmph, skb1); + return(0); + case ICMP_REDIRECT: + icmp_statistics.IcmpInRedirects++; + icmp_redirect(icmph, skb1, dev, saddr); + return(0); + case ICMP_ECHO: + icmp_statistics.IcmpInEchos++; + icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_ECHOREPLY: + icmp_statistics.IcmpInEchoReps++; + kfree_skb(skb1, FREE_READ); + return(0); + case ICMP_TIMESTAMP: + icmp_statistics.IcmpInTimestamps++; + icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_TIMESTAMPREPLY: + icmp_statistics.IcmpInTimestampReps++; + kfree_skb(skb1,FREE_READ); + return 0; + /* INFO is obsolete and doesn't even feature in the SNMP stats */ + case ICMP_INFO_REQUEST: + icmp_info(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_INFO_REPLY: + skb1->sk = NULL; + kfree_skb(skb1, FREE_READ); + return(0); + case ICMP_ADDRESS: + icmp_statistics.IcmpInAddrMasks++; + icmp_address(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_ADDRESSREPLY: + /* + * We ought to set our netmask on receiving this, but + * experience shows it's a waste of effort. + */ + icmp_statistics.IcmpInAddrMaskReps++; + kfree_skb(skb1, FREE_READ); + return(0); + default: + icmp_statistics.IcmpInErrors++; + kfree_skb(skb1, FREE_READ); + return(0); + } + /*NOTREACHED*/ + kfree_skb(skb1, FREE_READ); + return(-1); +} + + +/* + * Perform any ICMP-related I/O control requests. + * [to vanish soon] + */ + +int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + switch(cmd) + { + default: + return(-EINVAL); + } + return(0); +} diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c new file mode 100644 index 000000000..ec182d8e3 --- /dev/null +++ b/net/ipv4/igmp.c @@ -0,0 +1,402 @@ +/* + * Linux NET3: Internet Gateway Management Protocol [IGMP] + * + * Authors: + * Alan Cox <Alan.Cox@linux.org> + * + * 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 : Added lots of __inline__ to optimise + * the memory usage of all the tiny little + * functions. + */ + + +#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 <net/ip.h> +#include <net/protocol.h> +#include <net/route.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <linux/igmp.h> +#include <net/checksum.h> +#include <net/head_explode.h> + +#ifdef CONFIG_IP_MULTICAST + + +/* + * Timer management + */ + + +extern __inline__ void igmp_stop_timer(struct ip_mc_list *im) +{ + del_timer(&im->timer); + im->tm_running=0; +} + +extern __inline__ int random(void) +{ + static unsigned long seed=152L; + seed=seed*69069L+1; + return seed^jiffies; +} + +/* + * Inlined as its only called once. + */ + +extern __inline__ 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; + unsigned char *dp; + + 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; + } + dp=skb->data+tmp; + skb->len=tmp+sizeof(struct igmphdr); + + *dp++=type; + *dp++=0; + skb->h.raw=dp; + dp=imp_putu16(dp,0); /* checksum */ + dp=imp_putn32(dp,address); /* Address (already in net order) */ + imp_putn16(skb->h.raw,ip_compute_csum(skb->data+tmp,sizeof(struct igmphdr))); /* Checksum fill */ + 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); +} + +extern __inline__ 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; +} + + +extern __inline__ 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); +} + +extern __inline__ 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. + */ + +extern __inline__ 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); +} + +extern __inline__ 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);*/ +} + +extern __inline__ 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 igmp_header igh; + + /* Pull the IGMP header */ + igmp_explode(skb->h.raw,&igh); + + if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl!=1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr))) + { + 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/net/ipv4/ip.c b/net/ipv4/ip.c new file mode 100644 index 000000000..62d2ad38e --- /dev/null +++ b/net/ipv4/ip.c @@ -0,0 +1,2702 @@ +/* + * 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 Internet Protocol (IP) module. + * + * Version: @(#)ip.c 1.0.16b 9/1/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Donald Becker, <becker@super.org> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * Richard Underwood + * Stefan Becker, <stefanb@yello.ping.de> + * Jorge Cwik, <jorge@laser.satlink.net> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * + * + * Fixes: + * Alan Cox : Commented a couple of minor bits of surplus code + * Alan Cox : Undefining IP_FORWARD doesn't include the code + * (just stops a compiler warning). + * Alan Cox : Frames with >=MAX_ROUTE record routes, strict routes or loose routes + * are junked rather than corrupting things. + * Alan Cox : Frames to bad broadcast subnets are dumped + * We used to process them non broadcast and + * boy could that cause havoc. + * Alan Cox : ip_forward sets the free flag on the + * new frame it queues. Still crap because + * it copies the frame but at least it + * doesn't eat memory too. + * Alan Cox : Generic queue code and memory fixes. + * Fred Van Kempen : IP fragment support (borrowed from NET2E) + * Gerhard Koerting: Forward fragmented frames correctly. + * Gerhard Koerting: Fixes to my fix of the above 8-). + * Gerhard Koerting: IP interface addressing fix. + * Linus Torvalds : More robustness checks + * Alan Cox : Even more checks: Still not as robust as it ought to be + * Alan Cox : Save IP header pointer for later + * Alan Cox : ip option setting + * Alan Cox : Use ip_tos/ip_ttl settings + * Alan Cox : Fragmentation bogosity removed + * (Thanks to Mark.Bush@prg.ox.ac.uk) + * Dmitry Gorodchanin : Send of a raw packet crash fix. + * Alan Cox : Silly ip bug when an overlength + * fragment turns up. Now frees the + * queue. + * Linus Torvalds/ : Memory leakage on fragmentation + * Alan Cox : handling. + * Gerhard Koerting: Forwarding uses IP priority hints + * Teemu Rantanen : Fragment problems. + * Alan Cox : General cleanup, comments and reformat + * Alan Cox : SNMP statistics + * Alan Cox : BSD address rule semantics. Also see + * UDP as there is a nasty checksum issue + * if you do things the wrong way. + * Alan Cox : Always defrag, moved IP_FORWARD to the config.in file + * Alan Cox : IP options adjust sk->priority. + * Pedro Roque : Fix mtu/length error in ip_forward. + * Alan Cox : Avoid ip_chk_addr when possible. + * Richard Underwood : IP multicasting. + * Alan Cox : Cleaned up multicast handlers. + * Alan Cox : RAW sockets demultiplex in the BSD style. + * Gunther Mayer : Fix the SNMP reporting typo + * Alan Cox : Always in group 224.0.0.1 + * Pauline Middelink : Fast ip_checksum update when forwarding + * Masquerading support. + * Alan Cox : Multicast loopback error for 224.0.0.1 + * Alan Cox : IP_MULTICAST_LOOP option. + * Alan Cox : Use notifiers. + * Bjorn Ekwall : Removed ip_csum (from slhc.c too) + * Bjorn Ekwall : Moved ip_fast_csum to ip.h (inline!) + * Stefan Becker : Send out ICMP HOST REDIRECT + * Arnt Gulbrandsen : ip_build_xmit + * Alan Cox : Per socket routing cache + * Alan Cox : Fixed routing cache, added header cache. + * Alan Cox : Loopback didnt work right in original ip_build_xmit - fixed it. + * Alan Cox : Only send ICMP_REDIRECT if src/dest are the same net. + * Alan Cox : Incoming IP option handling. + * Alan Cox : Set saddr on raw output frames as per BSD. + * Alan Cox : Stopped broadcast source route explosions. + * Alan Cox : Can disable source routing + * + * + * + * To Fix: + * IP option processing is mostly not needed. ip_forward needs to know about routing rules + * and time stamp but that's about all. Use the route mtu field here too + * IP fragmentation wants rewriting cleanly. The RFC815 algorithm is much more efficient + * and could be made very efficient with the addition of some virtual memory hacks to permit + * the allocation of a buffer that can then be 'grown' by twiddling page tables. + * Output fragmentation wants updating along with the buffer management to use a single + * interleaved copy algorithm so that fragmenting has a one copy overhead. Actual packet + * output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause + * fragmentation anyway. + * + * FIXME: copy frag 0 iph to qp->iph + * + * 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/errno.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 <linux/etherdevice.h> + +#include <net/snmp.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/route.h> +#include <net/tcp.h> +#include <net/udp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/arp.h> +#include <net/icmp.h> +#include <net/raw.h> +#include <net/checksum.h> +#include <linux/igmp.h> +#include <linux/ip_fw.h> + +#define CONFIG_IP_DEFRAG + +extern int last_retran; +extern void sort_send(struct sock *sk); + +#define min(a,b) ((a)<(b)?(a):(b)) +#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000)) + +/* + * SNMP management statistics + */ + +#ifdef CONFIG_IP_FORWARD +struct ip_mib ip_statistics={1,64,}; /* Forwarding=Yes, Default TTL=64 */ +#else +struct ip_mib ip_statistics={0,64,}; /* Forwarding=No, Default TTL=64 */ +#endif + +/* + * Handle the issuing of an ioctl() request + * for the ip device. This is scheduled to + * disappear + */ + +int ip_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + switch(cmd) + { + default: + return(-EINVAL); + } +} + + +/* + * Take an skb, and fill in the MAC header. + */ + +static int ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, unsigned long saddr) +{ + int mac = 0; + + skb->dev = dev; + skb->arp = 1; + if (dev->hard_header) + { + /* + * Build a hardware header. Source address is our mac, destination unknown + * (rebuild header will sort this out) + */ + mac = dev->hard_header(skb->data, dev, ETH_P_IP, NULL, NULL, len, skb); + if (mac < 0) + { + mac = -mac; + skb->arp = 0; + skb->raddr = daddr; /* next routing address */ + } + } + return mac; +} + +int ip_id_count = 0; + +/* + * This routine builds the appropriate hardware/IP headers for + * the routine. It assumes that if *dev != NULL then the + * protocol knows what it's doing, otherwise it uses the + * routing/ARP tables to select a device struct. + */ +int ip_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) +{ + struct rtable *rt; + unsigned char *buff; + unsigned long raddr; + int tmp; + unsigned long src; + struct iphdr *iph; + + buff = skb->data; + + /* + * See if we need to look up the device. + */ + +#ifdef CONFIG_INET_MULTICAST + if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name) + *dev=dev_get(skb->sk->ip_mc_name); +#endif + if (*dev == NULL) + { + if(skb->localroute) + rt = ip_rt_local(daddr, NULL, &src); + else + rt = ip_rt_route(daddr, NULL, &src); + if (rt == NULL) + { + ip_statistics.IpOutNoRoutes++; + return(-ENETUNREACH); + } + + *dev = rt->rt_dev; + /* + * If the frame is from us and going off machine it MUST MUST MUST + * have the output device ip address and never the loopback + */ + if (LOOPBACK(saddr) && !LOOPBACK(daddr)) + saddr = src;/*rt->rt_dev->pa_addr;*/ + raddr = rt->rt_gateway; + + } + else + { + /* + * We still need the address of the first hop. + */ + if(skb->localroute) + rt = ip_rt_local(daddr, NULL, &src); + else + rt = ip_rt_route(daddr, NULL, &src); + /* + * If the frame is from us and going off machine it MUST MUST MUST + * have the output device ip address and never the loopback + */ + if (LOOPBACK(saddr) && !LOOPBACK(daddr)) + saddr = src;/*rt->rt_dev->pa_addr;*/ + + raddr = (rt == NULL) ? 0 : rt->rt_gateway; + } + + /* + * No source addr so make it our addr + */ + if (saddr == 0) + saddr = src; + + /* + * No gateway so aim at the real destination + */ + if (raddr == 0) + raddr = daddr; + + /* + * Now build the MAC header. + */ + + tmp = ip_send(skb, raddr, len, *dev, saddr); + buff += tmp; + len -= tmp; + + /* + * Book keeping + */ + + skb->dev = *dev; + skb->saddr = saddr; + if (skb->sk) + skb->sk->saddr = saddr; + + /* + * Now build the IP header. + */ + + /* + * If we are using IPPROTO_RAW, then we don't need an IP header, since + * one is being supplied to us by the user + */ + + if(type == IPPROTO_RAW) + return (tmp); + + /* + * Build the IP addresses + */ + + iph=(struct iphdr *)buff; + + iph->version = 4; + iph->tos = tos; + iph->frag_off = 0; + iph->ttl = ttl; + iph->daddr = daddr; + iph->saddr = saddr; + iph->protocol = type; + iph->ihl = 5; + skb->ip_hdr = iph; + + return(20 + tmp); /* IP header plus MAC header size */ +} + + +/* + * Generate a checksum for an outgoing IP datagram. + */ + +void ip_send_check(struct iphdr *iph) +{ + iph->check = 0; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); +} + +/************************ Fragment Handlers From NET2E **********************************/ + + +/* + * This fragment handler is a bit of a heap. On the other hand it works quite + * happily and handles things quite well. + */ + +static struct ipq *ipqueue = NULL; /* IP fragment queue */ + +/* + * Create a new fragment entry. + */ + +static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr) +{ + struct ipfrag *fp; + + fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC); + if (fp == NULL) + { + NETDEBUG(printk("IP: frag_create: no memory left !\n")); + return(NULL); + } + memset(fp, 0, sizeof(struct ipfrag)); + + /* Fill in the structure. */ + fp->offset = offset; + fp->end = end; + fp->len = end - offset; + fp->skb = skb; + fp->ptr = ptr; + + return(fp); +} + + +/* + * Find the correct entry in the "incomplete datagrams" queue for + * this IP datagram, and return the queue entry address if found. + */ + +static struct ipq *ip_find(struct iphdr *iph) +{ + struct ipq *qp; + struct ipq *qplast; + + cli(); + qplast = NULL; + for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next) + { + if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr && + iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol) + { + del_timer(&qp->timer); /* So it doesn't vanish on us. The timer will be reset anyway */ + sti(); + return(qp); + } + } + sti(); + return(NULL); +} + + +/* + * Remove an entry from the "incomplete datagrams" queue, either + * because we completed, reassembled and processed it, or because + * it timed out. + */ + +static void ip_free(struct ipq *qp) +{ + struct ipfrag *fp; + struct ipfrag *xp; + + /* + * Stop the timer for this entry. + */ + + del_timer(&qp->timer); + + /* Remove this entry from the "incomplete datagrams" queue. */ + cli(); + if (qp->prev == NULL) + { + ipqueue = qp->next; + if (ipqueue != NULL) + ipqueue->prev = NULL; + } + else + { + qp->prev->next = qp->next; + if (qp->next != NULL) + qp->next->prev = qp->prev; + } + + /* Release all fragment data. */ + + fp = qp->fragments; + while (fp != NULL) + { + xp = fp->next; + IS_SKB(fp->skb); + kfree_skb(fp->skb,FREE_READ); + kfree_s(fp, sizeof(struct ipfrag)); + fp = xp; + } + + /* Release the MAC header. */ + kfree_s(qp->mac, qp->maclen); + + /* Release the IP header. */ + kfree_s(qp->iph, 64 + 8); + + /* Finally, release the queue descriptor itself. */ + kfree_s(qp, sizeof(struct ipq)); + sti(); +} + + +/* + * Oops- a fragment queue timed out. Kill it and send an ICMP reply. + */ + +static void ip_expire(unsigned long arg) +{ + struct ipq *qp; + + qp = (struct ipq *)arg; + + /* + * Send an ICMP "Fragment Reassembly Timeout" message. + */ + + ip_statistics.IpReasmTimeout++; + ip_statistics.IpReasmFails++; + /* This if is always true... shrug */ + if(qp->fragments!=NULL) + icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED, + ICMP_EXC_FRAGTIME, 0, qp->dev); + + /* + * Nuke the fragment queue. + */ + ip_free(qp); +} + + +/* + * Add an entry to the 'ipq' queue for a newly received IP datagram. + * We will (hopefully :-) receive all other fragments of this datagram + * in time, so we just create a queue for this datagram, in which we + * will insert the received fragments at their respective positions. + */ + +static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev) +{ + struct ipq *qp; + int maclen; + int ihlen; + + qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC); + if (qp == NULL) + { + NETDEBUG(printk("IP: create: no memory left !\n")); + return(NULL); + skb->dev = qp->dev; + } + memset(qp, 0, sizeof(struct ipq)); + + /* + * Allocate memory for the MAC header. + * + * FIXME: We have a maximum MAC address size limit and define + * elsewhere. We should use it here and avoid the 3 kmalloc() calls + */ + + maclen = ((unsigned long) iph) - ((unsigned long) skb->data); + qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC); + if (qp->mac == NULL) + { + NETDEBUG(printk("IP: create: no memory left !\n")); + kfree_s(qp, sizeof(struct ipq)); + return(NULL); + } + + /* + * Allocate memory for the IP header (plus 8 octets for ICMP). + */ + + ihlen = (iph->ihl * sizeof(unsigned long)); + qp->iph = (struct iphdr *) kmalloc(64 + 8, GFP_ATOMIC); + if (qp->iph == NULL) + { + NETDEBUG(printk("IP: create: no memory left !\n")); + kfree_s(qp->mac, maclen); + kfree_s(qp, sizeof(struct ipq)); + return(NULL); + } + + /* Fill in the structure. */ + memcpy(qp->mac, skb->data, maclen); + memcpy(qp->iph, iph, ihlen + 8); + qp->len = 0; + qp->ihlen = ihlen; + qp->maclen = maclen; + qp->fragments = NULL; + qp->dev = dev; + + /* Start a timer for this entry. */ + qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ + qp->timer.data = (unsigned long) qp; /* pointer to queue */ + qp->timer.function = ip_expire; /* expire function */ + add_timer(&qp->timer); + + /* Add this entry to the queue. */ + qp->prev = NULL; + cli(); + qp->next = ipqueue; + if (qp->next != NULL) + qp->next->prev = qp; + ipqueue = qp; + sti(); + return(qp); +} + + +/* + * See if a fragment queue is complete. + */ + +static int ip_done(struct ipq *qp) +{ + struct ipfrag *fp; + int offset; + + /* Only possible if we received the final fragment. */ + if (qp->len == 0) + return(0); + + /* Check all fragment offsets to see if they connect. */ + fp = qp->fragments; + offset = 0; + while (fp != NULL) + { + if (fp->offset > offset) + return(0); /* fragment(s) missing */ + offset = fp->end; + fp = fp->next; + } + + /* All fragments are present. */ + return(1); +} + + +/* + * Build a new IP datagram from all its fragments. + * + * FIXME: We copy here because we lack an effective way of handling lists + * of bits on input. Until the new skb data handling is in I'm not going + * to touch this with a bargepole. This also causes a 4Kish limit on + * packet sizes. + */ + +static struct sk_buff *ip_glue(struct ipq *qp) +{ + struct sk_buff *skb; + struct iphdr *iph; + struct ipfrag *fp; + unsigned char *ptr; + int count, len; + + /* + * Allocate a new buffer for the datagram. + */ + + len = qp->maclen + qp->ihlen + qp->len; + + if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) + { + ip_statistics.IpReasmFails++; + NETDEBUG(printk("IP: queue_glue: no memory for gluing queue 0x%X\n", (int) qp)); + ip_free(qp); + return(NULL); + } + + /* Fill in the basic details. */ + skb->len = (len - qp->maclen); + skb->h.raw = skb->data; + skb->free = 1; + + /* Copy the original MAC and IP headers into the new buffer. */ + ptr = (unsigned char *) skb->h.raw; + memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen); + ptr += qp->maclen; + memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen); + ptr += qp->ihlen; + skb->h.raw += qp->maclen; + + count = 0; + + /* Copy the data portions of all fragments into the new buffer. */ + fp = qp->fragments; + while(fp != NULL) + { + if(count+fp->len > skb->len) + { + NETDEBUG(printk("Invalid fragment list: Fragment over size.\n")); + ip_free(qp); + kfree_skb(skb,FREE_WRITE); + ip_statistics.IpReasmFails++; + return NULL; + } + memcpy((ptr + fp->offset), fp->ptr, fp->len); + count += fp->len; + fp = fp->next; + } + + /* We glued together all fragments, so remove the queue entry. */ + ip_free(qp); + + /* Done with all fragments. Fixup the new IP header. */ + iph = skb->h.iph; + iph->frag_off = 0; + iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count); + skb->ip_hdr = iph; + + ip_statistics.IpReasmOKs++; + return(skb); +} + + +/* + * Process an incoming IP datagram fragment. + */ + +static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev) +{ + struct ipfrag *prev, *next, *tmp; + struct ipfrag *tfp; + struct ipq *qp; + struct sk_buff *skb2; + unsigned char *ptr; + int flags, offset; + int i, ihl, end; + + ip_statistics.IpReasmReqds++; + + /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */ + qp = ip_find(iph); + + /* Is this a non-fragmented datagram? */ + offset = ntohs(iph->frag_off); + flags = offset & ~IP_OFFSET; + offset &= IP_OFFSET; + if (((flags & IP_MF) == 0) && (offset == 0)) + { + if (qp != NULL) + ip_free(qp); /* Huh? How could this exist?? */ + return(skb); + } + + offset <<= 3; /* offset is in 8-byte chunks */ + + /* + * If the queue already existed, keep restarting its timer as long + * as we still are receiving fragments. Otherwise, create a fresh + * queue entry. + */ + + if (qp != NULL) + { + del_timer(&qp->timer); + qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ + qp->timer.data = (unsigned long) qp; /* pointer to queue */ + qp->timer.function = ip_expire; /* expire function */ + add_timer(&qp->timer); + } + else + { + /* + * If we failed to create it, then discard the frame + */ + if ((qp = ip_create(skb, iph, dev)) == NULL) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + ip_statistics.IpReasmFails++; + return NULL; + } + } + + /* + * Determine the position of this fragment. + */ + + ihl = (iph->ihl * sizeof(unsigned long)); + end = offset + ntohs(iph->tot_len) - ihl; + + /* + * Point into the IP datagram 'data' part. + */ + + ptr = skb->data + dev->hard_header_len + ihl; + + /* + * Is this the final fragment? + */ + + if ((flags & IP_MF) == 0) + qp->len = end; + + /* + * Find out which fragments are in front and at the back of us + * in the chain of fragments so far. We must know where to put + * this fragment, right? + */ + + prev = NULL; + for(next = qp->fragments; next != NULL; next = next->next) + { + if (next->offset > offset) + break; /* bingo! */ + prev = next; + } + + /* + * We found where to put this one. + * Check for overlap with preceding fragment, and, if needed, + * align things so that any overlaps are eliminated. + */ + if (prev != NULL && offset < prev->end) + { + i = prev->end - offset; + offset += i; /* ptr into datagram */ + ptr += i; /* ptr into fragment data */ + } + + /* + * Look for overlap with succeeding segments. + * If we can merge fragments, do it. + */ + + for(tmp=next; tmp != NULL; tmp = tfp) + { + tfp = tmp->next; + if (tmp->offset >= end) + break; /* no overlaps at all */ + + i = end - next->offset; /* overlap is 'i' bytes */ + tmp->len -= i; /* so reduce size of */ + tmp->offset += i; /* next fragment */ + tmp->ptr += i; + /* + * If we get a frag size of <= 0, remove it and the packet + * that it goes with. + */ + if (tmp->len <= 0) + { + if (tmp->prev != NULL) + tmp->prev->next = tmp->next; + else + qp->fragments = tmp->next; + + if (tfp->next != NULL) + tmp->next->prev = tmp->prev; + + next=tfp; /* We have killed the original next frame */ + + kfree_skb(tmp->skb,FREE_READ); + kfree_s(tmp, sizeof(struct ipfrag)); + } + } + + /* + * Insert this fragment in the chain of fragments. + */ + + tfp = NULL; + tfp = ip_frag_create(offset, end, skb, ptr); + + /* + * No memory to save the fragment - so throw the lot + */ + + if (!tfp) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return NULL; + } + tfp->prev = prev; + tfp->next = next; + if (prev != NULL) + prev->next = tfp; + else + qp->fragments = tfp; + + if (next != NULL) + next->prev = tfp; + + /* + * OK, so we inserted this new fragment into the chain. + * Check if we now have a full IP datagram which we can + * bump up to the IP layer... + */ + + if (ip_done(qp)) + { + skb2 = ip_glue(qp); /* glue together the fragments */ + return(skb2); + } + return(NULL); +} + + +/* + * This IP datagram is too large to be sent in one piece. Break it up into + * smaller pieces (each of size equal to the MAC header plus IP header plus + * a block of the data of the original IP data part) that will yet fit in a + * single device frame, and queue such a frame for sending by calling the + * ip_queue_xmit(). Note that this is recursion, and bad things will happen + * if this function causes a loop... + * + * Yes this is inefficient, feel free to submit a quicker one. + * + * **Protocol Violation** + * We copy all the options to each fragment. !FIXME! + */ +void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag) +{ + struct iphdr *iph; + unsigned char *raw; + unsigned char *ptr; + struct sk_buff *skb2; + int left, mtu, hlen, len; + int offset; + unsigned long flags; + + /* + * Point into the IP datagram header. + */ + + raw = skb->data; + iph = (struct iphdr *) (raw + dev->hard_header_len); + + skb->ip_hdr = iph; + + /* + * Setup starting values. + */ + + hlen = (iph->ihl * sizeof(unsigned long)); + left = ntohs(iph->tot_len) - hlen; /* Space per frame */ + hlen += dev->hard_header_len; /* Total header size */ + mtu = (dev->mtu - hlen); /* Size of data space */ + ptr = (raw + hlen); /* Where to start from */ + + /* + * Check for any "DF" flag. [DF means do not fragment] + */ + + if (ntohs(iph->frag_off) & IP_DF) + { + /* + * Reply giving the MTU of the failed hop. + */ + ip_statistics.IpFragFails++; + icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev); + return; + } + + /* + * The protocol doesn't seem to say what to do in the case that the + * frame + options doesn't fit the mtu. As it used to fall down dead + * in this case we were fortunate it didn't happen + */ + + if(mtu<8) + { + /* It's wrong but it's better than nothing */ + icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev); + ip_statistics.IpFragFails++; + return; + } + + /* + * Fragment the datagram. + */ + + /* + * The initial offset is 0 for a complete frame. When + * fragmenting fragments it's wherever this one starts. + */ + + if (is_frag & 2) + offset = (ntohs(iph->frag_off) & 0x1fff) << 3; + else + offset = 0; + + + /* + * Keep copying data until we run out. + */ + + while(left > 0) + { + len = left; + /* IF: it doesn't fit, use 'mtu' - the data space left */ + if (len > mtu) + len = mtu; + /* IF: we are not sending upto and including the packet end + then align the next start on an eight byte boundary */ + if (len < left) + { + len/=8; + len*=8; + } + /* + * Allocate buffer. + */ + + if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL) + { + NETDEBUG(printk("IP: frag: no memory for new fragment!\n")); + ip_statistics.IpFragFails++; + return; + } + + /* + * Set up data on packet + */ + + skb2->arp = skb->arp; + if(skb->free==0) + printk("IP fragmenter: BUG free!=1 in fragmenter\n"); + skb2->free = 1; + skb2->len = len + hlen; + skb2->h.raw=(char *) skb2->data; + /* + * Charge the memory for the fragment to any owner + * it might possess + */ + + save_flags(flags); + if (sk) + { + cli(); + sk->wmem_alloc += skb2->mem_len; + skb2->sk=sk; + } + restore_flags(flags); + skb2->raddr = skb->raddr; /* For rebuild_header - must be here */ + + /* + * Copy the packet header into the new buffer. + */ + + memcpy(skb2->h.raw, raw, hlen); + + /* + * Copy a block of the IP datagram. + */ + memcpy(skb2->h.raw + hlen, ptr, len); + left -= len; + + skb2->h.raw+=dev->hard_header_len; + + /* + * Fill in the new header fields. + */ + iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/); + iph->frag_off = htons((offset >> 3)); + /* + * Added AC : If we are fragmenting a fragment thats not the + * last fragment then keep MF on each bit + */ + if (left > 0 || (is_frag & 1)) + iph->frag_off |= htons(IP_MF); + ptr += len; + offset += len; + + /* + * Put this fragment into the sending queue. + */ + + ip_statistics.IpFragCreates++; + + ip_queue_xmit(sk, dev, skb2, 2); + } + ip_statistics.IpFragOKs++; +} + + + +#ifdef CONFIG_IP_FORWARD + +/* + * Forward an IP datagram to its next destination. + */ + +void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, unsigned long target_addr, int target_strict) +{ + struct device *dev2; /* Output device */ + struct iphdr *iph; /* Our header */ + struct sk_buff *skb2; /* Output packet */ + struct rtable *rt; /* Route we use */ + unsigned char *ptr; /* Data pointer */ + unsigned long raddr; /* Router IP address */ +#ifdef CONFIG_IP_FIREWALL + int fw_res = 0; /* Forwarding result */ + + /* + * See if we are allowed to forward this. + * Note: demasqueraded fragments are always 'back'warded. + */ + + + if(!(is_frag&4) && (fw_res=ip_fw_chk(skb->h.iph, dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0))!=1) + { + if(fw_res==-1) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev); + return; + } +#endif + /* + * According to the RFC, we must first decrease the TTL field. If + * that reaches zero, we must reply an ICMP control message telling + * that the packet's lifetime expired. + * + * Exception: + * We may not generate an ICMP for an ICMP. icmp_send does the + * enforcement of this so we can forget it here. It is however + * sometimes VERY important. + */ + + iph = skb->h.iph; + iph->ttl--; + + /* + * Re-compute the IP header checksum. + * This is inefficient. We know what has happened to the header + * and could thus adjust the checksum as Phil Karn does in KA9Q + */ + + iph->check = ntohs(iph->check) + 0x0100; + if ((iph->check & 0xFF00) == 0) + iph->check++; /* carry overflow */ + iph->check = htons(iph->check); + + if (iph->ttl <= 0) + { + /* Tell the sender its packet died... */ + icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev); + return; + } + + /* + * OK, the packet is still valid. Fetch its destination address, + * and give it to the IP sender for further processing. + */ + + rt = ip_rt_route(target_addr, NULL, NULL); + if (rt == NULL) + { + /* + * Tell the sender its packet cannot be delivered. Again + * ICMP is screened later. + */ + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev); + return; + } + + + /* + * Gosh. Not only is the packet valid; we even know how to + * forward it onto its final destination. Can we say this + * is being plain lucky? + * If the router told us that there is no GW, use the dest. + * IP address itself- we seem to be connected directly... + */ + + raddr = rt->rt_gateway; + + if (raddr != 0) + { + /* + * Strict routing permits no gatewaying + */ + + if(target_strict) + { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev); + kfree_skb(skb, FREE_READ); + return; + } + + /* + * There is a gateway so find the correct route for it. + * Gateways cannot in turn be gatewayed. + */ + + rt = ip_rt_route(raddr, NULL, NULL); + if (rt == NULL) + { + /* + * Tell the sender its packet cannot be delivered... + */ + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev); + return; + } + if (rt->rt_gateway != 0) + raddr = rt->rt_gateway; + } + else + raddr = target_addr; + + /* + * Having picked a route we can now send the frame out. + */ + + dev2 = rt->rt_dev; + + /* + * In IP you never have to forward a frame on the interface that it + * arrived upon. We now generate an ICMP HOST REDIRECT giving the route + * we calculated. + */ +#ifndef CONFIG_IP_NO_ICMP_REDIRECT + if (dev == dev2 && !((iph->saddr^iph->daddr)&dev->pa_mask) && rt->rt_flags&RTF_MODIFIED) + icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev); +#endif + + /* + * We now allocate a new buffer, and copy the datagram into it. + * If the indicated interface is up and running, kick it. + */ + + if (dev2->flags & IFF_UP) + { +#ifdef CONFIG_IP_MASQUERADE + /* + * If this fragment needs masquerading, make it so... + * (Dont masquerade de-masqueraded fragments) + */ + if (!(is_frag&4) && fw_res==2) + ip_fw_masquerade(&skb, dev2); +#endif + + /* + * Current design decrees we copy the packet. For identical header + * lengths we could avoid it. The new skb code will let us push + * data so the problem goes away then. + */ + + skb2 = alloc_skb(dev2->hard_header_len + skb->len, GFP_ATOMIC); + /* + * This is rare and since IP is tolerant of network failures + * quite harmless. + */ + if (skb2 == NULL) + { + NETDEBUG(printk("\nIP: No memory available for IP forward\n")); + return; + } + ptr = skb2->data; + skb2->free = 1; + skb2->len = skb->len + dev2->hard_header_len; + skb2->h.raw = ptr; + + /* + * Copy the packet data into the new buffer. + */ + memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len); + + /* Now build the MAC header. */ + (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr); + + ip_statistics.IpForwDatagrams++; + + /* + * See if it needs fragmenting. Note in ip_rcv we tagged + * the fragment type. This must be right so that + * the fragmenter does the right thing. + */ + + if(skb2->len > dev2->mtu + dev2->hard_header_len) + { + ip_fragment(NULL,skb2,dev2, is_frag); + kfree_skb(skb2,FREE_WRITE); + } + else + { +#ifdef CONFIG_IP_ACCT + /* + * Count mapping we shortcut + */ + + ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1); +#endif + + /* + * Map service types to priority. We lie about + * throughput being low priority, but it's a good + * choice to help improve general usage. + */ + if(iph->tos & IPTOS_LOWDELAY) + dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE); + else if(iph->tos & IPTOS_THROUGHPUT) + dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND); + else + dev_queue_xmit(skb2, dev2, SOPRI_NORMAL); + } + } +} + + +#endif + +/* + * This function receives all incoming IP datagrams. + */ + +int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ + struct iphdr *iph = skb->h.iph; + struct sock *raw_sk=NULL; + unsigned char hash; + unsigned char flag = 0; + struct inet_protocol *ipprot; + int brd=IS_MYADDR; + unsigned long target_addr; + int target_strict=0; + int is_frag=0; +#ifdef CONFIG_IP_FIREWALL + int err; +#endif + + ip_statistics.IpInReceives++; + + /* + * Tag the ip header of this packet so we can find it + */ + + skb->ip_hdr = iph; + + /* + * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. + * RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING]. + * + * Is the datagram acceptable? + * + * 1. Length at least the size of an ip header + * 2. Version of 4 + * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] + * 4. Doesn't have a bogus length + * (5. We ought to check for IP multicast addresses and undefined types.. does this matter ?) + */ + + if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0 + || skb->len < ntohs(iph->tot_len)) + { + ip_statistics.IpInHdrErrors++; + kfree_skb(skb, FREE_WRITE); + return(0); + } + + /* + * Our transport medium may have padded the buffer out. Now we know it + * is IP we can trim to the true length of the frame. + */ + + skb->len=ntohs(iph->tot_len); + + /* + * See if the firewall wants to dispose of the packet. + */ + +#ifdef CONFIG_IP_FIREWALL + + if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))<1) + { + if(err==-1) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev); + kfree_skb(skb, FREE_WRITE); + return 0; + } + +#endif + + + /* + * Next analyse the packet for options. Studies show under one packet in + * a thousand have options.... + */ + + target_addr = iph->daddr; + + if (iph->ihl != 5) + { + /* Humph.. options. Lots of annoying fiddly bits */ + + /* + * This is straight from the RFC. It might even be right ;) + * + * RFC 1122: 3.2.1.8 STREAMID option is obsolete and MUST be ignored. + * RFC 1122: 3.2.1.8 MUST NOT crash on a zero length option. + * RFC 1122: 3.2.1.8 MUST support acting as final destination of a source route. + */ + + int opt_space=4*(iph->ihl-5); + int opt_size; + unsigned char *opt_ptr=skb->h.raw+sizeof(struct iphdr); + + while(opt_space>0) + { + if(*opt_ptr==IPOPT_NOOP) + { + opt_ptr++; + opt_space--; + continue; + } + if(*opt_ptr==IPOPT_END) + break; /* Done */ + if(opt_space<2 || (opt_size=opt_ptr[1])<2 || opt_ptr[1]>opt_space) + { + /* + * RFC 1122: 3.2.2.5 SHOULD send parameter problem reports. + */ + icmp_send(skb, ICMP_PARAMETERPROB, 0, 0, skb->dev); + kfree_skb(skb, FREE_READ); + return -EINVAL; + } + switch(opt_ptr[0]) + { + case IPOPT_SEC: + /* Should we drop this ?? */ + break; + case IPOPT_SSRR: /* These work almost the same way */ + target_strict=1; + /* Fall through */ + case IPOPT_LSRR: +#ifdef CONFIG_IP_NOSR + kfree_skb(skb, FREE_READ); + return -EINVAL; +#endif + case IPOPT_RR: + /* + * RFC 1122: 3.2.1.8 Support for RR is OPTIONAL. + */ + if (iph->daddr!=skb->dev->pa_addr && (brd = ip_chk_addr(iph->daddr)) == 0) + break; + if((opt_size<3) || ( opt_ptr[0]==IPOPT_RR && opt_ptr[2] > opt_size-4 )) + { + if(ip_chk_addr(iph->daddr)) + icmp_send(skb, ICMP_PARAMETERPROB, 0, 0, skb->dev); + kfree_skb(skb, FREE_READ); + return -EINVAL; + } + if(opt_ptr[2] > opt_size-4 ) + break; + /* Bytes are [IPOPT_xxRR][Length][EntryPointer][Entry0][Entry1].... */ + /* This isn't going to be too portable - FIXME */ + if(opt_ptr[0]!=IPOPT_RR) + { + int t; + target_addr=*(long *)(&opt_ptr[opt_ptr[2]]); /* Get hop */ + t=ip_chk_addr(target_addr); + if(t==IS_MULTICAST||t==IS_BROADCAST) + { + if(ip_chk_addr(iph->daddr)) + icmp_send(skb, ICMP_PARAMETERPROB, 0, 0, skb->dev); + kfree_skb(skb,FREE_READ); + return -EINVAL; + } + } + *(long *)(&opt_ptr[opt_ptr[2]])=skb->dev->pa_addr; /* Record hop */ + break; + case IPOPT_TIMESTAMP: + /* + * RFC 1122: 3.2.1.8 The timestamp option is OPTIONAL but if implemented + * MUST meet various rules (read the spec). + */ + NETDEBUG(printk("ICMP: Someone finish the timestamp routine ;)\n")); + break; + default: + break; + } + opt_ptr+=opt_size; + opt_space-=opt_size; + } + + } + + + /* + * Remember if the frame is fragmented. + */ + + if(iph->frag_off) + { + if (iph->frag_off & 0x0020) + is_frag|=1; + /* + * Last fragment ? + */ + + if (ntohs(iph->frag_off) & 0x1fff) + is_frag|=2; + } + + /* + * Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. + * + * This is inefficient. While finding out if it is for us we could also compute + * the routing table entry. This is where the great unified cache theory comes + * in as and when someone implements it + * + * For most hosts over 99% of packets match the first conditional + * and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at + * function entry. + */ + + if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0) + { +#ifdef CONFIG_IP_MULTICAST + + if(brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK)) + { + /* + * Check it is for one of our groups + */ + struct ip_mc_list *ip_mc=dev->ip_mc_list; + do + { + if(ip_mc==NULL) + { + kfree_skb(skb, FREE_WRITE); + return 0; + } + if(ip_mc->multiaddr==iph->daddr) + break; + ip_mc=ip_mc->next; + } + while(1); + } +#endif + +#ifdef CONFIG_IP_MASQUERADE + /* + * Do we need to de-masquerade this fragment? + */ + if (ip_fw_demasquerade(skb)) + { + struct iphdr *iph=skb->h.iph; + ip_forward(skb, dev, is_frag|4, iph->daddr, 0); + kfree_skb(skb, FREE_WRITE); + return(0); + } +#endif + + /* + * Account for the packet + */ + +#ifdef CONFIG_IP_ACCT + ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1); +#endif + + /* + * Reassemble IP fragments. + */ + + if(is_frag) + { + /* Defragment. Obtain the complete packet if there is one */ + skb=ip_defrag(iph,skb,dev); + if(skb==NULL) + return 0; + skb->dev = dev; + iph=skb->h.iph; + } + + /* + * Point into the IP datagram, just past the header. + */ + + skb->ip_hdr = iph; + skb->h.raw += iph->ihl*4; + + /* + * Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies. + * + * RFC 1122: SHOULD pass TOS value up to the transport layer. + */ + + hash = iph->protocol & (SOCK_ARRAY_SIZE-1); + + /* + * If there maybe a raw socket we must check - if not we don't care less + */ + + if((raw_sk=raw_prot.sock_array[hash])!=NULL) + { + struct sock *sknext=NULL; + struct sk_buff *skb1; + raw_sk=get_sock_raw(raw_sk, hash, iph->saddr, iph->daddr); + if(raw_sk) /* Any raw sockets */ + { + do + { + /* Find the next */ + sknext=get_sock_raw(raw_sk->next, hash, iph->saddr, iph->daddr); + if(sknext) + skb1=skb_clone(skb, GFP_ATOMIC); + else + break; /* One pending raw socket left */ + if(skb1) + raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr); + raw_sk=sknext; + } + while(raw_sk!=NULL); + + /* + * Here either raw_sk is the last raw socket, or NULL if none + */ + + /* + * We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy + */ + } + } + + /* + * skb->h.raw now points at the protocol beyond the IP header. + */ + + hash = iph->protocol & (MAX_INET_PROTOS -1); + for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next) + { + struct sk_buff *skb2; + + if (ipprot->protocol != iph->protocol) + continue; + /* + * See if we need to make a copy of it. This will + * only be set if more than one protocol wants it. + * and then not for the last one. If there is a pending + * raw delivery wait for that + */ + + if (ipprot->copy || raw_sk) + { + skb2 = skb_clone(skb, GFP_ATOMIC); + if(skb2==NULL) + continue; + } + else + { + skb2 = skb; + } + flag = 1; + + /* + * Pass on the datagram to each protocol that wants it, + * based on the datagram protocol. We should really + * check the protocol handler's return values here... + */ + + ipprot->handler(skb2, dev, NULL, iph->daddr, + (ntohs(iph->tot_len) - (iph->ihl * 4)), + iph->saddr, 0, ipprot); + + } + + /* + * All protocols checked. + * If this packet was a broadcast, we may *not* reply to it, since that + * causes (proven, grin) ARP storms and a leakage of memory (i.e. all + * ICMP reply messages get queued up for transmission...) + */ + + if(raw_sk!=NULL) /* Shift to last raw user */ + raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr); + else if (!flag) /* Free and report errors */ + { + if (brd != IS_BROADCAST && brd!=IS_MULTICAST) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev); + kfree_skb(skb, FREE_WRITE); + } + + return(0); + } + + /* + * Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. + * + * This is inefficient. While finding out if it is for us we could also compute + * the routing table entry. This is where the great unified cache theory comes + * in as and when someone implements it + * + * For most hosts over 99% of packets match the first conditional + * and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at + * function entry. + */ + + /* + * Don't forward multicast or broadcast frames. + */ + + if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST) + { + kfree_skb(skb,FREE_WRITE); + return 0; + } + + /* + * The packet is for another target. Forward the frame + */ + +#ifdef CONFIG_IP_FORWARD + ip_forward(skb, dev, is_frag, target_addr, target_strict); +#else +/* printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n", + iph->saddr,iph->daddr);*/ + ip_statistics.IpInAddrErrors++; +#endif + /* + * The forwarder is inefficient and copies the packet. We + * free the original now. + */ + + kfree_skb(skb, FREE_WRITE); + return(0); +} + + +/* + * Loop a packet back to the sender. + */ + +static void ip_loopback(struct device *old_dev, struct sk_buff *skb) +{ + extern struct device loopback_dev; + struct device *dev=&loopback_dev; + int len=skb->len-old_dev->hard_header_len; + struct sk_buff *newskb=alloc_skb(len+dev->hard_header_len, GFP_ATOMIC); + + if(newskb==NULL) + return; + + newskb->link3=NULL; + newskb->sk=NULL; + newskb->dev=dev; + newskb->saddr=skb->saddr; + newskb->daddr=skb->daddr; + newskb->raddr=skb->raddr; + newskb->free=1; + newskb->lock=0; + newskb->users=0; + newskb->pkt_type=skb->pkt_type; + newskb->len=len+dev->hard_header_len; + + + newskb->ip_hdr=(struct iphdr *)(newskb->data+ip_send(newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr)); + memcpy(newskb->ip_hdr,skb->ip_hdr,len); + + /* Recurse. The device check against IFF_LOOPBACK will stop infinite recursion */ + + /*printk("Loopback output queued [%lX to %lX].\n", newskb->ip_hdr->saddr,newskb->ip_hdr->daddr);*/ + ip_queue_xmit(NULL, dev, newskb, 1); +} + + +/* + * Queues a packet to be sent, and starts the transmitter + * if necessary. if free = 1 then we free the block after + * transmit, otherwise we don't. If free==2 we not only + * free the block but also don't assign a new ip seq number. + * This routine also needs to put in the total length, + * and compute the checksum + */ + +void ip_queue_xmit(struct sock *sk, struct device *dev, + struct sk_buff *skb, int free) +{ + struct iphdr *iph; + unsigned char *ptr; + + /* Sanity check */ + if (dev == NULL) + { + NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n")); + return; + } + + IS_SKB(skb); + + /* + * Do some book-keeping in the packet for later + */ + + + skb->dev = dev; + skb->when = jiffies; + + /* + * Find the IP header and set the length. This is bad + * but once we get the skb data handling code in the + * hardware will push its header sensibly and we will + * set skb->ip_hdr to avoid this mess and the fixed + * header length problem + */ + + ptr = skb->data; + ptr += dev->hard_header_len; + iph = (struct iphdr *)ptr; + skb->ip_hdr = iph; + iph->tot_len = ntohs(skb->len-dev->hard_header_len); + +#ifdef CONFIG_IP_FIREWALL + if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy, 0) != 1) + /* just don't send this packet */ + return; +#endif + + /* + * No reassigning numbers to fragments... + */ + + if(free!=2) + iph->id = htons(ip_id_count++); + else + free=1; + + /* All buffers without an owner socket get freed */ + if (sk == NULL) + free = 1; + + skb->free = free; + + /* + * Do we need to fragment. Again this is inefficient. + * We need to somehow lock the original buffer and use + * bits of it. + */ + + if(skb->len > dev->mtu + dev->hard_header_len) + { + ip_fragment(sk,skb,dev,0); + IS_SKB(skb); + kfree_skb(skb,FREE_WRITE); + return; + } + + /* + * Add an IP checksum + */ + + ip_send_check(iph); + + /* + * Print the frame when debugging + */ + + /* + * More debugging. You cannot queue a packet already on a list + * Spot this and moan loudly. + */ + if (skb->next != NULL) + { + NETDEBUG(printk("ip_queue_xmit: next != NULL\n")); + skb_unlink(skb); + } + + /* + * If a sender wishes the packet to remain unfreed + * we add it to his send queue. This arguably belongs + * in the TCP level since nobody else uses it. BUT + * remember IPng might change all the rules. + */ + + if (!free) + { + unsigned long flags; + /* The socket now has more outstanding blocks */ + + sk->packets_out++; + + /* Protect the list for a moment */ + save_flags(flags); + cli(); + + if (skb->link3 != NULL) + { + NETDEBUG(printk("ip.c: link3 != NULL\n")); + skb->link3 = NULL; + } + if (sk->send_head == NULL) + { + sk->send_tail = skb; + sk->send_head = skb; + } + else + { + sk->send_tail->link3 = skb; + sk->send_tail = skb; + } + /* skb->link3 is NULL */ + + /* Interrupt restore */ + restore_flags(flags); + } + else + /* Remember who owns the buffer */ + skb->sk = sk; + + /* + * If the indicated interface is up and running, send the packet. + */ + + ip_statistics.IpOutRequests++; +#ifdef CONFIG_IP_ACCT + ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1); +#endif + +#ifdef CONFIG_IP_MULTICAST + + /* + * Multicasts are looped back for other local users + */ + + if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK)) + { + if(sk==NULL || sk->ip_mc_loop) + { + if(iph->daddr==IGMP_ALL_HOSTS) + ip_loopback(dev,skb); + else + { + struct ip_mc_list *imc=dev->ip_mc_list; + while(imc!=NULL) + { + if(imc->multiaddr==iph->daddr) + { + ip_loopback(dev,skb); + break; + } + imc=imc->next; + } + } + } + /* Multicasts with ttl 0 must not go beyond the host */ + + if(skb->ip_hdr->ttl==0) + { + kfree_skb(skb, FREE_READ); + return; + } + } +#endif + if((dev->flags&IFF_BROADCAST) && iph->daddr==dev->pa_brdaddr && !(dev->flags&IFF_LOOPBACK)) + ip_loopback(dev,skb); + + if (dev->flags & IFF_UP) + { + /* + * If we have an owner use its priority setting, + * otherwise use NORMAL + */ + + if (sk != NULL) + { + dev_queue_xmit(skb, dev, sk->priority); + } + else + { + dev_queue_xmit(skb, dev, SOPRI_NORMAL); + } + } + else + { + ip_statistics.IpOutDiscards++; + if (free) + kfree_skb(skb, FREE_WRITE); + } +} + + + +#ifdef CONFIG_IP_MULTICAST + +/* + * Write an multicast group list table for the IGMP daemon to + * read. + */ + +int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length) +{ + off_t pos=0, begin=0; + struct ip_mc_list *im; + unsigned long flags; + int len=0; + struct device *dev; + + len=sprintf(buffer,"Device : Count\tGroup Users Timer\n"); + save_flags(flags); + cli(); + + for(dev = dev_base; dev; dev = dev->next) + { + if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)) + { + len+=sprintf(buffer+len,"%-10s: %5d\n", + dev->name, dev->mc_count); + for(im = dev->ip_mc_list; im; im = im->next) + { + len+=sprintf(buffer+len, + "\t\t\t%08lX %5d %d:%08lX\n", + im->multiaddr, im->users, + im->tm_running, im->timer.expires); + pos=begin+len; + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + } + } + restore_flags(flags); + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} + + +#endif +/* + * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on + * an IP socket. + * + * We implement IP_TOS (type of service), IP_TTL (time to live). + * + * Next release we will sort out IP_OPTIONS since for some people are kind of important. + */ + +static struct device *ip_mc_find_devfor(unsigned long addr) +{ + struct device *dev; + for(dev = dev_base; dev; dev = dev->next) + { + if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&& + (dev->pa_addr==addr)) + return dev; + } + + return NULL; +} + +int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) +{ + int val,err; + unsigned char ucval; +#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT) + struct ip_fw tmp_fw; +#endif + if (optval == NULL) + return(-EINVAL); + + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + ucval=get_fs_byte((unsigned char *)optval); + + if(level!=SOL_IP) + return -EOPNOTSUPP; + + switch(optname) + { + case IP_TOS: + if(val<0||val>255) + return -EINVAL; + sk->ip_tos=val; + if(val==IPTOS_LOWDELAY) + sk->priority=SOPRI_INTERACTIVE; + if(val==IPTOS_THROUGHPUT) + sk->priority=SOPRI_BACKGROUND; + return 0; + case IP_TTL: + if(val<1||val>255) + return -EINVAL; + sk->ip_ttl=val; + return 0; +#ifdef CONFIG_IP_MULTICAST + case IP_MULTICAST_TTL: + { + sk->ip_mc_ttl=(int)ucval; + return 0; + } + case IP_MULTICAST_LOOP: + { + if(ucval!=0 && ucval!=1) + return -EINVAL; + sk->ip_mc_loop=(int)ucval; + return 0; + } + case IP_MULTICAST_IF: + { + struct in_addr addr; + struct device *dev=NULL; + + /* + * Check the arguments are allowable + */ + + err=verify_area(VERIFY_READ, optval, sizeof(addr)); + if(err) + return err; + + memcpy_fromfs(&addr,optval,sizeof(addr)); + + + /* + * What address has been requested + */ + + if(addr.s_addr==INADDR_ANY) /* Default */ + { + sk->ip_mc_name[0]=0; + return 0; + } + + /* + * Find the device + */ + + dev=ip_mc_find_devfor(addr.s_addr); + + /* + * Did we find one + */ + + if(dev) + { + strcpy(sk->ip_mc_name,dev->name); + return 0; + } + return -EADDRNOTAVAIL; + } + + case IP_ADD_MEMBERSHIP: + { + +/* + * FIXME: Add/Del membership should have a semaphore protecting them from re-entry + */ + struct ip_mreq mreq; + unsigned long route_src; + struct rtable *rt; + struct device *dev=NULL; + + /* + * Check the arguments. + */ + + err=verify_area(VERIFY_READ, optval, sizeof(mreq)); + if(err) + return err; + + memcpy_fromfs(&mreq,optval,sizeof(mreq)); + + /* + * Get device for use later + */ + + if(mreq.imr_interface.s_addr==INADDR_ANY) + { + /* + * Not set so scan. + */ + if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL) + { + dev=rt->rt_dev; + rt->rt_use--; + } + } + else + { + /* + * Find a suitable device. + */ + + dev=ip_mc_find_devfor(mreq.imr_interface.s_addr); + } + + /* + * No device, no cookies. + */ + + if(!dev) + return -ENODEV; + + /* + * Join group. + */ + + return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr); + } + + case IP_DROP_MEMBERSHIP: + { + struct ip_mreq mreq; + struct rtable *rt; + unsigned long route_src; + struct device *dev=NULL; + + /* + * Check the arguments + */ + + err=verify_area(VERIFY_READ, optval, sizeof(mreq)); + if(err) + return err; + + memcpy_fromfs(&mreq,optval,sizeof(mreq)); + + /* + * Get device for use later + */ + + if(mreq.imr_interface.s_addr==INADDR_ANY) + { + if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL) + { + dev=rt->rt_dev; + rt->rt_use--; + } + } + else + { + + dev=ip_mc_find_devfor(mreq.imr_interface.s_addr); + } + + /* + * Did we find a suitable device. + */ + + if(!dev) + return -ENODEV; + + /* + * Leave group + */ + + return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr); + } +#endif +#ifdef CONFIG_IP_FIREWALL + case IP_FW_ADD_BLK: + case IP_FW_DEL_BLK: + case IP_FW_ADD_FWD: + case IP_FW_DEL_FWD: + case IP_FW_CHK_BLK: + case IP_FW_CHK_FWD: + case IP_FW_FLUSH_BLK: + case IP_FW_FLUSH_FWD: + case IP_FW_ZERO_BLK: + case IP_FW_ZERO_FWD: + case IP_FW_POLICY_BLK: + case IP_FW_POLICY_FWD: + if(!suser()) + return -EPERM; + if(optlen>sizeof(tmp_fw) || optlen<1) + return -EINVAL; + err=verify_area(VERIFY_READ,optval,optlen); + if(err) + return err; + memcpy_fromfs(&tmp_fw,optval,optlen); + err=ip_fw_ctl(optname, &tmp_fw,optlen); + return -err; /* -0 is 0 after all */ + +#endif +#ifdef CONFIG_IP_ACCT + case IP_ACCT_DEL: + case IP_ACCT_ADD: + case IP_ACCT_FLUSH: + case IP_ACCT_ZERO: + if(!suser()) + return -EPERM; + if(optlen>sizeof(tmp_fw) || optlen<1) + return -EINVAL; + err=verify_area(VERIFY_READ,optval,optlen); + if(err) + return err; + memcpy_fromfs(&tmp_fw, optval,optlen); + err=ip_acct_ctl(optname, &tmp_fw,optlen); + return -err; /* -0 is 0 after all */ +#endif + /* IP_OPTIONS and friends go here eventually */ + default: + return(-ENOPROTOOPT); + } +} + +/* + * Get the options. Note for future reference. The GET of IP options gets the + * _received_ ones. The set sets the _sent_ ones. + */ + +int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) +{ + int val,err; +#ifdef CONFIG_IP_MULTICAST + int len; +#endif + + if(level!=SOL_IP) + return -EOPNOTSUPP; + + switch(optname) + { + case IP_TOS: + val=sk->ip_tos; + break; + case IP_TTL: + val=sk->ip_ttl; + break; +#ifdef CONFIG_IP_MULTICAST + case IP_MULTICAST_TTL: + val=sk->ip_mc_ttl; + break; + case IP_MULTICAST_LOOP: + val=sk->ip_mc_loop; + break; + case IP_MULTICAST_IF: + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + len=strlen(sk->ip_mc_name); + err=verify_area(VERIFY_WRITE, optval, len); + if(err) + return err; + put_fs_long(len,(unsigned long *) optlen); + memcpy_tofs((void *)optval,sk->ip_mc_name, len); + return 0; +#endif + 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); +} + +/* + * Build and send a packet, with as little as one copy + * + * Doesn't care much about ip options... option length can be + * different for fragment at 0 and other fragments. + * + * Note that the fragment at the highest offset is sent first, + * so the getfrag routine can fill in the TCP/UDP checksum header + * field in the last fragment it sends... actually it also helps + * the reassemblers, they can put most packets in at the head of + * the fragment queue, and they know the total size in advance. This + * last feature will measurable improve the Linux fragment handler. + * + * The callback has five args, an arbitrary pointer (copy of frag), + * the source IP address (may depend on the routing table), the + * destination adddress (char *), the offset to copy from, and the + * length to be copied. + * + */ + +int ip_build_xmit(struct sock *sk, + void getfrag (void *, + int, + char *, + unsigned int, + unsigned int), + void *frag, + unsigned short int length, + int daddr, + int flags, + int type) +{ + struct rtable *rt; + unsigned int fraglen, maxfraglen, fragheaderlen; + int offset, mf; + unsigned long saddr; + unsigned short id; + struct iphdr *iph; + int local=0; + struct device *dev; + + +#ifdef CONFIG_INET_MULTICAST + if(sk && MULTICAST(daddr) && *sk->ip_mc_name) + { + dev=dev_get(skb->ip_mc_name); + if(!dev) + return -ENODEV; + rt=NULL; + } + else + { +#endif + /* + * Perform the IP routing decisions + */ + + if(sk->localroute || flags&MSG_DONTROUTE) + local=1; + + rt = sk->ip_route_cache; + + /* + * See if the routing cache is outdated. We need to clean this up once we are happy it is reliable + * by doing the invalidation actively in the route change and header change. + */ + + saddr=sk->ip_route_saddr; + if(!rt || sk->ip_route_stamp != rt_stamp || daddr!=sk->ip_route_daddr || sk->ip_route_local!=local || sk->saddr!=sk->ip_route_saddr) + { + if(local) + rt = ip_rt_local(daddr, NULL, &saddr); + else + rt = ip_rt_route(daddr, NULL, &saddr); + sk->ip_route_local=local; + sk->ip_route_daddr=daddr; + sk->ip_route_saddr=saddr; + sk->ip_route_stamp=rt_stamp; + sk->ip_route_cache=rt; + sk->ip_hcache_ver=NULL; + sk->ip_hcache_state= 0; + } + else if(rt) + { + /* + * Attempt header caches only if the cached route is being reused. Header cache + * is not ultra cheap to set up. This means we only set it up on the second packet, + * so one shot communications are not slowed. We assume (seems reasonable) that 2 is + * probably going to be a stream of data. + */ + if(rt->rt_dev->header_cache && sk->ip_hcache_state!= -1) + { + if(sk->ip_hcache_ver==NULL || sk->ip_hcache_stamp!=*sk->ip_hcache_ver) + rt->rt_dev->header_cache(rt->rt_dev,sk,saddr,daddr); + else + /* Can't cache. Remember this */ + sk->ip_hcache_state= -1; + } + } + + if (rt == NULL) + { + ip_statistics.IpOutNoRoutes++; + return(-ENETUNREACH); + } + + if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr))) + saddr = sk->saddr; + + dev=rt->rt_dev; +#ifdef CONFIG_INET_MULTICAST + } +#endif + + /* + * Now compute the buffer space we require + */ + + fragheaderlen = dev->hard_header_len; + if(type != IPPROTO_RAW) + fragheaderlen += 20; + + /* + * Fragheaderlen is the size of 'overhead' on each buffer. Now work + * out the size of the frames to send. + */ + + maxfraglen = ((dev->mtu-20) & ~7) + fragheaderlen; + + /* + * Start at the end of the frame by handling the remainder. + */ + + offset = length - (length % (maxfraglen - fragheaderlen)); + + /* + * Amount of memory to allocate for final fragment. + */ + + fraglen = length - offset + fragheaderlen; + + if(fraglen==0) + { + fraglen = maxfraglen; + offset -= maxfraglen-fragheaderlen; + } + + + /* + * The last fragment will not have MF (more fragments) set. + */ + + mf = 0; + + /* + * Can't fragment raw packets + */ + + if (type == IPPROTO_RAW && offset > 0) + return(-EMSGSIZE); + + /* + * Get an identifier + */ + + id = htons(ip_id_count++); + + /* + * Being outputting the bytes. + */ + + do + { + struct sk_buff * skb; + int error; + char *data; + + /* + * Get the memory we require. + */ + + skb = sock_alloc_send_skb(sk, fraglen, 0, &error); + if (skb == NULL) + return(error); + + /* + * Fill in the control structures + */ + + skb->next = skb->prev = NULL; + skb->dev = dev; + skb->when = jiffies; + skb->free = 1; /* dubious, this one */ + skb->sk = sk; + skb->arp = 0; + skb->saddr = saddr; + skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr; + skb->len = fraglen; + + /* + * Save us ARP and stuff. In the optimal case we do no route lookup (route cache ok) + * no ARP lookup (arp cache ok) and output. The cache checks are still too slow but + * this can be fixed later. For gateway routes we ought to have a rt->.. header cache + * pointer to speed header cache builds for identical targets. + */ + + if(sk->ip_hcache_state>0) + { + memcpy(skb->data,sk->ip_hcache_data, dev->hard_header_len); + skb->arp=1; + } + else if (dev->hard_header) + { + if(dev->hard_header(skb->data, dev, ETH_P_IP, + NULL, NULL, 0, NULL)>0) + skb->arp=1; + } + + /* + * Find where to start putting bytes. + */ + + data = (char *)skb->data + dev->hard_header_len; + iph = (struct iphdr *)data; + + /* + * Only write IP header onto non-raw packets + */ + + if(type != IPPROTO_RAW) + { + + iph->version = 4; + iph->ihl = 5; /* ugh */ + iph->tos = sk->ip_tos; + iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4); + iph->id = id; + iph->frag_off = htons(offset>>3); + iph->frag_off |= mf; +#ifdef CONFIG_IP_MULTICAST + if (MULTICAST(daddr)) + iph->ttl = sk->ip_mc_ttl; + else +#endif + iph->ttl = sk->ip_ttl; + iph->protocol = type; + iph->check = 0; + iph->saddr = saddr; + iph->daddr = daddr; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + data += iph->ihl*4; + + /* + * Any further fragments will have MF set. + */ + + mf = htons(IP_MF); + } + + /* + * User data callback + */ + + getfrag(frag, saddr, data, offset, fraglen-fragheaderlen); + + /* + * Account for the fragment. + */ + +#ifdef CONFIG_IP_ACCT + if(!offset) + ip_fw_chk(iph, dev, ip_acct_chain, IP_FW_F_ACCEPT, 1); +#endif + offset -= (maxfraglen-fragheaderlen); + fraglen = maxfraglen; + +#ifdef CONFIG_IP_MULTICAST + + /* + * Multicasts are looped back for other local users + */ + + if (MULTICAST(daddr) && !(dev->flags&IFF_LOOPBACK)) + { + /* + * Loop back any frames. The check for IGMP_ALL_HOSTS is because + * you are always magically a member of this group. + */ + + if(sk==NULL || sk->ip_mc_loop) + { + if(skb->daddr==IGMP_ALL_HOSTS) + ip_loopback(rt->rt_dev,skb); + else + { + struct ip_mc_list *imc=rt->rt_dev->ip_mc_list; + while(imc!=NULL) + { + if(imc->multiaddr==daddr) + { + ip_loopback(rt->rt_dev,skb); + break; + } + imc=imc->next; + } + } + } + + /* + * Multicasts with ttl 0 must not go beyond the host. Fixme: avoid the + * extra clone. + */ + + if(skb->ip_hdr->ttl==0) + kfree_skb(skb, FREE_READ); + } +#endif + /* + * Now queue the bytes into the device. + */ + + if (dev->flags & IFF_UP) + { + dev_queue_xmit(skb, dev, sk->priority); + } + else + { + /* + * Whoops... + * + * FIXME: There is a small nasty here. During the ip_build_xmit we could + * page fault between the route lookup and device send, the device might be + * removed and unloaded.... We need to add device locks on this. + */ + + ip_statistics.IpOutDiscards++; + kfree_skb(skb, FREE_WRITE); + return(0); /* lose rest of fragments */ + } + } + while (offset >= 0); + + return(0); +} + + +/* + * IP protocol layer initialiser + */ + +static struct packet_type ip_packet_type = +{ + 0, /* MUTTER ntohs(ETH_P_IP),*/ + NULL, /* All devices */ + ip_rcv, + NULL, + NULL, +}; + +/* + * Device notifier + */ + +static int ip_rt_event(unsigned long event, void *ptr) +{ + if(event==NETDEV_DOWN) + ip_rt_flush(ptr); + return NOTIFY_DONE; +} + +struct notifier_block ip_rt_notifier={ + ip_rt_event, + NULL, + 0 +}; + +/* + * IP registers the packet type and then calls the subprotocol initialisers + */ + +void ip_init(void) +{ + ip_packet_type.type=htons(ETH_P_IP); + dev_add_pack(&ip_packet_type); + + /* So we flush routes when a device is downed */ + register_netdevice_notifier(&ip_rt_notifier); +/* ip_raw_init(); + ip_packet_init(); + ip_tcp_init(); + ip_udp_init();*/ +} + diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c new file mode 100644 index 000000000..b2e901926 --- /dev/null +++ b/net/ipv4/ip_fw.c @@ -0,0 +1,1514 @@ +/* + * IP firewalling code. This is taken from 4.4BSD. Please note the + * copyright message below. As per the GPL it must be maintained + * and the licenses thus do not conflict. While this port is subject + * to the GPL I also place my modifications under the original + * license in recognition of the original copyright. + * -- Alan Cox. + * + * Ported from BSD to Linux, + * Alan Cox 22/Nov/1994. + * Zeroing /proc and other additions + * Jos Vos 4/Feb/1995. + * Merged and included the FreeBSD-Current changes at Ugen's request + * (but hey it's a lot cleaner now). Ugen would prefer in some ways + * we waited for his final product but since Linux 1.2.0 is about to + * appear it's not practical - Read: It works, it's not clean but please + * don't consider it to be his standard of finished work. + * Alan Cox 12/Feb/1995 + * Porting bidirectional entries from BSD, fixing accounting issues, + * adding struct ip_fwpkt for checking packets with interface address + * Jos Vos 5/Mar/1995. + * + * Masquerading functionality + * + * Copyright (c) 1994 Pauline Middelink + * + * The pieces which added masquerading functionality are totaly + * my responsibility and have nothing to with the original authors + * copyright or doing. + * + * Parts distributed under GPL. + * + * Fixes: + * Pauline Middelink : Added masquerading. + * Alan Cox : Fixed an error in the merge. + * + * All the real work was done by ..... + * + */ + + +/* + * Copyright (c) 1993 Daniel Boulet + * Copyright (c) 1994 Ugen J.S.Antsilevich + * + * Redistribution and use in source forms, with and without modification, + * are permitted provided that this entire comment appears intact. + * + * Redistribution in binary form may occur without any restrictions. + * Obviously, it would be nice if you gave credit where credit is due + * but requiring it would be too onerous. + * + * This software is provided ``AS IS'' without any warranties of any kind. + */ + +#include <linux/config.h> +#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/errno.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 <linux/icmp.h> +#include <linux/udp.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/route.h> +#include <net/tcp.h> +#include <net/udp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/icmp.h> +#include <linux/ip_fw.h> +#include <net/checksum.h> + +/* + * Implement IP packet firewall + */ + +#ifdef CONFIG_IPFIREWALL_DEBUG +#define dprintf1(a) printk(a) +#define dprintf2(a1,a2) printk(a1,a2) +#define dprintf3(a1,a2,a3) printk(a1,a2,a3) +#define dprintf4(a1,a2,a3,a4) printk(a1,a2,a3,a4) +#else +#define dprintf1(a) +#define dprintf2(a1,a2) +#define dprintf3(a1,a2,a3) +#define dprintf4(a1,a2,a3,a4) +#endif + +#define print_ip(a) printk("%ld.%ld.%ld.%ld",(ntohl(a)>>24)&0xFF,\ + (ntohl(a)>>16)&0xFF,\ + (ntohl(a)>>8)&0xFF,\ + (ntohl(a))&0xFF); + +#ifdef IPFIREWALL_DEBUG +#define dprint_ip(a) print_ip(a) +#else +#define dprint_ip(a) +#endif + +#ifdef CONFIG_IP_FIREWALL +struct ip_fw *ip_fw_fwd_chain; +struct ip_fw *ip_fw_blk_chain; +int ip_fw_blk_policy=IP_FW_F_ACCEPT; +int ip_fw_fwd_policy=IP_FW_F_ACCEPT; +#endif +#ifdef CONFIG_IP_ACCT +struct ip_fw *ip_acct_chain; +#endif + +#define IP_INFO_BLK 0 +#define IP_INFO_FWD 1 +#define IP_INFO_ACCT 2 + +#ifdef CONFIG_IP_MASQUERADE +/* + * Implement IP packet masquerading + */ + +static unsigned short masq_port = PORT_MASQ_BEGIN; +static char *strProt[] = {"UDP","TCP"}; +struct ip_masq *ip_msq_hosts; + +#endif + +/* + * Returns 1 if the port is matched by the vector, 0 otherwise + */ + +extern inline int port_match(unsigned short *portptr,int nports,unsigned short port,int range_flag) +{ + if (!nports) + return 1; + if ( range_flag ) + { + if ( portptr[0] <= port && port <= portptr[1] ) + { + return( 1 ); + } + nports -= 2; + portptr += 2; + } + while ( nports-- > 0 ) + { + if ( *portptr++ == port ) + { + return( 1 ); + } + } + return(0); +} + +#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL) + + +/* + * Returns 0 if packet should be dropped, 1 if it should be accepted, + * and -1 if an ICMP host unreachable packet should be sent. + * Also does accounting so you can feed it the accounting chain. + * If opt is set to 1, it means that we do this for accounting + * purposes (searches all entries and handles fragments different). + * If opt is set to 2, it doesn't count a matching packet, which + * is used when calling this for checking purposes (IP_FW_CHK_*). + */ + + +int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int policy, int opt) +{ + struct ip_fw *f; + struct tcphdr *tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl); + struct udphdr *udp=(struct udphdr *)((unsigned long *)ip+ip->ihl); + __u32 src, dst; + __u16 src_port=0, dst_port=0; + unsigned short f_prt=0, prt; + char notcpsyn=1, frag1, match; + unsigned short f_flag; + + /* + * If the chain is empty follow policy. The BSD one + * accepts anything giving you a time window while + * flushing and rebuilding the tables. + */ + + src = ip->saddr; + dst = ip->daddr; + + /* + * This way we handle fragmented packets. + * we ignore all fragments but the first one + * so the whole packet can't be reassembled. + * This way we relay on the full info which + * stored only in first packet. + * + * Note that this theoretically allows partial packet + * spoofing. Not very dangerous but paranoid people may + * wish to play with this. It also allows the so called + * "fragment bomb" denial of service attack on some types + * of system. + */ + + frag1 = ((ntohs(ip->frag_off) & IP_OFFSET) == 0); + if (!frag1 && (opt != 1) && (ip->protocol == IPPROTO_TCP || + ip->protocol == IPPROTO_UDP)) + return(1); + + src = ip->saddr; + dst = ip->daddr; + + /* + * If we got interface from which packet came + * we can use the address directly. This is unlike + * 4.4BSD derived systems that have an address chain + * per device. We have a device per address with dummy + * devices instead. + */ + + dprintf1("Packet "); + switch(ip->protocol) + { + case IPPROTO_TCP: + dprintf1("TCP "); + /* ports stay 0 if it is not the first fragment */ + if (frag1) { + src_port=ntohs(tcp->source); + dst_port=ntohs(tcp->dest); + if(tcp->syn && !tcp->ack) + /* We *DO* have SYN, value FALSE */ + notcpsyn=0; + } + prt=IP_FW_F_TCP; + break; + case IPPROTO_UDP: + dprintf1("UDP "); + /* ports stay 0 if it is not the first fragment */ + if (frag1) { + src_port=ntohs(udp->source); + dst_port=ntohs(udp->dest); + } + prt=IP_FW_F_UDP; + break; + case IPPROTO_ICMP: + dprintf2("ICMP:%d ",((char *)portptr)[0]&0xff); + prt=IP_FW_F_ICMP; + break; + default: + dprintf2("p=%d ",ip->protocol); + prt=IP_FW_F_ALL; + break; + } +#ifdef CONFIG_IP_FIREWALL_DEBUG + dprint_ip(ip->saddr); + + if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) + /* This will print 0 when it is not the first fragment! */ + dprintf2(":%d ", src_port); + dprint_ip(ip->daddr); + if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) + /* This will print 0 when it is not the first fragment! */ + dprintf2(":%d ",dst_port); + dprintf1("\n"); +#endif + + for (f=chain;f;f=f->fw_next) + { + /* + * This is a bit simpler as we don't have to walk + * an interface chain as you do in BSD - same logic + * however. + */ + + /* + * Match can become 0x01 (a "normal" match was found), + * 0x02 (a reverse match was found), and 0x03 (the + * IP addresses match in both directions). + * Now we know in which direction(s) we should look + * for a match for the TCP/UDP ports. Both directions + * might match (e.g., when both addresses are on the + * same network for which an address/mask is given), but + * the ports might only match in one direction. + * This was obviously wrong in the original BSD code. + */ + match = 0x00; + + if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr + && (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) + /* normal direction */ + match |= 0x01; + + if ((f->fw_flg & IP_FW_F_BIDIR) && + (dst&f->fw_smsk.s_addr)==f->fw_src.s_addr + && (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) + /* reverse direction */ + match |= 0x02; + + if (match) + { + /* + * Look for a VIA match + */ + if(f->fw_via.s_addr && rif) + { + if(rif->pa_addr!=f->fw_via.s_addr) + continue; /* Mismatch */ + } + /* + * Drop through - this is a match + */ + } + else + continue; + + /* + * Ok the chain addresses match. + */ + + f_prt=f->fw_flg&IP_FW_F_KIND; + if (f_prt!=IP_FW_F_ALL) + { + /* + * This is actually buggy as if you set SYN flag + * on UDP or ICMP firewall it will never work,but + * actually it is a concern of software which sets + * firewall entries. + */ + + if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn) + continue; + /* + * Specific firewall - packet's protocol + * must match firewall's. + */ + + if(prt!=f_prt) + continue; + + if(!(prt==IP_FW_F_ICMP || ((match & 0x01) && + port_match(&f->fw_pts[0], f->fw_nsp, src_port, + f->fw_flg&IP_FW_F_SRNG) && + port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port, + f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) && + port_match(&f->fw_pts[0], f->fw_nsp, dst_port, + f->fw_flg&IP_FW_F_SRNG) && + port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port, + f->fw_flg&IP_FW_F_DRNG)))) + { + continue; + } + } +#ifdef CONFIG_IP_FIREWALL_VERBOSE + /* + * VERY ugly piece of code which actually + * makes kernel printf for denied packets... + */ + + if (f->fw_flg & IP_FW_F_PRN) + { + if(opt != 1) { + if(f->fw_flg&IP_FW_F_ACCEPT) + printk("Accept "); + else if(f->fw_flg&IP_FW_F_ICMPRPL) + printk("Reject "); + else + printk("Deny "); + } + switch(ip->protocol) + { + case IPPROTO_TCP: + printk("TCP "); + break; + case IPPROTO_UDP: + printk("UDP "); + case IPPROTO_ICMP: + printk("ICMP "); + break; + default: + printk("p=%d ",ip->protocol); + break; + } + print_ip(ip->saddr); + if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP) + printk(":%d", src_port); + printk(" "); + print_ip(ip->daddr); + if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP) + printk(":%d",dst_port); + printk("\n"); + } +#endif + if (opt != 2) { + f->fw_bcnt+=ntohs(ip->tot_len); + f->fw_pcnt++; + } + if (opt != 1) + break; + } /* Loop */ + + if(opt == 1) + return 0; + + /* + * We rely on policy defined in the rejecting entry or, if no match + * was found, we rely on the general policy variable for this type + * of firewall. + */ + + if(f!=NULL) /* A match was found */ + f_flag=f->fw_flg; + else + f_flag=policy; + if(f_flag&IP_FW_F_ACCEPT) + return ((f_flag&IP_FW_F_MASQ)?2:1); + if(f_flag&IP_FW_F_ICMPRPL) + return -1; + return 0; +} + +#ifdef CONFIG_IP_MASQUERADE + +static void masq_expire(unsigned long data) +{ + struct ip_masq *ms = (struct ip_masq *)data; + struct ip_masq *old,*cur; + unsigned long flags; + +#ifdef DEBUG_MASQ + printk("Masqueraded %s %lX:%X expired\n", + strProt[ms->protocol==IPPROTO_TCP], + ntohl(ms->src),ntohs(ms->sport)); +#endif + + save_flags(flags); + cli(); + + /* delete from list of hosts */ + old = NULL; + cur = ip_msq_hosts; + while (cur!=NULL) { + if (cur==ms) { + if (old==NULL) ip_msq_hosts = ms->next; + else old->next = ms->next; + kfree_s(ms,sizeof(*ms)); + break; + } + old = cur; + cur=cur->next; + } + restore_flags(flags); +} + +/* + * Create a new masquerade list entry, also allocate an + * unused mport, keeping the portnumber between the + * given boundaries MASQ_BEGIN and MASQ_END. + * + * FIXME: possible deadlock if all free ports are exhausted! + */ +static struct ip_masq *alloc_masq_entry(void) +{ + struct ip_masq *ms, *mst; + unsigned long flags; + + ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC); + if (ms==NULL) + return NULL; + + memset(ms,0,sizeof(*ms)); + init_timer(&ms->timer); + ms->timer.data = (unsigned long)ms; + ms->timer.function = masq_expire; + + save_flags(flags); + cli(); + do + { + /* Try the next available port number */ + ms->mport = htons(masq_port++); + if (masq_port==PORT_MASQ_END) + masq_port = PORT_MASQ_BEGIN; + + /* Now hunt through the used ports to see if + * this port is in use... */ + mst = ip_msq_hosts; + while (mst && mst->mport!=ms->mport) + mst = mst->next; + } + while (mst!=NULL); + + /* add new entry in front of list to minimize lookup-time */ + ms->next = ip_msq_hosts; + ip_msq_hosts = ms; + restore_flags(flags); + + return ms; +} + +/* + * When passing an FTP 'PORT' command, try to replace the IP + * address with an newly assigned (masquereded) port on this + * host, so the ftp-data connect FROM the site will succeed... + * + * Also, when the size of the packet changes, create an delta + * offset, which will be added to every th->seq (and subtracted for + * (th->acqseq) whose seq > init_seq. + * + * Not for the faint of heart! + */ + +static struct sk_buff *revamp(struct sk_buff *skb, struct device *dev, struct ip_masq *ftp) +{ + struct iphdr *iph = skb->h.iph; + struct tcphdr *th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); + struct sk_buff *skb2; + char *p, *data = (char *)&th[1]; + unsigned char p1,p2,p3,p4,p5,p6; + unsigned long from; + unsigned short port; + struct ip_masq *ms; + char buf[20]; /* xxx.xxx.xxx.xxx\r\n */ + + /* + * Adjust seq and ack_seq with delta-offset for + * the packets AFTER this one... + */ + if (ftp->delta && after(ftp->init_seq,th->seq)) + { + th->seq += ftp->delta; +/* th->ack_seq += ftp->delta;*/ + } + + while (skb->len - ((unsigned char *)data - skb->h.raw) > 18) + { + if (memcmp(data,"PORT ",5)!=0 && memcmp(data,"port ",5)!=0) + { + data += 5; + continue; + } + p = data+5; + p1 = simple_strtoul(data+5,&data,10); + if (*data!=',') + continue; + p2 = simple_strtoul(data+1,&data,10); + if (*data!=',') + continue; + p3 = simple_strtoul(data+1,&data,10); + if (*data!=',') + continue; + p4 = simple_strtoul(data+1,&data,10); + if (*data!=',') + continue; + p5 = simple_strtoul(data+1,&data,10); + if (*data!=',') + continue; + p6 = simple_strtoul(data+1,&data,10); + if (*data!='\r' && *data!='\n') + continue; + + from = (p1<<24) | (p2<<16) | (p3<<8) | p4; + port = (p5<<8) | p6; + printk("PORT %lX:%X detected\n",from,port); + + /* + * Now create an masquerade entry for it + */ + ms = alloc_masq_entry(); + if (ms==NULL) + return skb; + ms->protocol = IPPROTO_TCP; + ms->src = htonl(from); /* derived from PORT cmd */ + ms->sport = htons(port); /* derived from PORT cmd */ + ms->dst = iph->daddr; + ms->dport = htons(20); /* ftp-data */ + ms->timer.expires = MASQUERADE_EXPIRE_TCP_FIN; + add_timer(&ms->timer); + + /* + * Replace the old PORT with the new one + */ + from = ntohl(dev->pa_addr); + port = ntohs(ms->mport); + sprintf(buf,"%ld,%ld,%ld,%ld,%d,%d", + from>>24&255,from>>16&255,from>>8&255,from&255, + port>>8&255,port&255); + + /* + * Calculate required delta-offset to keep TCP happy + */ + ftp->delta += strlen(buf) - (data-p); + if (ftp->delta==0) + { + /* + * simple case, just replace the old PORT cmd + */ + ftp->init_seq = 0; + memcpy(p,buf,strlen(buf)); + return skb; + } + + /* + * Sizes differ, make a copy + */ + printk("MASQUERADE: resizing needed for %d bytes (%ld)\n",ftp->delta, skb->len); + if (!ftp->init_seq) + ftp->init_seq = th->seq; + + skb2 = alloc_skb(skb->mem_len-sizeof(struct sk_buff)+ftp->delta, GFP_ATOMIC); + if (skb2 == NULL) { + printk("MASQUERADE: No memory available\n"); + return skb; + } + skb2->free = skb->free; + skb2->len = skb->len + ftp->delta; + skb2->h.raw = &skb2->data[skb->h.raw - skb->data]; + + /* + * Copy the packet data into the new buffer. + * Thereby replacing the PORT cmd. + */ + memcpy(skb2->data, skb->data, (p - (char *)skb->data)); + memcpy(&skb2->data[(p - (char *)skb->data)], buf, strlen(buf)); + memcpy(&skb2->data[(p - (char *)skb->data) + strlen(buf)], data, + skb->mem_len - sizeof(struct sk_buff) - ((char *)skb->h.raw - data)); + + /* + * Problem, how to replace the new skb with old one, + * preferably inplace, so all the pointers in the + * calling tree keep ok :( + */ + kfree_skb(skb, FREE_WRITE); + return skb2; + } + return skb; +} + +static void recalc_check(struct udphdr *uh, unsigned long saddr, + unsigned long daddr, int len) +{ + uh->check=0; + uh->check=csum_tcpudp_magic(saddr,daddr,len, + IPPROTO_UDP, csum_partial((char *)uh,len,0)); + if(uh->check==0) + uh->check=-0xFFFF; +} + +void ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev) +{ + struct sk_buff *skb=*skb_ptr; + struct iphdr *iph = skb->h.iph; + unsigned short *portptr; + struct ip_masq *ms; + int size; + + /* + * We can only masquerade protocols with ports... + */ + + if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP) + return; + + /* + * Now hunt the list to see if we have an old entry + */ + + portptr = (unsigned short *)&(((char *)iph)[iph->ihl*4]); + ms = ip_msq_hosts; + +#ifdef DEBUG_MASQ + printk("Outgoing %s %lX:%X -> %lX:%X\n", + strProt[iph->protocol==IPPROTO_TCP], + ntohl(iph->saddr), ntohs(portptr[0]), + ntohl(iph->daddr), ntohs(portptr[1])); +#endif + while (ms!=NULL) + { + if (iph->protocol == ms->protocol && + iph->saddr == ms->src && iph->daddr == ms->dst && + portptr[0] == ms->sport && portptr[1] == ms->dport) + { + del_timer(&ms->timer); + break; + } + ms = ms->next; + } + + /* + * Nope, not found, create a new entry for it + */ + + if (ms==NULL) + { + ms = alloc_masq_entry(); + if (ms==NULL) + { + printk("MASQUERADE: no memory left !\n"); + return; + } + ms->protocol = iph->protocol; + ms->src = iph->saddr; + ms->dst = iph->daddr; + ms->sport = portptr[0]; + ms->dport = portptr[1]; + } + + /* + * Change the fragments origin + */ + + size = skb->len - ((unsigned char *)portptr - skb->h.raw); + iph->saddr = dev->pa_addr; /* my own address */ + portptr[0] = ms->mport; + + /* + * Adjust packet accordingly to protocol + */ + + if (iph->protocol==IPPROTO_UDP) + { + ms->timer.expires = MASQUERADE_EXPIRE_UDP; + recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size); + } + else + { + struct tcphdr *th; + if (portptr[1]==htons(21)) + { + skb = revamp(*skb_ptr, dev, ms); + skb = *skb_ptr; + iph = skb->h.iph; + portptr = (unsigned short *)&(((char *)iph)[iph->ihl*4]); + } + th = (struct tcphdr *)portptr; + + /* + * Timeout depends if FIN packet was seen + */ + if (ms->sawfin || th->fin) + { + ms->timer.expires = MASQUERADE_EXPIRE_TCP_FIN; + ms->sawfin = 1; + } + else ms->timer.expires = MASQUERADE_EXPIRE_TCP; + + tcp_send_check(th,iph->saddr,iph->daddr,size,skb->sk); + } + add_timer(&ms->timer); + ip_send_check(iph); + + #ifdef DEBUG_MASQ + printk("O-routed from %lX:%X over %s\n",ntohl(dev->pa_addr),ntohs(ms->mport),dev->name); + #endif + } + + /* + * Check if it's an masqueraded port, look it up, + * and send it on it's way... + * + * Better not have many hosts using the designated portrange + * as 'normal' ports, or you'll be spending lots of time in + * this function. + */ + +int ip_fw_demasquerade(struct sk_buff *skb_ptr) +{ + struct iphdr *iph = skb_ptr->h.iph; + unsigned short *portptr; + struct ip_masq *ms; + struct tcphdr *th = (struct tcphdr *)(skb_ptr->h.raw+(iph->ihl<<2)); + + if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP) + return 0; + + portptr = (unsigned short *)&(((char *)iph)[iph->ihl*4]); + if (ntohs(portptr[1]) < PORT_MASQ_BEGIN || + ntohs(portptr[1]) > PORT_MASQ_END) + return 0; + +#ifdef DEBUG_MASQ + printk("Incoming %s %lX:%X -> %lX:%X\n", + strProt[iph->protocol==IPPROTO_TCP], + ntohl(iph->saddr), ntohs(portptr[0]), + ntohl(iph->daddr), ntohs(portptr[1])); +#endif + /* + * reroute to original host:port if found... + * + * NB. Cannot check destination address, just for the incoming port. + * reason: archie.doc.ac.uk has 6 interfaces, you send to + * phoenix and get a reply from any other interface(==dst)! + * + * [Only for UDP] - AC + */ + ms = ip_msq_hosts; + while (ms!=NULL) + { + if (iph->protocol==ms->protocol && + (iph->saddr==ms->dst || iph->protocol==IPPROTO_UDP) && + portptr[0]==ms->dport && + portptr[1]==ms->mport) + { + int size = skb_ptr->len - ((unsigned char *)portptr - skb_ptr->h.raw); + iph->daddr = ms->src; + portptr[1] = ms->sport; + + /* + * Yug! adjust UDP/TCP and IP checksums + */ + if (iph->protocol==IPPROTO_UDP) + recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size); + else + { + /* + * Adjust seq and ack_seq with delta-offset for + * the packets AFTER this one... + */ + if (ms->delta && after(ms->init_seq,th->ack_seq)) + { +/* th->seq += ms->delta;*/ + th->ack_seq -= ms->delta; + } + tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,size,skb_ptr->sk); + } + ip_send_check(iph); +#ifdef DEBUG_MASQ + printk("I-routed to %lX:%X\n",ntohl(iph->daddr),ntohs(portptr[1])); +#endif + return 1; + } + ms = ms->next; + } + + /* sorry, all this trouble for a no-hit :) */ + return 0; +} +#endif + + + +static void zero_fw_chain(struct ip_fw *chainptr) +{ + struct ip_fw *ctmp=chainptr; + while(ctmp) + { + ctmp->fw_pcnt=0L; + ctmp->fw_bcnt=0L; + ctmp=ctmp->fw_next; + } +} + +static void free_fw_chain(struct ip_fw *volatile* chainptr) +{ + unsigned long flags; + save_flags(flags); + cli(); + while ( *chainptr != NULL ) + { + struct ip_fw *ftmp; + ftmp = *chainptr; + *chainptr = ftmp->fw_next; + kfree_s(ftmp,sizeof(*ftmp)); + } + restore_flags(flags); +} + +/* Volatiles to keep some of the compiler versions amused */ + +static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl) +{ + struct ip_fw *ftmp; + struct ip_fw *chtmp=NULL; + struct ip_fw *volatile chtmp_prev=NULL; + unsigned long flags; + unsigned long m_src_mask,m_dst_mask; + unsigned long n_sa,n_da,o_sa,o_da,o_sm,o_dm,n_sm,n_dm; + unsigned short n_sr,n_dr,o_sr,o_dr; + unsigned short oldkind,newkind; + int addb4=0; + int n_o,n_n; + + save_flags(flags); + + ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC ); + if ( ftmp == NULL ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: malloc said no\n"); +#endif + return( ENOMEM ); + } + + memcpy(ftmp, frwl, sizeof( struct ip_fw ) ); + + ftmp->fw_pcnt=0L; + ftmp->fw_bcnt=0L; + + ftmp->fw_next = NULL; + + cli(); + + if (*chainptr==NULL) + { + *chainptr=ftmp; + } + else + { + chtmp_prev=NULL; + for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next) + { + addb4=0; + newkind=ftmp->fw_flg & IP_FW_F_KIND; + oldkind=chtmp->fw_flg & IP_FW_F_KIND; + + if (newkind!=IP_FW_F_ALL + && oldkind!=IP_FW_F_ALL + && oldkind!=newkind) + { + chtmp_prev=chtmp; + continue; + } + + /* + * Very very *UGLY* code... + * Sorry,but i had to do this.... + */ + + n_sa=ntohl(ftmp->fw_src.s_addr); + n_da=ntohl(ftmp->fw_dst.s_addr); + n_sm=ntohl(ftmp->fw_smsk.s_addr); + n_dm=ntohl(ftmp->fw_dmsk.s_addr); + + o_sa=ntohl(chtmp->fw_src.s_addr); + o_da=ntohl(chtmp->fw_dst.s_addr); + o_sm=ntohl(chtmp->fw_smsk.s_addr); + o_dm=ntohl(chtmp->fw_dmsk.s_addr); + + m_src_mask = o_sm & n_sm; + m_dst_mask = o_dm & n_dm; + + if ((o_sa & m_src_mask) == (n_sa & m_src_mask)) + { + if (n_sm > o_sm) + addb4++; + if (n_sm < o_sm) + addb4--; + } + + if ((o_da & m_dst_mask) == (n_da & m_dst_mask)) + { + if (n_dm > o_dm) + addb4++; + if (n_dm < o_dm) + addb4--; + } + + if (((o_da & o_dm) == (n_da & n_dm)) + &&((o_sa & o_sm) == (n_sa & n_sm))) + { + if (newkind!=IP_FW_F_ALL && + oldkind==IP_FW_F_ALL) + addb4++; + if (newkind==oldkind && (oldkind==IP_FW_F_TCP + || oldkind==IP_FW_F_UDP)) + { + + /* + * Here the main idea is to check the size + * of port range which the frwl covers + * We actually don't check their values but + * just the wideness of range they have + * so that less wide ranges or single ports + * go first and wide ranges go later. No ports + * at all treated as a range of maximum number + * of ports. + */ + + if (ftmp->fw_flg & IP_FW_F_SRNG) + n_sr=ftmp->fw_pts[1]-ftmp->fw_pts[0]; + else + n_sr=(ftmp->fw_nsp)? + ftmp->fw_nsp : 0xFFFF; + + if (chtmp->fw_flg & IP_FW_F_SRNG) + o_sr=chtmp->fw_pts[1]-chtmp->fw_pts[0]; + else + o_sr=(chtmp->fw_nsp)?chtmp->fw_nsp : 0xFFFF; + + if (n_sr<o_sr) + addb4++; + if (n_sr>o_sr) + addb4--; + + n_n=ftmp->fw_nsp; + n_o=chtmp->fw_nsp; + + /* + * Actually this cannot happen as the frwl control + * procedure checks for number of ports in source and + * destination range but we will try to be more safe. + */ + + if ((n_n>(IP_FW_MAX_PORTS-2)) || + (n_o>(IP_FW_MAX_PORTS-2))) + goto skip_check; + + if (ftmp->fw_flg & IP_FW_F_DRNG) + n_dr=ftmp->fw_pts[n_n+1]-ftmp->fw_pts[n_n]; + else + n_dr=(ftmp->fw_ndp)? ftmp->fw_ndp : 0xFFFF; + + if (chtmp->fw_flg & IP_FW_F_DRNG) + o_dr=chtmp->fw_pts[n_o+1]-chtmp->fw_pts[n_o]; + else + o_dr=(chtmp->fw_ndp)? chtmp->fw_ndp : 0xFFFF; + if (n_dr<o_dr) + addb4++; + if (n_dr>o_dr) + addb4--; +skip_check: + } + /* finally look at the interface address */ + if ((addb4 == 0) && ftmp->fw_via.s_addr && + !(chtmp->fw_via.s_addr)) + addb4++; + } + if (addb4>0) + { + if (chtmp_prev) + { + chtmp_prev->fw_next=ftmp; + ftmp->fw_next=chtmp; + } + else + { + *chainptr=ftmp; + ftmp->fw_next=chtmp; + } + restore_flags(flags); + return 0; + } + chtmp_prev=chtmp; + } + } + + if (chtmp_prev) + chtmp_prev->fw_next=ftmp; + else + *chainptr=ftmp; + restore_flags(flags); + return(0); +} + +static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl) +{ + struct ip_fw *ftmp,*ltmp; + unsigned short tport1,tport2,tmpnum; + char matches,was_found; + unsigned long flags; + + save_flags(flags); + cli(); + + ftmp=*chainptr; + + if ( ftmp == NULL ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: chain is empty\n"); +#endif + restore_flags(flags); + return( EINVAL ); + } + + ltmp=NULL; + was_found=0; + + while( ftmp != NULL ) + { + matches=1; + if (ftmp->fw_src.s_addr!=frwl->fw_src.s_addr + || ftmp->fw_dst.s_addr!=frwl->fw_dst.s_addr + || ftmp->fw_smsk.s_addr!=frwl->fw_smsk.s_addr + || ftmp->fw_dmsk.s_addr!=frwl->fw_dmsk.s_addr + || ftmp->fw_via.s_addr!=frwl->fw_via.s_addr + || ftmp->fw_flg!=frwl->fw_flg) + matches=0; + + tport1=ftmp->fw_nsp+ftmp->fw_ndp; + tport2=frwl->fw_nsp+frwl->fw_ndp; + if (tport1!=tport2) + matches=0; + else if (tport1!=0) + { + for (tmpnum=0;tmpnum < tport1 && tmpnum < IP_FW_MAX_PORTS;tmpnum++) + if (ftmp->fw_pts[tmpnum]!=frwl->fw_pts[tmpnum]) + matches=0; + } + if(matches) + { + was_found=1; + if (ltmp) + { + ltmp->fw_next=ftmp->fw_next; + kfree_s(ftmp,sizeof(*ftmp)); + ftmp=ltmp->fw_next; + } + else + { + *chainptr=ftmp->fw_next; + kfree_s(ftmp,sizeof(*ftmp)); + ftmp=*chainptr; + } + } + else + { + ltmp = ftmp; + ftmp = ftmp->fw_next; + } + } + restore_flags(flags); + if (was_found) + return 0; + else + return(EINVAL); +} + +#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */ + +struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len) +{ + + if ( len != sizeof(struct ip_fw) ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw)); +#endif + return(NULL); + } + + if ( (frwl->fw_flg & ~IP_FW_F_MASK) != 0 ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: undefined flag bits set (flags=%x)\n", + frwl->fw_flg); +#endif + return(NULL); + } + + if ( (frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2 ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: src range set but fw_nsp=%d\n", + frwl->fw_nsp); +#endif + return(NULL); + } + + if ( (frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2 ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: dst range set but fw_ndp=%d\n", + frwl->fw_ndp); +#endif + return(NULL); + } + + if ( frwl->fw_nsp + frwl->fw_ndp > IP_FW_MAX_PORTS ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: too many ports (%d+%d)\n", + frwl->fw_nsp,frwl->fw_ndp); +#endif + return(NULL); + } + + return frwl; +} + + + + +#ifdef CONFIG_IP_ACCT + +#if 0 +void ip_acct_cnt(struct iphdr *iph, struct device *dev, struct ip_fw *f) +{ + (void) ip_fw_chk(iph, dev, f, 0, 1); + return; +} +#endif + +int ip_acct_ctl(int stage, void *m, int len) +{ + if ( stage == IP_ACCT_FLUSH ) + { + free_fw_chain(&ip_acct_chain); + return(0); + } + if ( stage == IP_ACCT_ZERO ) + { + zero_fw_chain(ip_acct_chain); + return(0); + } + if ( stage == IP_ACCT_ADD + || stage == IP_ACCT_DEL + ) + { + struct ip_fw *frwl; + + if (!(frwl=check_ipfw_struct(m,len))) + return (EINVAL); + + switch (stage) + { + case IP_ACCT_ADD: + return( add_to_chain(&ip_acct_chain,frwl)); + case IP_ACCT_DEL: + return( del_from_chain(&ip_acct_chain,frwl)); + default: + /* + * Should be panic but... (Why ??? - AC) + */ +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_acct_ctl: unknown request %d\n",stage); +#endif + return(EINVAL); + } + } +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_acct_ctl: unknown request %d\n",stage); +#endif + return(EINVAL); +} +#endif + +#ifdef CONFIG_IP_FIREWALL +int ip_fw_ctl(int stage, void *m, int len) +{ + int ret; + + if ( stage == IP_FW_FLUSH_BLK ) + { + free_fw_chain(&ip_fw_blk_chain); + return(0); + } + + if ( stage == IP_FW_FLUSH_FWD ) + { + free_fw_chain(&ip_fw_fwd_chain); + return(0); + } + + if ( stage == IP_FW_ZERO_BLK ) + { + zero_fw_chain(ip_fw_blk_chain); + return(0); + } + + if ( stage == IP_FW_ZERO_FWD ) + { + zero_fw_chain(ip_fw_fwd_chain); + return(0); + } + + if ( stage == IP_FW_POLICY_BLK || stage == IP_FW_POLICY_FWD ) + { + int *tmp_policy_ptr; + tmp_policy_ptr=(int *)m; + if ( stage == IP_FW_POLICY_BLK ) + ip_fw_blk_policy=*tmp_policy_ptr; + else + ip_fw_fwd_policy=*tmp_policy_ptr; + return 0; + } + + if ( stage == IP_FW_CHK_BLK || stage == IP_FW_CHK_FWD ) + { + struct device viadev; + struct ip_fwpkt *ipfwp; + struct iphdr *ip; + + if ( len < sizeof(struct ip_fwpkt) ) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printf("ip_fw_ctl: length=%d, expected %d\n", + len, sizeof(struct ip_fwpkt)); +#endif + return( EINVAL ); + } + + ipfwp = (struct ip_fwpkt *)m; + ip = &(ipfwp->fwp_iph); + + if ( ip->ihl != sizeof(struct iphdr) / sizeof(int)) + { +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl, + sizeof(struct iphdr)/sizeof(int)); +#endif + return(EINVAL); + } + + viadev.pa_addr = ipfwp->fwp_via.s_addr; + + if ((ret = ip_fw_chk(ip, &viadev, + stage == IP_FW_CHK_BLK ? + ip_fw_blk_chain : ip_fw_fwd_chain, + stage == IP_FW_CHK_BLK ? + ip_fw_blk_policy : ip_fw_fwd_policy, 2 )) > 0 + ) + return(0); + else if (ret == -1) + return(ECONNREFUSED); + else + return(ETIMEDOUT); + } + +/* + * Here we really working hard-adding new elements + * to blocking/forwarding chains or deleting 'em + */ + + if ( stage == IP_FW_ADD_BLK || stage == IP_FW_ADD_FWD + || stage == IP_FW_DEL_BLK || stage == IP_FW_DEL_FWD + ) + { + struct ip_fw *frwl; + frwl=check_ipfw_struct(m,len); + if (frwl==NULL) + return (EINVAL); + + switch (stage) + { + case IP_FW_ADD_BLK: + return(add_to_chain(&ip_fw_blk_chain,frwl)); + case IP_FW_ADD_FWD: + return(add_to_chain(&ip_fw_fwd_chain,frwl)); + case IP_FW_DEL_BLK: + return(del_from_chain(&ip_fw_blk_chain,frwl)); + case IP_FW_DEL_FWD: + return(del_from_chain(&ip_fw_fwd_chain,frwl)); + default: + /* + * Should be panic but... (Why are BSD people panic obsessed ??) + */ +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: unknown request %d\n",stage); +#endif + return(EINVAL); + } + } + +#ifdef DEBUG_CONFIG_IP_FIREWALL + printk("ip_fw_ctl: unknown request %d\n",stage); +#endif + return(EINVAL); +} +#endif /* CONFIG_IP_FIREWALL */ + +#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT) + +static int ip_chain_procinfo(int stage, char *buffer, char **start, + off_t offset, int length, int reset) +{ + off_t pos=0, begin=0; + struct ip_fw *i; + unsigned long flags; + int len, p; + + + switch(stage) + { +#ifdef CONFIG_IP_FIREWALL + case IP_INFO_BLK: + i = ip_fw_blk_chain; + len=sprintf(buffer, "IP firewall block rules, default %d\n", + ip_fw_blk_policy); + break; + case IP_INFO_FWD: + i = ip_fw_fwd_chain; + len=sprintf(buffer, "IP firewall forward rules, default %d\n", + ip_fw_fwd_policy); + break; +#endif +#ifdef CONFIG_IP_ACCT + case IP_INFO_ACCT: + i = ip_acct_chain; + len=sprintf(buffer,"IP accounting rules\n"); + break; +#endif + default: + /* this should never be reached, but safety first... */ + i = NULL; + len=0; + break; + } + + save_flags(flags); + cli(); + + while(i!=NULL) + { + len+=sprintf(buffer+len,"%08lX/%08lX->%08lX/%08lX %08lX %X ", + ntohl(i->fw_src.s_addr),ntohl(i->fw_smsk.s_addr), + ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr), + ntohl(i->fw_via.s_addr),i->fw_flg); + len+=sprintf(buffer+len,"%u %u %-9lu %-9lu", + i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt); + for (p = 0; p < IP_FW_MAX_PORTS; p++) + len+=sprintf(buffer+len, " %u", i->fw_pts[p]); + buffer[len++]='\n'; + buffer[len]='\0'; + pos=begin+len; + if(pos<offset) + { + len=0; + begin=pos; + } + else if(reset) + { + /* This needs to be done at this specific place! */ + i->fw_pcnt=0L; + i->fw_bcnt=0L; + } + if(pos>offset+length) + break; + i=i->fw_next; + } + restore_flags(flags); + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} +#endif + +#ifdef CONFIG_IP_ACCT + +int ip_acct_procinfo(char *buffer, char **start, off_t offset, int length, int reset) +{ + return ip_chain_procinfo(IP_INFO_ACCT, buffer,start,offset,length,reset); +} + +#endif + +#ifdef CONFIG_IP_FIREWALL + +int ip_fw_blk_procinfo(char *buffer, char **start, off_t offset, int length, int reset) +{ + return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,reset); +} + +int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset, int length, int reset) +{ + return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,reset); +} +#endif + +#ifdef CONFIG_IP_MASQUERADE + +int ip_msqhst_procinfo(char *buffer, char **start, off_t offset, int length) +{ + off_t pos=0, begin=0; + struct ip_masq *ms; + unsigned long flags; + int len=0; + + len=sprintf(buffer,"Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta Expires\n"); + save_flags(flags); + cli(); + + ms=ip_msq_hosts; + while (ms!=NULL) + { + int timer_active = del_timer(&ms->timer); + if (!timer_active) + ms->timer.expires = 0; + len+=sprintf(buffer+len,"%s %08lX:%04X %08lX:%04X %04X %08lX %5d %lu\n", + strProt[ms->protocol==IPPROTO_TCP], + ntohl(ms->src),ntohs(ms->sport), + ntohl(ms->dst),ntohs(ms->dport), + ntohs(ms->mport), + ms->init_seq,ms->delta,ms->timer.expires); + if (timer_active) + add_timer(&ms->timer); + + pos=begin+len; + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + ms=ms->next; + } + restore_flags(flags); + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} + +#endif + diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c new file mode 100644 index 000000000..5227d9474 --- /dev/null +++ b/net/ipv4/ipip.c @@ -0,0 +1,95 @@ +/* + * Linux NET3: IP/IP protocol decoder. + * + * Authors: + * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 + * + * Fixes: + * Alan Cox : Merged and made usable non modular (its so tiny its silly as + * a module taking up 2 pages). + * + * 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/types.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <netinet/in.h> +#include <net/datalink.h> +#include <net/sock.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/ipip.h> + +/* + * NB. we must include the kernel idenfication string in to install the module. + */ + +#if ( defined(CONFIG_NET_IPIP) && defined(CONFIG_IP_FORWARD)) || defined(MODULE) +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> + +static char kernel_version[] = UTS_RELEASE; + +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + + +/* + * The driver. + */ + +int ipip_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) +{ + /* Don't unlink in the middle of a turnaround */ + MOD_INC_USE_COUNT; +#ifdef TUNNEL_DEBUG + printk("ipip_rcv: got a packet!\n"); +#endif + ip_forward(skb, dev, 0, daddr, 0); + kfree_skb(skb, FREE_READ); + MOD_DEC_USE_COUNT; + return(0); +} + +#ifdef MODULE +static struct inet_protocol ipip_protocol = { + ipip_rcv, /* IPIP handler */ + NULL, /* Will be UDP fraglist handler */ + NULL, /* TUNNEL error control */ + 0, /* next */ + IPPROTO_IPIP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "IPIP" /* name */ +}; + + +/* + * And now the modules code and kernel interface. + */ + +int init_module( void) +{ + inet_add_protocol(&ipip_protocol); + return 0; +} + +void cleanup_module( void) +{ + if ( inet_del_protocol(&ipip_protocol) < 0 ) + printk("ipip close: can't remove protocol\n"); +} + +#endif +#endif diff --git a/net/ipv4/packet.c b/net/ipv4/packet.c new file mode 100644 index 000000000..fbc4dd5ca --- /dev/null +++ b/net/ipv4/packet.c @@ -0,0 +1,410 @@ +/* + * 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. + * + * PACKET - implements raw packet sockets. + * + * Version: @(#)packet.c 1.0.6 05/25/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * + * Fixes: + * Alan Cox : verify_area() now used correctly + * Alan Cox : new skbuff lists, look ma no backlogs! + * Alan Cox : tidied skbuff lists. + * Alan Cox : Now uses generic datagram routines I + * added. Also fixed the peek/read crash + * from all old Linux datagram code. + * Alan Cox : Uses the improved datagram code. + * Alan Cox : Added NULL's for socket options. + * Alan Cox : Re-commented the code. + * Alan Cox : Use new kernel side addressing + * Rob Janssen : Correct MTU usage. + * Dave Platt : Counter leaks caused by incorrect + * interrupt locking and some slightly + * dubious gcc output. Can you read + * compiler: it said _VOLATILE_ + * Richard Kooijman : Timestamp fixes. + * + * 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/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/fcntl.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <asm/system.h> +#include <asm/segment.h> + +/* + * We really ought to have a single public _inline_ min function! + */ + +static unsigned long min(unsigned long a, unsigned long b) +{ + if (a < b) + return(a); + return(b); +} + + +/* + * This should be the easiest of all, all we do is copy it into a buffer. + */ + +int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ + struct sock *sk; + unsigned long flags; + + /* + * When we registered the protocol we saved the socket in the data + * field for just this event. + */ + + sk = (struct sock *) pt->data; + + /* + * The SOCK_PACKET socket receives _all_ frames, and as such + * therefore needs to put the header back onto the buffer. + * (it was removed by inet_bh()). + */ + + skb->dev = dev; + skb->len += dev->hard_header_len; + + /* + * Charge the memory to the socket. This is done specifically + * to prevent sockets using all the memory up. + */ + + if (sk->rmem_alloc & 0xFF000000) { + printk("packet_rcv: sk->rmem_alloc = %ld\n", sk->rmem_alloc); + sk->rmem_alloc = 0; + } + + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) + { +/* printk("packet_rcv: drop, %d+%d>%d\n", sk->rmem_alloc, skb->mem_len, sk->rcvbuf); */ + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return(0); + } + + save_flags(flags); + cli(); + + skb->sk = sk; + sk->rmem_alloc += skb->mem_len; + + /* + * Queue the packet up, and wake anyone waiting for it. + */ + + skb_queue_tail(&sk->receive_queue,skb); + if(!sk->dead) + sk->data_ready(sk,skb->len); + + restore_flags(flags); + + /* + * Processing complete. + */ + + return(0); +} + + +/* + * Output a raw packet to a device layer. This bypasses all the other + * protocol layers and you must therefore supply it with a complete frame + */ + +static int packet_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; + struct sockaddr *saddr=(struct sockaddr *)usin; + + /* + * Check the flags. + */ + + if (flags) + return(-EINVAL); + + /* + * Get and verify the address. + */ + + if (usin) + { + if (addr_len < sizeof(*saddr)) + return(-EINVAL); + } + else + return(-EINVAL); /* SOCK_PACKET must be sent giving an address */ + + /* + * Find the device first to size check it + */ + + saddr->sa_data[13] = 0; + dev = dev_get(saddr->sa_data); + if (dev == NULL) + { + return(-ENXIO); + } + + /* + * You may not queue a frame bigger than the mtu. This is the lowest level + * raw protocol and you must do your own fragmentation at this level. + */ + + if(len>dev->mtu+dev->hard_header_len) + return -EMSGSIZE; + + skb = sk->prot->wmalloc(sk, len, 0, GFP_KERNEL); + + /* + * If the write buffer is full, then tough. At this level the user gets to + * deal with the problem - do your own algorithmic backoffs. + */ + + if (skb == NULL) + { + return(-ENOBUFS); + } + + /* + * Fill it in + */ + + skb->sk = sk; + skb->free = 1; + memcpy_fromfs(skb->data, from, len); + skb->len = len; + skb->arp = 1; /* No ARP needs doing on this (complete) frame */ + + /* + * Now send it + */ + + if (dev->flags & IFF_UP) + dev_queue_xmit(skb, dev, sk->priority); + else + kfree_skb(skb, FREE_WRITE); + return(len); +} + +/* + * A write to a SOCK_PACKET can't actually do anything useful and will + * always fail but we include it for completeness and future expansion. + */ + +static int packet_write(struct sock *sk, unsigned char *buff, + int len, int noblock, unsigned flags) +{ + return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0)); +} + +/* + * Close a SOCK_PACKET socket. This is fairly simple. We immediately go + * to 'closed' state and remove our protocol entry in the device list. + * The release_sock() will destroy the socket if a user has closed the + * file side of the object. + */ + +static void packet_close(struct sock *sk, int timeout) +{ + sk->inuse = 1; + sk->state = TCP_CLOSE; + dev_remove_pack((struct packet_type *)sk->pair); + kfree_s((void *)sk->pair, sizeof(struct packet_type)); + sk->pair = NULL; + release_sock(sk); +} + +/* + * Create a packet of type SOCK_PACKET. We do one slightly irregular + * thing here that wants tidying up. We borrow the 'pair' pointer in + * the socket object so we can find the packet_type entry in the + * device list. The reverse is easy as we use the data field of the + * packet type to point to our socket. + */ + +static int packet_init(struct sock *sk) +{ + struct packet_type *p; + + p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return(-ENOMEM); + + p->func = packet_rcv; + p->type = sk->num; + p->data = (void *)sk; + p->dev = NULL; + dev_add_pack(p); + + /* + * We need to remember this somewhere. + */ + + sk->pair = (struct sock *)p; + + return(0); +} + + +/* + * Pull a packet from our receive queue and hand it to the user. + * If necessary we block. + */ + +int packet_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; + struct sockaddr *saddr; + int err; + int truesize; + + saddr = (struct sockaddr *)sin; + + if (sk->shutdown & RCV_SHUTDOWN) + return(0); + + /* + * If the address length field is there to be filled in, we fill + * it in now. + */ + + if (addr_len) + *addr_len=sizeof(*saddr); + + /* + * Call the generic datagram receiver. This handles all sorts + * of horrible races and re-entrancy so we can forget about it + * in the protocol layers. + */ + + skb=skb_recv_datagram(sk,flags,noblock,&err); + + /* + * An error occurred so return it. Because skb_recv_datagram() + * handles the blocking we don't see and worry about blocking + * retries. + */ + + if(skb==NULL) + return err; + + /* + * You lose any data beyond the buffer you gave. If it worries a + * user program they can ask the device for its MTU anyway. + */ + + truesize = skb->len; + copied = min(len, truesize); + + memcpy_tofs(to, skb->data, copied); /* We can't use skb_copy_datagram here */ + sk->stamp=skb->stamp; + + /* + * Copy the address. + */ + + if (saddr) + { + saddr->sa_family = skb->dev->type; + memcpy(saddr->sa_data,skb->dev->name, 14); + } + + /* + * Free or return the buffer as appropriate. Again this hides all the + * races and re-entrancy issues from us. + */ + + skb_free_datagram(skb); + + /* + * We are done. + */ + + release_sock(sk); + return(truesize); +} + + +/* + * A packet read can succeed and is just the same as a recvfrom but without the + * addresses being recorded. + */ + +int packet_read(struct sock *sk, unsigned char *buff, + int len, int noblock, unsigned flags) +{ + return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); +} + + +/* + * This structure declares to the lower layer socket subsystem currently + * incorrectly embedded in the IP code how to behave. This interface needs + * a lot of work and will change. + */ + +struct proto packet_prot = +{ + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + packet_close, + packet_read, + packet_write, + packet_sendto, + packet_recvfrom, + ip_build_header, /* Not actually used */ + NULL, + NULL, + ip_queue_xmit, /* These two are not actually used */ + NULL, + NULL, + NULL, + NULL, + datagram_select, + NULL, + packet_init, + NULL, + NULL, /* No set/get socket options */ + NULL, + 128, + 0, + "PACKET", + 0, 0 +}; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c new file mode 100644 index 000000000..e7124a42d --- /dev/null +++ b/net/ipv4/proc.c @@ -0,0 +1,268 @@ +/* + * 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. + * Gerhard Koerting : Show both timers + * + * 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 <net/ip.h> +#include <net/icmp.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <net/udp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/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; + int timer_active1; + int timer_active2; + unsigned long timer_expires; + 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_active1 = del_timer(&sp->retransmit_timer); + timer_active2 = del_timer(&sp->timer); + if (!timer_active1) sp->retransmit_timer.expires=0; + if (!timer_active2) sp->timer.expires=0; + timer_active=0; + timer_expires=(unsigned)-1; + if (timer_active1 && + sp->retransmit_timer.expires < timer_expires) { + timer_active=timer_active1; + timer_expires=sp->retransmit_timer.expires; + } + if (timer_active2 && + sp->timer.expires < timer_expires) { + timer_active=timer_active2; + timer_expires=sp->timer.expires; + } + 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, timer_expires, (unsigned) sp->retransmits, + sp->socket?SOCK_INODE(sp->socket)->i_uid:0, + timer_active?sp->timeout:0); + if (timer_active1) add_timer(&sp->retransmit_timer); + if (timer_active2) 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/net/ipv4/protocol.c b/net/ipv4/protocol.c new file mode 100644 index 000000000..f10cc8254 --- /dev/null +++ b/net/ipv4/protocol.c @@ -0,0 +1,200 @@ +/* + * 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. + * + * INET protocol dispatch tables. + * + * Version: @(#)protocol.c 1.0.5 05/25/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * Fixes: + * Alan Cox : Ahah! udp icmp errors don't work because + * udp_err is never called! + * Alan Cox : Added new fields for init and ready for + * proper fragmentation (_NO_ 4K limits!) + * + * 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/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/timer.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/icmp.h> +#include <net/udp.h> +#include <net/ipip.h> +#include <linux/igmp.h> + + +#ifdef CONFIG_IP_FORWARD +#ifdef CONFIG_NET_IPIP + +static struct inet_protocol ipip_protocol = { + ipip_rcv, /* IPIP handler */ + NULL, /* Will be UDP fraglist handler */ + NULL, /* TUNNEL error control */ + 0, /* next */ + IPPROTO_IPIP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "IPIP" /* name */ +}; + + +#endif +#endif + +static struct inet_protocol tcp_protocol = { + tcp_rcv, /* TCP handler */ + NULL, /* No fragment handler (and won't be for a long time) */ + tcp_err, /* TCP error control */ +#if defined(CONFIG_NET_IPIP) && defined(CONFIG_IP_FORWARD) + &ipip_protocol, +#else + NULL, /* next */ +#endif + IPPROTO_TCP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "TCP" /* name */ +}; + + +static struct inet_protocol udp_protocol = { + udp_rcv, /* UDP handler */ + NULL, /* Will be UDP fraglist handler */ + udp_err, /* UDP error control */ + &tcp_protocol, /* next */ + IPPROTO_UDP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "UDP" /* name */ +}; + + +static struct inet_protocol icmp_protocol = { + icmp_rcv, /* ICMP handler */ + NULL, /* ICMP never fragments anyway */ + NULL, /* ICMP error control */ + &udp_protocol, /* next */ + IPPROTO_ICMP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "ICMP" /* name */ +}; + +#ifndef CONFIG_IP_MULTICAST +struct inet_protocol *inet_protocol_base = &icmp_protocol; +#else +static struct inet_protocol igmp_protocol = { + igmp_rcv, /* IGMP handler */ + NULL, /* IGMP never fragments anyway */ + NULL, /* IGMP error control */ + &icmp_protocol, /* next */ + IPPROTO_IGMP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "IGMP" /* name */ +}; + +struct inet_protocol *inet_protocol_base = &igmp_protocol; +#endif + +struct inet_protocol *inet_protos[MAX_INET_PROTOS] = { + NULL +}; + + +struct inet_protocol * +inet_get_protocol(unsigned char prot) +{ + unsigned char hash; + struct inet_protocol *p; + + hash = prot & (MAX_INET_PROTOS - 1); + for (p = inet_protos[hash] ; p != NULL; p=p->next) { + if (p->protocol == prot) return((struct inet_protocol *) p); + } + return(NULL); +} + + +void +inet_add_protocol(struct inet_protocol *prot) +{ + unsigned char hash; + struct inet_protocol *p2; + + hash = prot->protocol & (MAX_INET_PROTOS - 1); + prot ->next = inet_protos[hash]; + inet_protos[hash] = prot; + prot->copy = 0; + + /* Set the copy bit if we need to. */ + p2 = (struct inet_protocol *) prot->next; + while(p2 != NULL) { + if (p2->protocol == prot->protocol) { + prot->copy = 1; + break; + } + p2 = (struct inet_protocol *) prot->next; + } +} + + +int +inet_del_protocol(struct inet_protocol *prot) +{ + struct inet_protocol *p; + struct inet_protocol *lp = NULL; + unsigned char hash; + + hash = prot->protocol & (MAX_INET_PROTOS - 1); + if (prot == inet_protos[hash]) { + inet_protos[hash] = (struct inet_protocol *) inet_protos[hash]->next; + return(0); + } + + p = (struct inet_protocol *) inet_protos[hash]; + while(p != NULL) { + /* + * We have to worry if the protocol being deleted is + * the last one on the list, then we may need to reset + * someone's copied bit. + */ + if (p->next != NULL && p->next == prot) { + /* + * if we are the last one with this protocol and + * there is a previous one, reset its copy bit. + */ + if (p->copy == 0 && lp != NULL) lp->copy = 0; + p->next = prot->next; + return(0); + } + + if (p->next != NULL && p->next->protocol == prot->protocol) { + lp = p; + } + + p = (struct inet_protocol *) p->next; + } + return(-1); +} diff --git a/net/ipv4/rarp.c b/net/ipv4/rarp.c new file mode 100644 index 000000000..a7b3719ed --- /dev/null +++ b/net/ipv4/rarp.c @@ -0,0 +1,491 @@ +/* linux/net/inet/rarp.c + * + * Copyright (C) 1994 by Ross Martin + * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche + * + * This module implements the Reverse Address Resolution Protocol + * (RARP, RFC 903), which is used to convert low level addresses such + * as ethernet addresses into high level addresses such as IP addresses. + * The most common use of RARP is as a means for a diskless workstation + * to discover its IP address during a network boot. + * + ** + *** WARNING:::::::::::::::::::::::::::::::::WARNING + **** + ***** SUN machines seem determined to boot solely from the person who + **** answered their RARP query. NEVER add a SUN to your RARP table + *** unless you have all the rest to boot the box from it. + ** + * + * Currently, only ethernet address -> IP address is likely to work. + * (Is RARP ever used for anything else?) + * + * This code 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/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.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 <asm/system.h> +#include <asm/segment.h> +#include <stdarg.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <net/ip.h> +#include <net/route.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/arp.h> +#include <net/rarp.h> +#ifdef CONFIG_AX25 +#include <net/ax25.h> +#endif + +#ifdef CONFIG_INET_RARP + +/* + * This structure defines the RARP mapping cache. As long as we make + * changes in this structure, we keep interrupts off. + */ + +struct rarp_table +{ + struct rarp_table *next; /* Linked entry list */ + unsigned long ip; /* ip address of entry */ + unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */ + unsigned char hlen; /* Length of hardware address */ + unsigned char htype; /* Type of hardware in use */ + struct device *dev; /* Device the entry is tied to */ +}; + +struct rarp_table *rarp_tables = NULL; + + +static struct packet_type rarp_packet_type = +{ + 0, /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */ + 0, /* copy */ + rarp_rcv, + NULL, + NULL +}; + +static initflag = 1; + +/* + * Called once when data first added to rarp cache with ioctl. + */ + +static void rarp_init (void) +{ + /* Register the packet type */ + rarp_packet_type.type=htons(ETH_P_RARP); + dev_add_pack(&rarp_packet_type); +} + +/* + * Release the memory for this entry. + */ + +static inline void rarp_release_entry(struct rarp_table *entry) +{ + kfree_s(entry, sizeof(struct rarp_table)); + return; +} + +/* + * Delete a RARP mapping entry in the cache. + */ + +static void rarp_destroy(unsigned long ip_addr) +{ + struct rarp_table *entry; + struct rarp_table **pentry; + + cli(); + pentry = &rarp_tables; + while ((entry = *pentry) != NULL) + { + if (entry->ip == ip_addr) + { + *pentry = entry->next; + sti(); + rarp_release_entry(entry); + return; + } + pentry = &entry->next; + } + sti(); +} + + +/* + * Receive an arp request by the device layer. Maybe it should be + * rewritten to use the incoming packet for the reply. The current + * "overhead" time isn't that high... + */ + +int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ +/* + * We shouldn't use this type conversion. Check later. + */ + struct arphdr *rarp = (struct arphdr *)skb->h.raw; + unsigned char *rarp_ptr = (unsigned char *)(rarp+1); + struct rarp_table *entry; + long sip,tip; + unsigned char *sha,*tha; /* s for "source", t for "target" */ + +/* + * If this test doesn't pass, it's not IP, or we should ignore it anyway + */ + + if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) + || dev->flags&IFF_NOARP) + { + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * If it's not a RARP request, delete it. + */ + if (rarp->ar_op != htons(ARPOP_RREQUEST)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * For now we will only deal with IP addresses. + */ + + if ( +#ifdef CONFIG_AX25 + (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) || +#endif + (rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) + || rarp->ar_pln != 4) + { + /* + * This packet is not for us. Remove it. + */ + kfree_skb(skb, FREE_READ); + return 0; +} + +/* + * Extract variable width fields + */ + + sha=rarp_ptr; + rarp_ptr+=dev->addr_len; + memcpy(&sip,rarp_ptr,4); + rarp_ptr+=4; + tha=rarp_ptr; + rarp_ptr+=dev->addr_len; + memcpy(&tip,rarp_ptr,4); + +/* + * Process entry. Use tha for table lookup according to RFC903. + */ + + cli(); + for (entry = rarp_tables; entry != NULL; entry = entry->next) + if (!memcmp(entry->ha, tha, rarp->ar_hln)) + break; + + if (entry != NULL) + { + sip=entry->ip; + sti(); + + arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, + dev->dev_addr); + } + else + sti(); + + kfree_skb(skb, FREE_READ); + return 0; +} + + +/* + * Set (create) a RARP cache entry. + */ + +static int rarp_req_set(struct arpreq *req) +{ + struct arpreq r; + struct rarp_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; + + switch (r.arp_ha.sa_family) + { + case ARPHRD_ETHER: + htype = ARPHRD_ETHER; + hlen = ETH_ALEN; + 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("RARP: SETRARP: 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? Find out... + */ + + cli(); + for (entry = rarp_tables; entry != NULL; entry = entry->next) + if (entry->ip == ip) + break; + +/* + * If no entry was found, create a new one. + */ + + if (entry == NULL) + { + entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table), + GFP_ATOMIC); + if (entry == NULL) + { + sti(); + return -ENOMEM; + } + if(initflag) + { + rarp_init(); + initflag=0; + } + + entry->next = rarp_tables; + rarp_tables = entry; + } + + entry->ip = ip; + entry->hlen = hlen; + entry->htype = htype; + memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); + entry->dev = rt->rt_dev; + + sti(); + + return 0; +} + + +/* + * Get a RARP cache entry. + */ + +static int rarp_req_get(struct arpreq *req) +{ + struct arpreq r; + struct rarp_table *entry; + struct sockaddr_in *si; + unsigned long ip; + +/* + * 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; + ip = si->sin_addr.s_addr; + + cli(); + for (entry = rarp_tables; entry != NULL; entry = entry->next) + if (entry->ip == ip) + break; + + 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; + sti(); + +/* + * Copy the information back + */ + + memcpy_tofs(req, &r, sizeof(r)); + return 0; +} + + +/* + * Handle a RARP layer I/O control request. + */ + +int rarp_ioctl(unsigned int cmd, void *arg) +{ + struct arpreq r; + struct sockaddr_in *si; + int err; + + switch(cmd) + { + case SIOCDRARP: + 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; + rarp_destroy(si->sin_addr.s_addr); + return 0; + + case SIOCGRARP: + err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); + if(err) + return err; + return rarp_req_get((struct arpreq *)arg); + case SIOCSRARP: + if (!suser()) + return -EPERM; + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); + if(err) + return err; + return rarp_req_set((struct arpreq *)arg); + default: + return -EINVAL; + } + + /*NOTREACHED*/ + return 0; +} + +int rarp_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 rarp_table *entry; + char ipbuffer[20]; + unsigned long netip; + if(initflag) + { + size = sprintf(buffer,"RARP disabled until entries added to cache.\n"); + pos+=size; + len+=size; + } + else + { + size = sprintf(buffer, + "IP address HW type HW address\n"); + pos+=size; + len+=size; + + cli(); + for(entry=rarp_tables; entry!=NULL; entry=entry->next) + { + netip=htonl(entry->ip); /* switch to network order */ + sprintf(ipbuffer,"%d.%d.%d.%d", + (unsigned int)(netip>>24)&255, + (unsigned int)(netip>>16)&255, + (unsigned int)(netip>>8)&255, + (unsigned int)(netip)&255); + + size = sprintf(buffer+len, + "%-17s%-20s%02x:%02x:%02x:%02x:%02x:%02x\n", + ipbuffer, + "10Mbps Ethernet", + (unsigned int)entry->ha[0], + (unsigned int)entry->ha[1], + (unsigned int)entry->ha[2], + (unsigned int)entry->ha[3], + (unsigned int)entry->ha[4], + (unsigned int)entry->ha[5]); + + 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; +} + +#endif diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c new file mode 100644 index 000000000..f01489b4a --- /dev/null +++ b/net/ipv4/raw.c @@ -0,0 +1,304 @@ +/* + * 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 <net/ip.h> +#include <net/protocol.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/icmp.h> +#include <net/udp.h> +#include <net/checksum.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. + */ + +/* + * Callback support is trivial for SOCK_RAW + */ + +static void raw_getfrag(void *p, int saddr, char *to, unsigned int offset, unsigned int fraglen) +{ + memcpy_fromfs(to, (unsigned char *)p+offset, fraglen); +} + +/* + * IPPROTO_RAW needs extra work. + */ + +static void raw_getrawfrag(void *p, int saddr, char *to, unsigned int offset, unsigned int fraglen) +{ + memcpy_fromfs(to, (unsigned char *)p+offset, fraglen); + if(offset==0) + { + struct iphdr *iph=(struct iphdr *)to; + iph->saddr=saddr; + iph->check=0; + iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl); + } +} + +static int raw_sendto(struct sock *sk, unsigned char *from, + int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) +{ + int err; + struct sockaddr_in sin; + + /* + * 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; + + if(sk->num==IPPROTO_RAW) + err=ip_build_xmit(sk, raw_getrawfrag, from, len, sin.sin_addr.s_addr, flags, sin.sin_port); + else + err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, flags, sin.sin_port); + return err<0?err: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, + "RAW", + 0, 0, + {NULL,} +}; diff --git a/net/ipv4/route.c b/net/ipv4/route.c new file mode 100644 index 000000000..d2186a45d --- /dev/null +++ b/net/ipv4/route.c @@ -0,0 +1,654 @@ +/* + * 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() + * Alan Cox : Routing cache support. + * Alan Cox : Removed compatibility cruft. + * Alan Cox : RTF_REJECT support. + * Alan Cox : TCP irtt 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. + */ + +#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 <net/ip.h> +#include <net/protocol.h> +#include <net/route.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/icmp.h> + +/* + * The routing table list + */ + +static struct rtable *rt_base = NULL; +unsigned long rt_stamp = 1; /* Routing table version stamp for caches ( 0 is 'unset' ) */ + +/* + * 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)); + } + rt_stamp++; /* New table revision */ + + 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)); + } + rt_stamp++; /* New table revision */ + 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, unsigned short irtt) +{ + 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; + if(rt->rt_flags & RTF_IRTT) + rt->rt_irtt = irtt; + + /* + * 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; + + rt_stamp++; /* New table revision */ + + /* + * 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, r->rt_irtt); + 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\tIRTT\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\t%u\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, (int)r->rt_irtt); + 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(rt->rt_flags&RTF_REJECT) + return NULL; + + 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; +} + +/* + * 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 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/net/ipv4/tcp.c b/net/ipv4/tcp.c new file mode 100644 index 000000000..352e1a101 --- /dev/null +++ b/net/ipv4/tcp.c @@ -0,0 +1,5169 @@ +/* + * 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@nvg.unit.no> + * Jorge Cwik, <jorge@laser.satlink.net> + * + * 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. + * Jorge Cwik : Even faster checksumming. + * Alan Cox : tcp_data() doesn't ack illegal PSH + * only frames. At least one pc tcp stack + * generates them. + * Alan Cox : Cache last socket. + * Alan Cox : Per route irtt. + * Matt Day : Select() match BSD precisely on error + * + * + * 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 <net/snmp.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/icmp.h> +#include <net/tcp.h> +#include <net/arp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/route.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <linux/mm.h> +#include <net/checksum.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; + +/* + * Cached last hit socket + */ + +volatile unsigned long th_cache_saddr,th_cache_daddr; +volatile unsigned short th_cache_dport, th_cache_sport; +volatile struct sock *th_cache_sk; + +void tcp_cache_zap(void) +{ + unsigned long flags; + save_flags(flags); + cli(); + th_cache_saddr=0; + th_cache_daddr=0; + th_cache_dport=0; + th_cache_sport=0; + th_cache_sk=NULL; + restore_flags(flags); +} + +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->err) + return 1; + 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->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. + * + * Modified January 1995 from a go-faster DOS routine by + * Jorge Cwik <jorge@laser.satlink.net> + */ + +unsigned short tcp_check(struct tcphdr *th, int len, + unsigned long saddr, unsigned long daddr) +{ + return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP, + csum_partial((char *)th,len,0)); +} + + + +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); + + tcp_cache_zap(); + + 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(th_cache_sk==sk) + tcp_cache_zap(); + 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_cache_zap(); + tcp_set_state(sk,TCP_SYN_SENT); + if(rt&&rt->rt_flags&RTF_IRTT) + sk->rto = rt->rt_irtt; + else + sk->rto = TCP_TIMEOUT_INIT; + 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; + + tcp_statistics.TcpInSegs++; + + if(skb->pkt_type!=PACKET_HOST) + { + kfree_skb(skb,FREE_READ); + return(0); + } + + th = skb->h.th; + + /* + * Find the socket, using the last hit cache if applicable. + */ + + if(saddr==th_cache_saddr && daddr==th_cache_daddr && th->dest==th_cache_dport && th->source==th_cache_sport) + sk=(struct sock *)th_cache_sk; + else + { + sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr); + th_cache_saddr=saddr; + th_cache_daddr=daddr; + th_cache_dport=th->dest; + th_cache_sport=th->source; + th_cache_sk=sk; + } + + /* + * 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,*skb; + 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; + } + + if (before(sk->sent_seq, sk->window_seq) && + (skb=skb_peek(&sk->write_queue))) + { + /* + * We are probing the opening of a window + * but the window size is != 0 + * must have been a result SWS advoidance ( sender ) + */ + + struct iphdr *iph; + struct tcphdr *th; + struct tcphdr *nth; + unsigned long win_size, ow_size; + void * tcp_data_start; + + win_size = sk->window_seq - sk->sent_seq; + + iph = (struct iphdr *)(skb->data + skb->dev->hard_header_len); + th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2)); + + buff = sk->prot->wmalloc(sk, win_size + th->doff * 4 + + (iph->ihl << 2) + + skb->dev->hard_header_len, + 1, GFP_ATOMIC); + if ( buff == NULL ) + return; + + buff->len = 0; + + /* + * If we strip the packet on the write queue we must + * be ready to retransmit this one + */ + + buff->free = 0; + + buff->sk = sk; + buff->localroute = sk->localroute; + + tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, buff->mem_len, + sk->ip_tos,sk->ip_ttl); + if (tmp < 0) + { + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + return; + } + + buff->len += tmp; + buff->dev = dev; + + nth = (struct tcphdr *) (buff->data + buff->len); + buff->len += th->doff * 4; + + memcpy(nth, th, th->doff * 4); + + nth->ack = 1; + nth->ack_seq = ntohl(sk->acked_seq); + nth->window = ntohs(tcp_select_window(sk)); + nth->check = 0; + + tcp_data_start = skb->data + skb->dev->hard_header_len + + (iph->ihl << 2) + th->doff * 4; + + memcpy(buff->data + buff->len, tcp_data_start, win_size); + buff->len += win_size; + buff->h.seq = sk->sent_seq + win_size; + + /* + * now: shrink the queue head segment + */ + + th->check = 0; + ow_size = skb->len - win_size - + ((unsigned long) (tcp_data_start - (void *) skb->data)); + + memmove(tcp_data_start, tcp_data_start + win_size, ow_size); + skb->len -= win_size; + sk->sent_seq += win_size; + th->seq = htonl(sk->sent_seq); + + if (th->urg) + { + unsigned short urg_ptr; + + urg_ptr = ntohs(th->urg_ptr); + if (urg_ptr <= win_size) + th->urg = 0; + else + { + urg_ptr -= win_size; + th->urg_ptr = htons(urg_ptr); + nth->urg_ptr = htons(win_size); + } + } + + tcp_send_check(nth, sk->saddr, sk->daddr, + nth->doff * 4 + win_size , sk); + } + else + { + 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. + */ + + 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, + "TCP", + 0, 0, + {NULL,} +}; diff --git a/net/ipv4/timer.c b/net/ipv4/timer.c new file mode 100644 index 000000000..ebaa00d70 --- /dev/null +++ b/net/ipv4/timer.c @@ -0,0 +1,163 @@ +/* + * 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. + * + * TIMER - implementation of software timers for IP. + * + * Version: @(#)timer.c 1.0.7 05/25/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> + * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de> + * Florian La Roche, <flla@stud.uni-sb.de> + * + * Fixes: + * Alan Cox : To avoid destroying a wait queue as we use it + * we defer destruction until the destroy timer goes + * off. + * Alan Cox : Destroy socket doesn't write a status value to the + * socket buffer _AFTER_ freeing it! Also sock ensures + * the socket will get removed BEFORE this is called + * otherwise if the timer TIME_DESTROY occurs inside + * of inet_bh() with this socket being handled it goes + * BOOM! Have to stop timer going off if net_bh is + * active or the destroy causes crashes. + * Alan Cox : Cleaned up unused code. + * + * 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/types.h> +#include <linux/errno.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <asm/system.h> +#include <linux/interrupt.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/arp.h> + +void delete_timer (struct sock *t) +{ + unsigned long flags; + + save_flags (flags); + cli(); + + t->timeout = 0; + del_timer (&t->timer); + + restore_flags (flags); +} + +void reset_timer (struct sock *t, int timeout, unsigned long len) +{ + delete_timer (t); + t->timeout = timeout; +#if 1 + /* FIXME: ??? */ + if ((int) len < 0) /* prevent close to infinite timers. THEY _DO_ */ + len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ +#endif + t->timer.expires = len; + add_timer (&t->timer); +} + + +/* + * Now we will only be called whenever we need to do + * something, but we must be sure to process all of the + * sockets that need it. + */ + +void net_timer (unsigned long data) +{ + struct sock *sk = (struct sock*)data; + int why = sk->timeout; + + /* + * only process if socket is not in use + */ + + cli(); + if (sk->inuse || in_bh) + { + sk->timer.expires = 10; + add_timer(&sk->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) + { + case TIME_DONE: + if (! sk->dead || sk->state != TCP_CLOSE) + { + printk ("non dead socket in time_done\n"); + release_sock (sk); + break; + } + destroy_sock (sk); + break; + + case TIME_DESTROY: + /* + * We've waited for a while for all the memory associated with + * the socket to be freed. + */ + if(sk->wmem_alloc!=0 || sk->rmem_alloc!=0) + { + sk->wmem_alloc++; /* So it DOESN'T go away */ + destroy_sock (sk); + sk->wmem_alloc--; /* Might now have hit 0 - fall through and do it again if so */ + sk->inuse = 0; /* This will be ok, the destroy won't totally work */ + } + if(sk->wmem_alloc==0 && sk->rmem_alloc==0) + destroy_sock(sk); /* Socket gone, DON'T update sk->inuse! */ + break; + case TIME_CLOSE: + /* We've waited long enough, close the socket. */ + sk->state = TCP_CLOSE; + delete_timer (sk); + /* Kill the ARP entry in case the hardware has changed. */ + arp_destroy (sk->daddr, 0); + if (!sk->dead) + sk->state_change(sk); + sk->shutdown = SHUTDOWN_MASK; + reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME); + release_sock (sk); + break; + default: + printk ("net_timer: timer expired - reason %d is unknown\n", why); + release_sock (sk); + break; + } +} + diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c new file mode 100644 index 000000000..9976e2be2 --- /dev/null +++ b/net/ipv4/udp.c @@ -0,0 +1,690 @@ +/* + * 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> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * Alan Cox, <Alan.Cox@linux.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. + * Arnt Gulbrandsen : New udp_send and stuff + * Alan Cox : Cache last socket + * Alan Cox : Route cache + * + * + * 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 <net/snmp.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/udp.h> +#include <net/icmp.h> +#include <net/route.h> +#include <net/checksum.h> + +/* + * SNMP MIB for the UDP layer + */ + +struct udp_mib udp_statistics; + +/* + * Cached last hit socket + */ + +volatile unsigned long uh_cache_saddr,uh_cache_daddr; +volatile unsigned short uh_cache_dport, uh_cache_sport; +volatile struct sock *uh_cache_sk; + +void udp_cache_zap(void) +{ + unsigned long flags; + save_flags(flags); + cli(); + uh_cache_saddr=0; + uh_cache_daddr=0; + uh_cache_dport=0; + uh_cache_sport=0; + uh_cache_sk=NULL; + restore_flags(flags); +} + +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. + */ + + if (icmp_err_convert[err & 0xff].fatal) + { + sk->err = icmp_err_convert[err & 0xff].errno; + sk->error_report(sk); + } +} + + +static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr) +{ + return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, + csum_partial((char*)uh, len, 0))); +} + +struct udpfakehdr +{ + struct udphdr uh; + int daddr; + int other; + char *from; + int wcheck; +}; + +/* + * Copy and checksum a UDP packet from user space into a buffer. We still have to do the planning to + * get ip_build_xmit to spot direct transfer to network card and provide an additional callback mode + * for direct user->board I/O transfers. That one will be fun. + */ + +static void udp_getfrag(void *p, int saddr, char * to, unsigned int offset, unsigned int fraglen) +{ + struct udpfakehdr *ufh = (struct udpfakehdr *)p; + char *src, *dst; + unsigned int len; + + if (offset) + { + len = fraglen; + src = ufh->from+(offset-sizeof(struct udphdr)); + dst = to; + } + else + { + len = fraglen-sizeof(struct udphdr); + src = ufh->from; + dst = to+sizeof(struct udphdr); + } + ufh->wcheck = csum_partial_copyffs(src, dst, len, ufh->wcheck); + if (offset == 0) + { + ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr), + ufh->wcheck); + ufh->uh.check = csum_tcpudp_magic(saddr, ufh->daddr, + ntohs(ufh->uh.len), + IPPROTO_UDP, ufh->wcheck); + if (ufh->uh.check == 0) + ufh->uh.check = -1; + memcpy(to, ufh, sizeof(struct udphdr)); + } +} + +/* + * Uncheckummed UDP is sufficiently criticial to stuff like ATM video conferencing + * that we use two routines for this for speed. Probably we ought to have a CONFIG_FAST_NET + * set for >10Mb/second boards to activate this sort of coding. Timing needed to verify if + * this is a valid decision. + */ + +static void udp_getfrag_nosum(void *p, int saddr, char * to, unsigned int offset, unsigned int fraglen) +{ + struct udpfakehdr *ufh = (struct udpfakehdr *)p; + char *src, *dst; + unsigned int len; + + if (offset) + { + len = fraglen; + src = ufh->from+(offset-sizeof(struct udphdr)); + dst = to; + } + else + { + len = fraglen-sizeof(struct udphdr); + src = ufh->from; + dst = to+sizeof(struct udphdr); + } + memcpy_fromfs(src,dst,len); + if (offset == 0) + memcpy(to, ufh, sizeof(struct udphdr)); +} + + +/* + * Send UDP frames. + */ + +static int udp_send(struct sock *sk, struct sockaddr_in *sin, + unsigned char *from, int len, int rt) +{ + int ulen = len + sizeof(struct udphdr); + int a; + struct udpfakehdr ufh; + + ufh.uh.source = sk->dummy_th.source; + ufh.uh.dest = sin->sin_port; + ufh.uh.len = htons(ulen); + ufh.uh.check = 0; + ufh.daddr = sin->sin_addr.s_addr; + ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256; + ufh.from = from; + ufh.wcheck = 0; + if(sk->no_check) + a = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen, + sin->sin_addr.s_addr, rt, IPPROTO_UDP); + else + a = ip_build_xmit(sk, udp_getfrag, &ufh, ulen, + sin->sin_addr.s_addr, rt, IPPROTO_UDP); + return(a<0 ? a : 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=(sk->localroute?ip_rt_local: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; + udp_cache_zap(); + sk->ip_route_cache = rt; + sk->ip_route_stamp = rt_stamp; + return(0); +} + + +static void udp_close(struct sock *sk, int timeout) +{ + sk->inuse = 1; + sk->state = TCP_CLOSE; + if(uh_cache_sk==sk) + udp_cache_zap(); + 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)) + { + NETDEBUG(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... */ + NETDEBUG(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 + if(saddr==uh_cache_saddr && daddr==uh_cache_daddr && uh->dest==uh_cache_dport && uh->source==uh_cache_sport) + sk=(struct sock *)uh_cache_sk; + else + { + sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr); + uh_cache_saddr=saddr; + uh_cache_daddr=daddr; + uh_cache_dport=uh->dest; + uh_cache_sport=uh->source; + uh_cache_sk=sk; + } + + 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, + "UDP", + 0, 0, + {NULL,} +}; + diff --git a/net/ipv4/utils.c b/net/ipv4/utils.c new file mode 100644 index 000000000..21ce570f5 --- /dev/null +++ b/net/ipv4/utils.c @@ -0,0 +1,91 @@ +/* + * 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. + * + * Various kernel-resident INET utility functions; mainly + * for format conversion and debugging output. + * + * Version: @(#)utils.c 1.0.7 05/18/93 + * + * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * Fixes: + * Alan Cox : verify_area check. + * Alan Cox : removed old debugging. + * + * + * 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/mm.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/stat.h> +#include <stdarg.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <linux/skbuff.h> + + +/* + * Display an IP address in readable format. + */ + +char *in_ntoa(unsigned long in) +{ + static char buff[18]; + char *p; + + p = (char *) ∈ + sprintf(buff, "%d.%d.%d.%d", + (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255)); + return(buff); +} + + +/* + * Convert an ASCII string to binary IP. + */ + +unsigned long in_aton(char *str) +{ + unsigned long l; + unsigned int val; + int i; + + l = 0; + for (i = 0; i < 4; i++) + { + l <<= 8; + if (*str != '\0') + { + val = 0; + while (*str != '\0' && *str != '.') + { + val *= 10; + val += *str - '0'; + str++; + } + l |= val; + if (*str != '\0') + str++; + } + } + return(htonl(l)); +} + |