summaryrefslogtreecommitdiff
path: root/pfinet
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
committerMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
commita81cd86c8d93236ffccfbee44b5818ba21523463 (patch)
treef098368d79e9ed9b39907730c28ed3182b69519d /pfinet
parentc7923f6aa252a29ccb4f16bd91469c9000a2bd94 (diff)
entered into RCS
Diffstat (limited to 'pfinet')
-rw-r--r--pfinet/linux-inet/arp.h18
-rw-r--r--pfinet/linux-inet/datagram.c210
-rw-r--r--pfinet/linux-inet/datalink.h17
-rw-r--r--pfinet/linux-inet/dev_mcast.c169
-rw-r--r--pfinet/linux-inet/devinet.c213
-rw-r--r--pfinet/linux-inet/eth.c196
-rw-r--r--pfinet/linux-inet/eth.h35
-rw-r--r--pfinet/linux-inet/icmp.c774
-rw-r--r--pfinet/linux-inet/icmp.h38
-rw-r--r--pfinet/linux-inet/ip.c2427
-rw-r--r--pfinet/linux-inet/ip.h130
-rw-r--r--pfinet/linux-inet/ip_fw.c1016
-rw-r--r--pfinet/linux-inet/ipx.c1947
-rw-r--r--pfinet/linux-inet/ipx.h84
-rw-r--r--pfinet/linux-inet/ipxcall.h2
-rw-r--r--pfinet/linux-inet/p8022.c98
-rw-r--r--pfinet/linux-inet/p8022.h2
-rw-r--r--pfinet/linux-inet/p8022call.h2
-rw-r--r--pfinet/linux-inet/p8023.c35
-rw-r--r--pfinet/linux-inet/packet.c410
-rw-r--r--pfinet/linux-inet/pe2.c35
-rw-r--r--pfinet/linux-inet/protocol.c177
-rw-r--r--pfinet/linux-inet/protocol.h59
-rw-r--r--pfinet/linux-inet/psnap.c123
-rw-r--r--pfinet/linux-inet/psnap.h2
-rw-r--r--pfinet/linux-inet/psnapcall.h2
-rw-r--r--pfinet/linux-inet/rarp.c491
-rw-r--r--pfinet/linux-inet/rarp.h14
-rw-r--r--pfinet/linux-inet/raw.h34
-rw-r--r--pfinet/linux-inet/route.h53
-rw-r--r--pfinet/linux-inet/skbuff.c573
-rw-r--r--pfinet/linux-inet/snmp.h107
-rw-r--r--pfinet/linux-inet/sock.c574
-rw-r--r--pfinet/linux-inet/tcp.h142
-rw-r--r--pfinet/linux-inet/timer.c264
-rw-r--r--pfinet/linux-inet/udp.h50
-rw-r--r--pfinet/linux-inet/utils.c91
37 files changed, 10614 insertions, 0 deletions
diff --git a/pfinet/linux-inet/arp.h b/pfinet/linux-inet/arp.h
new file mode 100644
index 00000000..a68adc30
--- /dev/null
+++ b/pfinet/linux-inet/arp.h
@@ -0,0 +1,18 @@
+/* linux/net/inet/arp.h */
+#ifndef _ARP_H
+#define _ARP_H
+
+extern void arp_init(void);
+extern void arp_destroy(unsigned long paddr, int force);
+extern void arp_device_down(struct device *dev);
+extern int arp_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern int arp_find(unsigned char *haddr, unsigned long paddr,
+ struct device *dev, unsigned long saddr, struct sk_buff *skb);
+extern int arp_get_info(char *buffer, char **start, off_t origin, int length);
+extern int arp_ioctl(unsigned int cmd, void *arg);
+extern 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);
+
+#endif /* _ARP_H */
diff --git a/pfinet/linux-inet/datagram.c b/pfinet/linux-inet/datagram.c
new file mode 100644
index 00000000..cd248cfb
--- /dev/null
+++ b/pfinet/linux-inet/datagram.c
@@ -0,0 +1,210 @@
+/*
+ * SUCS NET3:
+ *
+ * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
+ * of these would make sense. Not tonight however 8-).
+ * This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly
+ * identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
+ *
+ * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code)
+ *
+ * Fixes:
+ * Alan Cox : NULL return from skb_peek_copy() understood
+ * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
+ * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
+ * AX.25 now works right, and SPX is feasible.
+ * Alan Cox : Fixed write select of non IP protocol crash.
+ * Florian La Roche: Changed for my new skbuff handling.
+ *
+ * Note:
+ * A lot of this will change when the protocol/socket separation
+ * occurs. Using this will make things reasonably clean.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+
+
+/*
+ * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible
+ * races. This replaces identical code in packet,raw and udp, as well as the yet to
+ * be released IPX support. It also finally fixes the long standing peek and read
+ * race for datagram sockets. If you alter this routine remember it must be
+ * re-entrant.
+ */
+
+struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err)
+{
+ struct sk_buff *skb;
+ unsigned long intflags;
+
+ /* Socket is inuse - so the timer doesn't attack it */
+ save_flags(intflags);
+restart:
+ sk->inuse = 1;
+ while(skb_peek(&sk->receive_queue) == NULL) /* No data */
+ {
+ /* If we are shutdown then no more data is going to appear. We are done */
+ if (sk->shutdown & RCV_SHUTDOWN)
+ {
+ release_sock(sk);
+ *err=0;
+ return NULL;
+ }
+
+ if(sk->err)
+ {
+ release_sock(sk);
+ *err=-sk->err;
+ sk->err=0;
+ return NULL;
+ }
+
+ /* Sequenced packets can come disconnected. If so we report the problem */
+ if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED)
+ {
+ release_sock(sk);
+ *err=-ENOTCONN;
+ return NULL;
+ }
+
+ /* User doesn't want to wait */
+ if (noblock)
+ {
+ release_sock(sk);
+ *err=-EAGAIN;
+ return NULL;
+ }
+ release_sock(sk);
+
+ /* Interrupts off so that no packet arrives before we begin sleeping.
+ Otherwise we might miss our wake up */
+ cli();
+ if (skb_peek(&sk->receive_queue) == NULL)
+ {
+ interruptible_sleep_on(sk->sleep);
+ /* Signals may need a restart of the syscall */
+ if (current->signal & ~current->blocked)
+ {
+ restore_flags(intflags);;
+ *err=-ERESTARTSYS;
+ return(NULL);
+ }
+ if(sk->err != 0) /* Error while waiting for packet
+ eg an icmp sent earlier by the
+ peer has finally turned up now */
+ {
+ *err = -sk->err;
+ sk->err=0;
+ restore_flags(intflags);
+ return NULL;
+ }
+ }
+ sk->inuse = 1;
+ restore_flags(intflags);
+ }
+ /* Again only user level code calls this function, so nothing interrupt level
+ will suddenly eat the receive_queue */
+ if (!(flags & MSG_PEEK))
+ {
+ skb=skb_dequeue(&sk->receive_queue);
+ if(skb!=NULL)
+ skb->users++;
+ else
+ goto restart; /* Avoid race if someone beats us to the data */
+ }
+ else
+ {
+ cli();
+ skb=skb_peek(&sk->receive_queue);
+ if(skb!=NULL)
+ skb->users++;
+ restore_flags(intflags);
+ if(skb==NULL) /* shouldn't happen but .. */
+ *err=-EAGAIN;
+ }
+ return skb;
+}
+
+void skb_free_datagram(struct sk_buff *skb)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ skb->users--;
+ if(skb->users>0)
+ {
+ restore_flags(flags);
+ return;
+ }
+ /* See if it needs destroying */
+ if(!skb->next && !skb->prev) /* Been dequeued by someone - ie it's read */
+ kfree_skb(skb,FREE_READ);
+ restore_flags(flags);
+}
+
+void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
+{
+ /* We will know all about the fraglist options to allow >4K receives
+ but not this release */
+ memcpy_tofs(to,skb->h.raw+offset,size);
+}
+
+/*
+ * Datagram select: Again totally generic. Moved from udp.c
+ * Now does seqpacket.
+ */
+
+int datagram_select(struct sock *sk, int sel_type, select_table *wait)
+{
+ select_wait(sk->sleep, wait);
+ switch(sel_type)
+ {
+ case SEL_IN:
+ if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE)
+ {
+ /* Connection closed: Wake up */
+ return(1);
+ }
+ if (skb_peek(&sk->receive_queue) != NULL || sk->err != 0)
+ { /* This appears to be consistent
+ with other stacks */
+ return(1);
+ }
+ return(0);
+
+ case SEL_OUT:
+ if (sk->prot && sk->prot->wspace(sk) >= MIN_WRITE_SPACE)
+ {
+ return(1);
+ }
+ if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE)
+ {
+ return(1);
+ }
+ return(0);
+
+ case SEL_EX:
+ if (sk->err)
+ return(1); /* Socket has gone into error state (eg icmp error) */
+ return(0);
+ }
+ return(0);
+}
diff --git a/pfinet/linux-inet/datalink.h b/pfinet/linux-inet/datalink.h
new file mode 100644
index 00000000..34ae08da
--- /dev/null
+++ b/pfinet/linux-inet/datalink.h
@@ -0,0 +1,17 @@
+#ifndef _NET_INET_DATALINK_H_
+#define _NET_INET_DATALINK_H_
+
+struct datalink_proto {
+ unsigned short type_len;
+ unsigned char type[8];
+ char *string_name;
+ unsigned short header_length;
+ int (*rcvfunc)(struct sk_buff *, struct device *,
+ struct packet_type *);
+ void (*datalink_header)(struct datalink_proto *, struct sk_buff *,
+ unsigned char *);
+ struct datalink_proto *next;
+};
+
+#endif
+
diff --git a/pfinet/linux-inet/dev_mcast.c b/pfinet/linux-inet/dev_mcast.c
new file mode 100644
index 00000000..cd5e356e
--- /dev/null
+++ b/pfinet/linux-inet/dev_mcast.c
@@ -0,0 +1,169 @@
+/*
+ * Linux NET3: Multicast List maintenance.
+ *
+ * Authors:
+ * Tim Kordas <tjk@nostromo.eeap.cwru.edu>
+ * Richard Underwood <richard@wuzz.demon.co.uk>
+ *
+ * Stir fried together from the IP multicast and CAP patches above
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * Fixes:
+ * Alan Cox : Update the device on a real delete
+ * rather than any time but...
+ *
+ * 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 <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 "ip.h"
+#include "route.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+
+
+/*
+ * Device multicast list maintenance. This knows about such little matters as promiscuous mode and
+ * converting from the list to the array the drivers use. At least until I fix the drivers up.
+ *
+ * This is used both by IP and by the user level maintenance functions. Unlike BSD we maintain a usage count
+ * on a given multicast address so that a casual user application can add/delete multicasts used by protocols
+ * without doing damage to the protocols when it deletes the entries. It also helps IP as it tracks overlapping
+ * maps.
+ */
+
+
+/*
+ * Update the multicast list into the physical NIC controller.
+ */
+
+void dev_mc_upload(struct device *dev)
+{
+ struct dev_mc_list *dmi;
+ char *data, *tmp;
+
+ /* Don't do anything till we up the interface
+ [dev_open will call this function so the list will
+ stay sane] */
+
+ if(!(dev->flags&IFF_UP))
+ return;
+
+
+ /* Devices with no set multicast don't get set */
+ if(dev->set_multicast_list==NULL)
+ return;
+ /* Promiscuous is promiscuous - so no filter needed */
+ if(dev->flags&IFF_PROMISC)
+ {
+ dev->set_multicast_list(dev, -1, NULL);
+ return;
+ }
+
+ if(dev->mc_count==0)
+ {
+ dev->set_multicast_list(dev,0,NULL);
+ return;
+ }
+
+ data=kmalloc(dev->mc_count*dev->addr_len, GFP_KERNEL);
+ if(data==NULL)
+ {
+ printk("Unable to get memory to set multicast list on %s\n",dev->name);
+ return;
+ }
+ for(tmp = data, dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
+ {
+ memcpy(tmp,dmi->dmi_addr, dmi->dmi_addrlen);
+ tmp+=dev->addr_len;
+ }
+ dev->set_multicast_list(dev,dev->mc_count,data);
+ kfree(data);
+}
+
+/*
+ * Delete a device level multicast
+ */
+
+void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
+{
+ struct dev_mc_list **dmi;
+ for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
+ {
+ if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
+ {
+ struct dev_mc_list *tmp= *dmi;
+ if(--(*dmi)->dmi_users && !all)
+ return;
+ *dmi=(*dmi)->next;
+ dev->mc_count--;
+ kfree_s(tmp,sizeof(*tmp));
+ dev_mc_upload(dev);
+ return;
+ }
+ }
+}
+
+/*
+ * Add a device level multicast
+ */
+
+void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
+{
+ struct dev_mc_list *dmi;
+ for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
+ {
+ if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
+ {
+ if(!newonly)
+ dmi->dmi_users++;
+ return;
+ }
+ }
+ dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi),GFP_KERNEL);
+ if(dmi==NULL)
+ return; /* GFP_KERNEL so can't happen anyway */
+ memcpy(dmi->dmi_addr, addr, alen);
+ dmi->dmi_addrlen=alen;
+ dmi->next=dev->mc_list;
+ dmi->dmi_users=1;
+ dev->mc_list=dmi;
+ dev->mc_count++;
+ dev_mc_upload(dev);
+}
+
+/*
+ * Discard multicast list when a device is downed
+ */
+
+void dev_mc_discard(struct device *dev)
+{
+ while(dev->mc_list!=NULL)
+ {
+ struct dev_mc_list *tmp=dev->mc_list;
+ dev->mc_list=dev->mc_list->next;
+ kfree_s(tmp,sizeof(*tmp));
+ }
+ dev->mc_count=0;
+}
+
diff --git a/pfinet/linux-inet/devinet.c b/pfinet/linux-inet/devinet.c
new file mode 100644
index 00000000..946536be
--- /dev/null
+++ b/pfinet/linux-inet/devinet.c
@@ -0,0 +1,213 @@
+/*
+ * 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 "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "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.
+ */
+
+ 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/pfinet/linux-inet/eth.c b/pfinet/linux-inet/eth.c
new file mode 100644
index 00000000..cbd2c94b
--- /dev/null
+++ b/pfinet/linux-inet/eth.c
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ *
+ * Ethernet-type device handling.
+ *
+ * Version: @(#)eth.c 1.0.7 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>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *
+ * Fixes:
+ * Mr Linux : Arp problems
+ * Alan Cox : Generic queue tidyup (very tiny here)
+ * Alan Cox : eth_header ntohs should be htons
+ * Alan Cox : eth_rebuild_header missing an htons and
+ * minor other things.
+ * Tegge : Arp bug fixes.
+ * Florian : Removed many unnecessary functions, code cleanup
+ * and changes for new arp and skbuff.
+ * Alan Cox : Redid header building to reflect new format.
+ * Alan Cox : ARP only when compiled with CONFIG_INET
+ * Greg Page : 802.2 and SNAP stuff
+ *
+ * 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/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include "arp.h"
+
+void eth_setup(char *str, int *ints)
+{
+ struct device *d = dev_base;
+
+ if (!str || !*str)
+ return;
+ while (d)
+ {
+ if (!strcmp(str,d->name))
+ {
+ if (ints[0] > 0)
+ d->irq=ints[1];
+ if (ints[0] > 1)
+ d->base_addr=ints[2];
+ if (ints[0] > 2)
+ d->mem_start=ints[3];
+ if (ints[0] > 3)
+ d->mem_end=ints[4];
+ break;
+ }
+ d=d->next;
+ }
+}
+
+
+/*
+ * Create the Ethernet MAC header for an arbitrary protocol layer
+ *
+ * saddr=NULL means use device source address
+ * daddr=NULL means leave destination address (eg unresolved arp)
+ */
+
+int eth_header(unsigned char *buff, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len,
+ struct sk_buff *skb)
+{
+ struct ethhdr *eth = (struct ethhdr *)buff;
+
+ /*
+ * Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+ * in here instead. It is up to the 802.2 layer to carry protocol information.
+ */
+
+ if(type!=ETH_P_802_3)
+ eth->h_proto = htons(type);
+ else
+ eth->h_proto = htons(len);
+
+ /*
+ * Set the source hardware address.
+ */
+
+ if(saddr)
+ memcpy(eth->h_source,saddr,dev->addr_len);
+ else
+ memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+ /*
+ * Anyway, the loopback-device should never use this function...
+ */
+
+ if (dev->flags & IFF_LOOPBACK)
+ {
+ memset(eth->h_dest, 0, dev->addr_len);
+ return(dev->hard_header_len);
+ }
+
+ if(daddr)
+ {
+ memcpy(eth->h_dest,daddr,dev->addr_len);
+ return dev->hard_header_len;
+ }
+
+ return -dev->hard_header_len;
+}
+
+
+/*
+ * Rebuild the Ethernet MAC header. This is called after an ARP
+ * (or in future other address resolution) has completed on this
+ * sk_buff. We now let ARP fill in the other fields.
+ */
+
+int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+ struct sk_buff *skb)
+{
+ struct ethhdr *eth = (struct ethhdr *)buff;
+
+ /*
+ * Only ARP/IP is currently supported
+ */
+
+ if(eth->h_proto != htons(ETH_P_IP))
+ {
+ printk("eth_rebuild_header: Don't know how to resolve type %d addresses?\n",(int)eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
+ }
+
+ /*
+ * Try and get ARP to resolve the header.
+ */
+#ifdef CONFIG_INET
+ return arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
+{
+ struct ethhdr *eth = (struct ethhdr *) skb->data;
+ unsigned char *rawp;
+
+ if(*eth->h_dest&1)
+ {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = (unsigned char *)(eth + 1);
+
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ return htons(ETH_P_802_2);
+}
diff --git a/pfinet/linux-inet/eth.h b/pfinet/linux-inet/eth.h
new file mode 100644
index 00000000..f8fed44e
--- /dev/null
+++ b/pfinet/linux-inet/eth.h
@@ -0,0 +1,35 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Ethernet handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.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.
+ */
+#ifndef _ETH_H
+#define _ETH_H
+
+
+#include <linux/if_ether.h>
+
+
+extern char *eth_print(unsigned char *ptr);
+extern void eth_dump(struct ethhdr *eth);
+extern int eth_header(unsigned char *buff, struct device *dev,
+ unsigned short type, unsigned long daddr,
+ unsigned long saddr, unsigned len);
+extern int eth_rebuild_header(void *buff, struct device *dev);
+extern void eth_add_arp(unsigned long addr, struct sk_buff *skb,
+ struct device *dev);
+extern unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev);
+
+#endif /* _ETH_H */
diff --git a/pfinet/linux-inet/icmp.c b/pfinet/linux-inet/icmp.c
new file mode 100644
index 00000000..c023eab2
--- /dev/null
+++ b/pfinet/linux-inet/icmp.c
@@ -0,0 +1,774 @@
+/*
+ * 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().
+ *
+ *
+ *
+ * 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 "snmp.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "icmp.h"
+#include "tcp.h"
+#include "snmp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.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
+ *
+ * Fixme: Fragment handling is wrong really.
+ */
+
+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:
+ printk("ICMP: %s:%d: protocol unreachable.\n",
+ in_ntoa(iph->daddr), ntohs(iph->protocol));
+ break;
+ case ICMP_PORT_UNREACH:
+ break;
+ case ICMP_FRAG_NEEDED:
+ printk("ICMP: %s: fragmentation needed and DF set.\n",
+ in_ntoa(iph->daddr));
+ break;
+ case ICMP_SR_FAILED:
+ printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+ 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;
+
+ 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 || ip_chk_addr(icmph->un.gateway))
+ break;
+ printk("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);
+ break;
+ case ICMP_REDIR_NETTOS:
+ case ICMP_REDIR_HOSTTOS:
+ printk("ICMP: cannot handle TOS redirects yet!\n");
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * 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++;
+#if 1
+ /* correct answers are possible for everything >= 12 */
+ if (len < 12)
+#endif
+ 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/pfinet/linux-inet/icmp.h b/pfinet/linux-inet/icmp.h
new file mode 100644
index 00000000..8f1c3498
--- /dev/null
+++ b/pfinet/linux-inet/icmp.h
@@ -0,0 +1,38 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ICMP module.
+ *
+ * Version: @(#)icmp.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.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.
+ */
+#ifndef _ICMP_H
+#define _ICMP_H
+
+#include <linux/icmp.h>
+
+
+extern struct icmp_err icmp_err_convert[];
+extern struct icmp_mib icmp_statistics;
+
+
+extern void icmp_send(struct sk_buff *skb_in, int type, int code,
+ unsigned long info, struct device *dev);
+extern 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);
+
+extern int icmp_ioctl(struct sock *sk, int cmd,
+ unsigned long arg);
+
+#endif /* _ICMP_H */
diff --git a/pfinet/linux-inet/ip.c b/pfinet/linux-inet/ip.c
new file mode 100644
index 00000000..dd188f54
--- /dev/null
+++ b/pfinet/linux-inet/ip.c
@@ -0,0 +1,2427 @@
+/*
+ * 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>
+ *
+ *
+ * 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
+ * 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
+ * Alan Cox : Only send ICMP_REDIRECT if src/dest are the same net.
+ *
+ *
+ * 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.
+ *
+ * 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 "snmp.h"
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+#include "icmp.h"
+#include "raw.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);
+ }
+}
+
+
+/* these two routines will do routing. */
+
+static void
+strict_route(struct iphdr *iph, struct options *opt)
+{
+}
+
+
+static void
+loose_route(struct iphdr *iph, struct options *opt)
+{
+}
+
+
+
+
+/* This routine will check to see if we have lost a gateway. */
+void
+ip_route_check(unsigned long daddr)
+{
+}
+
+
+#if 0
+/* this routine puts the options at the end of an ip header. */
+static int
+build_options(struct iphdr *iph, struct options *opt)
+{
+ unsigned char *ptr;
+ /* currently we don't support any options. */
+ ptr = (unsigned char *)(iph+1);
+ *ptr = 0;
+ return (4);
+}
+#endif
+
+
+/*
+ * 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)
+{
+ static struct options optmem;
+ struct iphdr *iph;
+ struct rtable *rt;
+ unsigned char *buff;
+ unsigned long raddr;
+ int tmp;
+ unsigned long src;
+
+ 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, &optmem, &src);
+ else
+ rt = ip_rt_route(daddr, &optmem, &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;
+
+ opt = &optmem;
+ }
+ else
+ {
+ /*
+ * We still need the address of the first hop.
+ */
+ if(skb->localroute)
+ rt = ip_rt_local(daddr, &optmem, &src);
+ else
+ rt = ip_rt_route(daddr, &optmem, &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);
+
+ 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;
+
+ /* Setup the IP options. */
+#ifdef Not_Yet_Avail
+ build_options(iph, opt);
+#endif
+
+ return(20 + tmp); /* IP header plus MAC header size */
+}
+
+
+static int
+do_options(struct iphdr *iph, struct options *opt)
+{
+ unsigned char *buff;
+ int done = 0;
+ int i, len = sizeof(struct iphdr);
+
+ /* Zero out the options. */
+ opt->record_route.route_size = 0;
+ opt->loose_route.route_size = 0;
+ opt->strict_route.route_size = 0;
+ opt->tstamp.ptr = 0;
+ opt->security = 0;
+ opt->compartment = 0;
+ opt->handling = 0;
+ opt->stream = 0;
+ opt->tcc = 0;
+ return(0);
+
+ /* Advance the pointer to start at the options. */
+ buff = (unsigned char *)(iph + 1);
+
+ /* Now start the processing. */
+ while (!done && len < iph->ihl*4) switch(*buff) {
+ case IPOPT_END:
+ done = 1;
+ break;
+ case IPOPT_NOOP:
+ buff++;
+ len++;
+ break;
+ case IPOPT_SEC:
+ buff++;
+ if (*buff != 11) return(1);
+ buff++;
+ opt->security = ntohs(*(unsigned short *)buff);
+ buff += 2;
+ opt->compartment = ntohs(*(unsigned short *)buff);
+ buff += 2;
+ opt->handling = ntohs(*(unsigned short *)buff);
+ buff += 2;
+ opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1));
+ buff += 3;
+ len += 11;
+ break;
+ case IPOPT_LSRR:
+ buff++;
+ if ((*buff - 3)% 4 != 0) return(1);
+ len += *buff;
+ opt->loose_route.route_size = (*buff -3)/4;
+ buff++;
+ if (*buff % 4 != 0) return(1);
+ opt->loose_route.pointer = *buff/4 - 1;
+ buff++;
+ buff++;
+ for (i = 0; i < opt->loose_route.route_size; i++) {
+ if(i>=MAX_ROUTE)
+ return(1);
+ opt->loose_route.route[i] = *(unsigned long *)buff;
+ buff += 4;
+ }
+ break;
+ case IPOPT_SSRR:
+ buff++;
+ if ((*buff - 3)% 4 != 0) return(1);
+ len += *buff;
+ opt->strict_route.route_size = (*buff -3)/4;
+ buff++;
+ if (*buff % 4 != 0) return(1);
+ opt->strict_route.pointer = *buff/4 - 1;
+ buff++;
+ buff++;
+ for (i = 0; i < opt->strict_route.route_size; i++) {
+ if(i>=MAX_ROUTE)
+ return(1);
+ opt->strict_route.route[i] = *(unsigned long *)buff;
+ buff += 4;
+ }
+ break;
+ case IPOPT_RR:
+ buff++;
+ if ((*buff - 3)% 4 != 0) return(1);
+ len += *buff;
+ opt->record_route.route_size = (*buff -3)/4;
+ buff++;
+ if (*buff % 4 != 0) return(1);
+ opt->record_route.pointer = *buff/4 - 1;
+ buff++;
+ buff++;
+ for (i = 0; i < opt->record_route.route_size; i++) {
+ if(i>=MAX_ROUTE)
+ return 1;
+ opt->record_route.route[i] = *(unsigned long *)buff;
+ buff += 4;
+ }
+ break;
+ case IPOPT_SID:
+ len += 4;
+ buff +=2;
+ opt->stream = *(unsigned short *)buff;
+ buff += 2;
+ break;
+ case IPOPT_TIMESTAMP:
+ buff++;
+ len += *buff;
+ if (*buff % 4 != 0) return(1);
+ opt->tstamp.len = *buff / 4 - 1;
+ buff++;
+ if ((*buff - 1) % 4 != 0) return(1);
+ opt->tstamp.ptr = (*buff-1)/4;
+ buff++;
+ opt->tstamp.x.full_char = *buff;
+ buff++;
+ for (i = 0; i < opt->tstamp.len; i++) {
+ opt->tstamp.data[i] = *(unsigned long *)buff;
+ buff += 4;
+ }
+ break;
+ default:
+ return(1);
+ }
+
+ if (opt->record_route.route_size == 0) {
+ if (opt->strict_route.route_size != 0) {
+ memcpy(&(opt->record_route), &(opt->strict_route),
+ sizeof(opt->record_route));
+ } else if (opt->loose_route.route_size != 0) {
+ memcpy(&(opt->record_route), &(opt->loose_route),
+ sizeof(opt->record_route));
+ }
+ }
+
+ if (opt->strict_route.route_size != 0 &&
+ opt->strict_route.route_size != opt->strict_route.pointer) {
+ strict_route(iph, opt);
+ return(0);
+ }
+
+ if (opt->loose_route.route_size != 0 &&
+ opt->loose_route.route_size != opt->loose_route.pointer) {
+ loose_route(iph, opt);
+ return(0);
+ }
+
+ return(0);
+}
+
+/*
+ * This routine does all the checksum computations that don't
+ * require anything special (like copying or special headers).
+ */
+
+unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ unsigned long sum = 0;
+
+ /* Do the first multiple of 4 bytes and convert to 16 bits. */
+ if (len > 3)
+ {
+ __asm__("clc\n"
+ "1:\t"
+ "lodsl\n\t"
+ "adcl %%eax, %%ebx\n\t"
+ "loop 1b\n\t"
+ "adcl $0, %%ebx\n\t"
+ "movl %%ebx, %%eax\n\t"
+ "shrl $16, %%eax\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum) , "=S" (buff)
+ : "0" (sum), "c" (len >> 2) ,"1" (buff)
+ : "ax", "cx", "si", "bx" );
+ }
+ if (len & 2)
+ {
+ __asm__("lodsw\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum), "=S" (buff)
+ : "0" (sum), "1" (buff)
+ : "bx", "ax", "si");
+ }
+ if (len & 1)
+ {
+ __asm__("lodsb\n\t"
+ "movb $0, %%ah\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum), "=S" (buff)
+ : "0" (sum), "1" (buff)
+ : "bx", "ax", "si");
+ }
+ sum =~sum;
+ return(sum & 0xffff);
+}
+
+/*
+ * 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)
+ {
+ 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, qp->ihlen + 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)
+ {
+ 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)
+ {
+ 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(ihlen + 8, GFP_ATOMIC);
+ if (qp->iph == NULL)
+ {
+ 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++;
+ 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)
+ {
+ 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;
+ 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(; next != NULL; next = tfp)
+ {
+ tfp = next->next;
+ if (next->offset >= end)
+ break; /* no overlaps at all */
+
+ i = end - next->offset; /* overlap is 'i' bytes */
+ next->len -= i; /* so reduce size of */
+ next->offset += i; /* next fragment */
+ next->ptr += i;
+
+ /*
+ * If we get a frag size of <= 0, remove it and the packet
+ * that it goes with.
+ */
+ if (next->len <= 0)
+ {
+ if (next->prev != NULL)
+ next->prev->next = next->next;
+ else
+ qp->fragments = next->next;
+
+ if (tfp->next != NULL)
+ next->next->prev = next->prev;
+
+ kfree_skb(next->skb,FREE_READ);
+ kfree_s(next, 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)
+ {
+ 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.
+ */
+
+static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
+{
+ 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 */
+
+ /*
+ * See if we are allowed to forward this.
+ */
+
+#ifdef CONFIG_IP_FIREWALL
+ int err;
+
+ if((err=ip_fw_chk(skb->h.iph, dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0))!=1)
+ {
+ if(err==-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--;
+ if (iph->ttl <= 0)
+ {
+ /* Tell the sender its packet died... */
+ icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
+ return;
+ }
+
+ /*
+ * 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
+ */
+
+ ip_send_check(iph);
+
+ /*
+ * OK, the packet is still valid. Fetch its destination address,
+ * and give it to the IP sender for further processing.
+ */
+
+ rt = ip_rt_route(iph->daddr, 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)
+ {
+ /*
+ * 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 = iph->daddr;
+
+ /*
+ * 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.
+ */
+#ifdef CONFIG_IP_NO_ICMP_REDIRECT
+ if (dev == dev2)
+ return;
+#else
+ if (dev == dev2 && (iph->saddr&dev->pa_mask) == (iph->daddr & dev->pa_mask))
+ 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)
+ {
+
+ /*
+ * 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)
+ {
+ 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_acct_cnt(iph,dev,ip_acct_chain);
+#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;
+ unsigned char opts_p = 0; /* Set iff the packet has options. */
+ struct inet_protocol *ipprot;
+ static struct options opt; /* since we don't use these yet, and they
+ take up stack space. */
+ int brd=IS_MYADDR;
+ 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;
+
+ /*
+ * 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. 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)
+ {
+ ip_statistics.IpInHdrErrors++;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ /*
+ * 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
+
+ /*
+ * 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);
+
+ /*
+ * Next analyse the packet for options. Studies show under one packet in
+ * a thousand have options....
+ */
+
+ if (iph->ihl != 5)
+ { /* Fast path for the typical optionless IP packet. */
+ memset((char *) &opt, 0, sizeof(opt));
+ if (do_options(iph, &opt) != 0)
+ return 0;
+ opts_p = 1;
+ }
+
+ /*
+ * 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)
+ {
+ /*
+ * 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);
+#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);
+ }
+
+#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
+ /*
+ * Account for the packet
+ */
+
+#ifdef CONFIG_IP_ACCT
+ ip_acct_cnt(iph,dev, ip_acct_chain);
+#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.
+ */
+
+ 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, opts_p ? &opt : 0, 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);
+}
+
+/*
+ * 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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_acct_cnt(iph,dev, ip_acct_chain);
+#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.
+ */
+
+int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
+{
+ int val,err;
+#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);
+
+ 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:
+ {
+ unsigned char ucval;
+
+ ucval=get_fs_byte((unsigned char *)optval);
+ if(ucval<1||ucval>255)
+ return -EINVAL;
+ sk->ip_mc_ttl=(int)ucval;
+ return 0;
+ }
+ case IP_MULTICAST_LOOP:
+ {
+ unsigned char ucval;
+
+ ucval=get_fs_byte((unsigned char *)optval);
+ if(ucval!=0 && ucval!=1)
+ return -EINVAL;
+ sk->ip_mc_loop=(int)ucval;
+ return 0;
+ }
+ case IP_MULTICAST_IF:
+ {
+ /* Not fully tested */
+ 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));
+
+ printk("MC bind %s\n", in_ntoa(addr.s_addr));
+
+ /*
+ * What address has been requested
+ */
+
+ if(addr.s_addr==INADDR_ANY) /* Default */
+ {
+ sk->ip_mc_name[0]=0;
+ return 0;
+ }
+
+ /*
+ * Find the device
+ */
+
+ for(dev = dev_base; dev; dev = dev->next)
+ {
+ if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
+ (dev->pa_addr==addr.s_addr))
+ break;
+ }
+
+ /*
+ * 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;
+ static struct options optmem;
+ 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,&optmem, &route_src))!=NULL)
+ {
+ dev=rt->rt_dev;
+ rt->rt_use--;
+ }
+ }
+ else
+ {
+ /*
+ * Find a suitable device.
+ */
+ for(dev = dev_base; dev; dev = dev->next)
+ {
+ if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
+ (dev->pa_addr==mreq.imr_interface.s_addr))
+ break;
+ }
+ }
+
+ /*
+ * 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;
+ static struct options optmem;
+ 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,&optmem, &route_src))!=NULL)
+ {
+ dev=rt->rt_dev;
+ rt->rt_use--;
+ }
+ }
+ else
+ {
+ for(dev = dev_base; dev; dev = dev->next)
+ {
+ if((dev->flags&IFF_UP)&& (dev->flags&IFF_MULTICAST)&&
+ (dev->pa_addr==mreq.imr_interface.s_addr))
+ break;
+ }
+ }
+
+ /*
+ * 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);
+}
+
+/*
+ * 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/pfinet/linux-inet/ip.h b/pfinet/linux-inet/ip.h
new file mode 100644
index 00000000..95954a8c
--- /dev/null
+++ b/pfinet/linux-inet/ip.h
@@ -0,0 +1,130 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP module.
+ *
+ * Version: @(#)ip.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.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.
+ */
+#ifndef _IP_H
+#define _IP_H
+
+
+#include <linux/ip.h>
+#include <linux/config.h>
+
+#ifndef _SNMP_H
+#include "snmp.h"
+#endif
+
+#include "sock.h" /* struct sock */
+
+/* IP flags. */
+#define IP_CE 0x8000 /* Flag: "Congestion" */
+#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
+#define IP_MF 0x2000 /* Flag: "More Fragments" */
+#define IP_OFFSET 0x1FFF /* "Fragment Offset" part */
+
+#define IP_FRAG_TIME (30 * HZ) /* fragment lifetime */
+
+#ifdef CONFIG_IP_MULTICAST
+extern void ip_mc_dropsocket(struct sock *);
+extern void ip_mc_dropdevice(struct device *dev);
+extern int ip_mc_procinfo(char *, char **, off_t, int);
+#define MULTICAST(x) (IN_MULTICAST(htonl(x)))
+#endif
+
+
+/* Describe an IP fragment. */
+struct ipfrag {
+ int offset; /* offset of fragment in IP datagram */
+ int end; /* last byte of data in datagram */
+ int len; /* length of this fragment */
+ struct sk_buff *skb; /* complete received fragment */
+ unsigned char *ptr; /* pointer into real fragment data */
+ struct ipfrag *next; /* linked list pointers */
+ struct ipfrag *prev;
+};
+
+/* Describe an entry in the "incomplete datagrams" queue. */
+struct ipq {
+ unsigned char *mac; /* pointer to MAC header */
+ struct iphdr *iph; /* pointer to IP header */
+ int len; /* total length of original datagram */
+ short ihlen; /* length of the IP header */
+ short maclen; /* length of the MAC header */
+ struct timer_list timer; /* when will this queue expire? */
+ struct ipfrag *fragments; /* linked list of received fragments */
+ struct ipq *next; /* linked list pointers */
+ struct ipq *prev;
+ struct device *dev; /* Device - for icmp replies */
+};
+
+
+extern int backoff(int n);
+
+extern void ip_print(const struct iphdr *ip);
+extern int ip_ioctl(struct sock *sk, int cmd,
+ unsigned long arg);
+extern void ip_route_check(unsigned long daddr);
+extern 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);
+extern unsigned short ip_compute_csum(unsigned char * buff, int len);
+extern int ip_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern void ip_send_check(struct iphdr *ip);
+extern int ip_id_count;
+extern void ip_queue_xmit(struct sock *sk,
+ struct device *dev, struct sk_buff *skb,
+ int free);
+extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
+extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+extern void ip_init(void);
+
+extern struct ip_mib ip_statistics;
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers, which
+ * always checksum on 4 octet boundaries.
+ * Used by ip.c and slhc.c (the net driver module)
+ * (Moved to here by bj0rn@blox.se)
+ */
+
+static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
+{
+ unsigned long sum = 0;
+
+ if (wlen)
+ {
+ unsigned long bogus;
+ __asm__("clc\n"
+ "1:\t"
+ "lodsl\n\t"
+ "adcl %3, %0\n\t"
+ "decl %2\n\t"
+ "jne 1b\n\t"
+ "adcl $0, %0\n\t"
+ "movl %0, %3\n\t"
+ "shrl $16, %3\n\t"
+ "addw %w3, %w0\n\t"
+ "adcw $0, %w0"
+ : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus)
+ : "0" (sum), "1" (buff), "2" (wlen));
+ }
+ return (~sum) & 0xffff;
+}
+#endif /* _IP_H */
diff --git a/pfinet/linux-inet/ip_fw.c b/pfinet/linux-inet/ip_fw.c
new file mode 100644
index 00000000..0572c8f1
--- /dev/null
+++ b/pfinet/linux-inet/ip_fw.c
@@ -0,0 +1,1016 @@
+/*
+ * 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.
+ *
+ * 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 "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "icmp.h"
+#include <linux/ip_fw.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) printf("%d.%d.%d.%d",(ntohl(a.s_addr)>>24)&0xFF,\
+ (ntohl(a.s_addr)>>16)&0xFF,\
+ (ntohl(a.s_addr)>>8)&0xFF,\
+ (ntohl(a.s_addr))&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
+
+
+/*
+ * 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;
+ }
+ 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");
+
+ 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 1;
+ if(f_flag&IP_FW_F_ICMPRPL)
+ return -1;
+ return 0;
+}
+
+
+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
+ printf("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",m->m_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 n_src_p=%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 n_dst_p=%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
+
+void ip_acct_cnt(struct iphdr *iph, struct device *dev, struct ip_fw *f)
+{
+ (void) ip_fw_chk(iph, dev, f, 0, 1);
+ return;
+}
+
+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
+ printf("ip_acct_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("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
+ printf("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
+ sizeof(struct ip)/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
+ printf("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 %lu %lu",
+ 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
diff --git a/pfinet/linux-inet/ipx.c b/pfinet/linux-inet/ipx.c
new file mode 100644
index 00000000..88b53c30
--- /dev/null
+++ b/pfinet/linux-inet/ipx.c
@@ -0,0 +1,1947 @@
+/*
+ * Implements an IPX socket layer (badly - but I'm working on it).
+ *
+ * This code is derived from work by
+ * Ross Biro : Writing the original IP stack
+ * Fred Van Kempen : Tidying up the TCP/IP
+ *
+ * Many thanks go to Keith Baker, Institute For Industrial Information
+ * Technology Ltd, Swansea University for allowing me to work on this
+ * in my own time even though it was in some ways related to commercial
+ * work I am currently employed to do there.
+ *
+ * All the material in this file is subject to the Gnu license version 2.
+ * Neither Alan Cox nor the Swansea University Computer Society admit liability
+ * nor provide warranty for any of this software. This material is provided
+ * as is and at no charge.
+ *
+ * Revision 0.21: Uses the new generic socket option code.
+ * Revision 0.22: Gcc clean ups and drop out device registration. Use the
+ * new multi-protocol edition of hard_header
+ * Revision 0.23: IPX /proc by Mark Evans.
+ * Adding a route will overwrite any existing route to the same
+ * network.
+ * Revision 0.24: Supports new /proc with no 4K limit
+ * Revision 0.25: Add ephemeral sockets, passive local network
+ * identification, support for local net 0 and
+ * multiple datalinks <Greg Page>
+ * Revision 0.26: Device drop kills IPX routes via it. (needed for modules)
+ * Revision 0.27: Autobind <Mark Evans>
+ * Revision 0.28: Small fix for multiple local networks <Thomas Winder>
+ * Revision 0.29: Assorted major errors removed <Mark Evans>
+ * Small correction to promisc mode error fix <Alan Cox>
+ * Asynchronous I/O support.
+ * Changed to use notifiers and the newer packet_type stuff.
+ * Assorted major fixes <Alejandro Liu>
+ *
+ * Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
+ * Neither Greg Page nor Caldera, Inc. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ */
+
+#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/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/ipx.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "sock.h"
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <linux/interrupt.h>
+#include "p8022.h"
+#include "psnap.h"
+
+#ifdef CONFIG_IPX
+/* Configuration Variables */
+static unsigned char ipxcfg_max_hops = 16;
+static char ipxcfg_auto_select_primary = 0;
+static char ipxcfg_auto_create_interfaces = 0;
+
+/* Global Variables */
+static struct datalink_proto *p8022_datalink = NULL;
+static struct datalink_proto *pEII_datalink = NULL;
+static struct datalink_proto *p8023_datalink = NULL;
+static struct datalink_proto *pSNAP_datalink = NULL;
+
+static ipx_interface *ipx_interfaces = NULL;
+static ipx_route *ipx_routes = NULL;
+static ipx_interface *ipx_internal_net = NULL;
+static ipx_interface *ipx_primary_net = NULL;
+
+static int
+ipxcfg_set_auto_create(char val)
+{
+ ipxcfg_auto_create_interfaces = val;
+ return 0;
+}
+
+static int
+ipxcfg_set_auto_select(char val)
+{
+ ipxcfg_auto_select_primary = val;
+ if (val && (ipx_primary_net == NULL))
+ ipx_primary_net = ipx_interfaces;
+ return 0;
+}
+
+static int
+ipxcfg_get_config_data(ipx_config_data *arg)
+{
+ ipx_config_data vals;
+
+ vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;
+ vals.ipxcfg_auto_select_primary = ipxcfg_auto_select_primary;
+ memcpy_tofs(arg, &vals, sizeof(vals));
+ return 0;
+}
+
+
+/***********************************************************************************************************************\
+* *
+* Handlers for the socket list. *
+* *
+\***********************************************************************************************************************/
+
+/*
+ * Note: Sockets may not be removed _during_ an interrupt or inet_bh
+ * handler using this technique. They can be added although we do not
+ * use this facility.
+ */
+
+static void
+ipx_remove_socket(ipx_socket *sk)
+{
+ ipx_socket *s;
+ ipx_interface *intrfc;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ /* Determine interface with which socket is associated */
+ intrfc = sk->ipx_intrfc;
+ if (intrfc == NULL) {
+ restore_flags(flags);
+ return;
+ }
+
+ s=intrfc->if_sklist;
+ if(s==sk) {
+ intrfc->if_sklist=s->next;
+ restore_flags(flags);
+ return;
+ }
+
+ while(s && s->next) {
+ if(s->next==sk) {
+ s->next=sk->next;
+ restore_flags(flags);
+ return;
+ }
+ s=s->next;
+ }
+ restore_flags(flags);
+}
+
+/*
+ * This is only called from user mode. Thus it protects itself against
+ * interrupt users but doesn't worry about being called during work.
+ * Once it is removed from the queue no interrupt or bottom half will
+ * touch it and we are (fairly 8-) ) safe.
+ */
+
+static void
+ipx_destroy_socket(ipx_socket *sk)
+{
+ struct sk_buff *skb;
+
+ ipx_remove_socket(sk);
+ while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
+ kfree_skb(skb,FREE_READ);
+ }
+
+ kfree_s(sk,sizeof(*sk));
+}
+
+/* The following code is used to support IPX Interfaces (IPXITF). An
+ * IPX interface is defined by a physical device and a frame type.
+ */
+
+static ipx_route * ipxrtr_lookup(unsigned long);
+
+static void
+ipxitf_clear_primary_net(void)
+{
+ if (ipxcfg_auto_select_primary && (ipx_interfaces != NULL))
+ ipx_primary_net = ipx_interfaces;
+ else
+ ipx_primary_net = NULL;
+}
+
+static ipx_interface *
+ipxitf_find_using_phys(struct device *dev, unsigned short datalink)
+{
+ ipx_interface *i;
+
+ for (i=ipx_interfaces;
+ i && ((i->if_dev!=dev) || (i->if_dlink_type!=datalink));
+ i=i->if_next)
+ ;
+ return i;
+}
+
+static ipx_interface *
+ipxitf_find_using_net(unsigned long net)
+{
+ ipx_interface *i;
+
+ if (net == 0L)
+ return ipx_primary_net;
+
+ for (i=ipx_interfaces; i && (i->if_netnum!=net); i=i->if_next)
+ ;
+
+ return i;
+}
+
+/* Sockets are bound to a particular IPX interface. */
+static void
+ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk)
+{
+ ipx_socket *s;
+
+ sk->ipx_intrfc = intrfc;
+ sk->next = NULL;
+ if (intrfc->if_sklist == NULL) {
+ intrfc->if_sklist = sk;
+ } else {
+ for (s = intrfc->if_sklist; s->next != NULL; s = s->next)
+ ;
+ s->next = sk;
+ }
+}
+
+static ipx_socket *
+ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
+{
+ ipx_socket *s;
+
+ for (s=intrfc->if_sklist;
+ (s != NULL) && (s->ipx_port != port);
+ s=s->next)
+ ;
+
+ return s;
+}
+
+static void ipxrtr_del_routes(ipx_interface *);
+
+static void
+ipxitf_down(ipx_interface *intrfc)
+{
+ ipx_interface *i;
+ ipx_socket *s, *t;
+
+ /* Delete all routes associated with this interface */
+ ipxrtr_del_routes(intrfc);
+
+ /* error sockets */
+ for (s = intrfc->if_sklist; s != NULL; ) {
+ s->err = ENOLINK;
+ s->error_report(s);
+ s->ipx_intrfc = NULL;
+ s->ipx_port = 0;
+ s->zapped=1; /* Indicates it is no longer bound */
+ t = s;
+ s = s->next;
+ t->next = NULL;
+ }
+ intrfc->if_sklist = NULL;
+
+ /* remove this interface from list */
+ if (intrfc == ipx_interfaces) {
+ ipx_interfaces = intrfc->if_next;
+ } else {
+ for (i = ipx_interfaces;
+ (i != NULL) && (i->if_next != intrfc);
+ i = i->if_next)
+ ;
+ if ((i != NULL) && (i->if_next == intrfc))
+ i->if_next = intrfc->if_next;
+ }
+
+ /* remove this interface from *special* networks */
+ if (intrfc == ipx_primary_net)
+ ipxitf_clear_primary_net();
+ if (intrfc == ipx_internal_net)
+ ipx_internal_net = NULL;
+
+ kfree_s(intrfc, sizeof(*intrfc));
+}
+
+static int
+ipxitf_device_event(unsigned long event, void *ptr)
+{
+ struct device *dev = ptr;
+ ipx_interface *i, *tmp;
+
+ if(event!=NETDEV_DOWN)
+ return NOTIFY_DONE;
+
+ for (i = ipx_interfaces; i != NULL; ) {
+
+ tmp = i->if_next;
+ if (i->if_dev == dev)
+ ipxitf_down(i);
+ i = tmp;
+
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int
+ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
+{
+ int retval;
+
+ if((retval = sock_queue_rcv_skb(sock, skb))<0) {
+ /*
+ * We do a FREE_WRITE here because this indicates how
+ * to treat the socket with which the packet is
+ * associated. If this packet is associated with a
+ * socket at all, it must be the originator of the
+ * packet. Incoming packets will have no socket
+ * associated with them at this point.
+ */
+ kfree_skb(skb,FREE_WRITE);
+ }
+ return retval;
+}
+
+static int
+ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
+{
+ ipx_packet *ipx = (ipx_packet *)(skb->h.raw);
+ ipx_socket *sock1 = NULL, *sock2 = NULL;
+ struct sk_buff *skb1 = NULL, *skb2 = NULL;
+ int ipx_offset;
+
+ sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+
+ /*
+ * We need to check if there is a primary net and if
+ * this is addressed to one of the *SPECIAL* sockets because
+ * these need to be propagated to the primary net.
+ * The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
+ * 0x456(Diagnostic).
+ */
+ if (ipx_primary_net && (intrfc != ipx_primary_net)) {
+ switch (ntohs(ipx->ipx_dest.sock)) {
+ case 0x452:
+ case 0x453:
+ case 0x456:
+ /*
+ * The appropriate thing to do here is to
+ * dup the packet and route to the primary net
+ * interface via ipxitf_send; however, we'll cheat
+ * and just demux it here.
+ */
+ sock2 = ipxitf_find_socket(ipx_primary_net,
+ ipx->ipx_dest.sock);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* if there is nothing to do, return */
+ if ((sock1 == NULL) && (sock2 == NULL)) {
+ if (!copy)
+ kfree_skb(skb,FREE_WRITE);
+ return 0;
+ }
+
+ ipx_offset = (char *)(skb->h.raw) - (char *)(skb->data);
+
+ /* This next segment of code is a little awkward, but it sets it up
+ * so that the appropriate number of copies of the SKB are made and
+ * that skb1 and skb2 point to it (them) so that it (they) can be
+ * demuxed to sock1 and/or sock2. If we are unable to make enough
+ * copies, we do as much as is possible.
+ */
+ if (copy) {
+ skb1 = skb_clone(skb, GFP_ATOMIC);
+ if (skb1 != NULL) {
+ skb1->h.raw = (unsigned char *)&(skb1->data[ipx_offset]);
+ skb1->arp = skb1->free = 1;
+ }
+ } else {
+ skb1 = skb;
+ }
+
+ if (skb1 == NULL) return -ENOMEM;
+
+ /* Do we need 2 SKBs? */
+ if (sock1 && sock2) {
+ skb2 = skb_clone(skb1, GFP_ATOMIC);
+ if (skb2 != NULL) {
+ skb2->h.raw = (unsigned char *)&(skb2->data[ipx_offset]);
+ skb2->arp = skb2->free = 1;
+ }
+ } else {
+ skb2 = skb1;
+ }
+
+ if (sock1) {
+ (void) ipxitf_def_skb_handler(sock1, skb1);
+ }
+
+ if (skb2 == NULL) return -ENOMEM;
+
+ if (sock2) {
+ (void) ipxitf_def_skb_handler(sock2, skb2);
+ }
+
+ return 0;
+}
+
+static struct sk_buff *
+ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ int in_offset = skb->h.raw - skb->data;
+ int out_offset = intrfc->if_ipx_offset;
+ char *oldraw;
+ int len;
+
+ /* Hopefully, most cases */
+ if (in_offset == out_offset) {
+ skb->len += out_offset;
+ skb->arp = skb->free = 1;
+ return skb;
+ }
+
+ /* Existing SKB will work, just need to move things around a little */
+ if (in_offset > out_offset) {
+ oldraw = skb->h.raw;
+ skb->h.raw = &(skb->data[out_offset]);
+ memmove(skb->h.raw, oldraw, skb->len);
+ skb->len += out_offset;
+ skb->arp = skb->free = 1;
+ return skb;
+ }
+
+ /* Need new SKB */
+ len = skb->len + out_offset;
+ skb2 = alloc_skb(len, GFP_ATOMIC);
+ if (skb2 != NULL) {
+ skb2->h.raw = &(skb2->data[out_offset]);
+ skb2->len = len;
+ skb2->free=1;
+ skb2->arp=1;
+ memcpy(skb2->h.raw, skb->h.raw, skb->len);
+ }
+ kfree_skb(skb, FREE_WRITE);
+ return skb2;
+}
+
+static int
+ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
+{
+ ipx_packet *ipx = (ipx_packet *)(skb->h.raw);
+ struct device *dev = intrfc->if_dev;
+ struct datalink_proto *dl = intrfc->if_dlink;
+ char dest_node[IPX_NODE_LEN];
+ int send_to_wire = 1;
+ int addr_len;
+
+ /* We need to know how many skbuffs it will take to send out this
+ * packet to avoid unnecessary copies.
+ */
+ if ((dl == NULL) || (dev == NULL) || (dev->flags & IFF_LOOPBACK))
+ send_to_wire = 0;
+
+ /* See if this should be demuxed to sockets on this interface */
+ if (ipx->ipx_dest.net == intrfc->if_netnum) {
+ if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0)
+ return ipxitf_demux_socket(intrfc, skb, 0);
+ if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+ ipxitf_demux_socket(intrfc, skb, send_to_wire);
+ if (!send_to_wire) return 0;
+ }
+ }
+
+ /* if the originating net is not equal to our net; this is routed */
+ if (ipx->ipx_source.net != intrfc->if_netnum) {
+ if (++(ipx->ipx_tctrl) > ipxcfg_max_hops)
+ send_to_wire = 0;
+ }
+
+ if (!send_to_wire) {
+ /*
+ * We do a FREE_WRITE here because this indicates how
+ * to treat the socket with which the packet is
+ * associated. If this packet is associated with a
+ * socket at all, it must be the originator of the
+ * packet. Routed packets will have no socket associated
+ * with them.
+ */
+ kfree_skb(skb,FREE_WRITE);
+ return 0;
+ }
+
+ /* determine the appropriate hardware address */
+ addr_len = dev->addr_len;
+ if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+ memcpy(dest_node, dev->broadcast, addr_len);
+ } else {
+ memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
+ }
+
+ /* make any compensation for differing physical/data link size */
+ skb = ipxitf_adjust_skbuff(intrfc, skb);
+ if (skb == NULL) return 0;
+
+ /* set up data link and physical headers */
+ skb->dev = dev;
+ dl->datalink_header(dl, skb, dest_node);
+
+ if (skb->sk != NULL) {
+ /* This is an outbound packet from this host. We need to
+ * increment the write count.
+ */
+ skb->sk->wmem_alloc += skb->mem_len;
+ }
+
+ /* Send it out */
+ dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ return 0;
+}
+
+static int
+ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
+
+static int
+ipxitf_add_local_route(ipx_interface *intrfc)
+{
+ return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
+}
+
+static char * ipx_frame_name(unsigned short);
+static char * ipx_device_name(ipx_interface *);
+static int ipxrtr_route_skb(struct sk_buff *);
+
+static int
+ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
+{
+ ipx_packet *ipx = (ipx_packet *) (skb->h.raw);
+ ipx_interface *i;
+
+ /* See if we should update our network number */
+ if ((intrfc->if_netnum == 0L) &&
+ (ipx->ipx_source.net == ipx->ipx_dest.net) &&
+ (ipx->ipx_source.net != 0L)) {
+ /* NB: NetWare servers lie about their hop count so we
+ * dropped the test based on it. This is the best way
+ * to determine this is a 0 hop count packet.
+ */
+ if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL) {
+ intrfc->if_netnum = ipx->ipx_source.net;
+ (void) ipxitf_add_local_route(intrfc);
+ } else {
+ printk("IPX: Network number collision %lx\n\t%s %s and %s %s\n",
+ htonl(ipx->ipx_source.net),
+ ipx_device_name(i),
+ ipx_frame_name(i->if_dlink_type),
+ ipx_device_name(intrfc),
+ ipx_frame_name(intrfc->if_dlink_type));
+ }
+ }
+
+ if (ipx->ipx_dest.net == 0L)
+ ipx->ipx_dest.net = intrfc->if_netnum;
+ if (ipx->ipx_source.net == 0L)
+ ipx->ipx_source.net = intrfc->if_netnum;
+
+ if (intrfc->if_netnum != ipx->ipx_dest.net) {
+ /* We only route point-to-point packets. */
+ if ((skb->pkt_type != PACKET_BROADCAST) &&
+ (skb->pkt_type != PACKET_MULTICAST))
+ return ipxrtr_route_skb(skb);
+
+ kfree_skb(skb,FREE_READ);
+ return 0;
+ }
+
+ /* see if we should keep it */
+ if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)
+ || (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)) {
+ return ipxitf_demux_socket(intrfc, skb, 0);
+ }
+
+ /* we couldn't pawn it off so unload it */
+ kfree_skb(skb,FREE_READ);
+ return 0;
+}
+
+static void
+ipxitf_insert(ipx_interface *intrfc)
+{
+ ipx_interface *i;
+
+ intrfc->if_next = NULL;
+ if (ipx_interfaces == NULL) {
+ ipx_interfaces = intrfc;
+ } else {
+ for (i = ipx_interfaces; i->if_next != NULL; i = i->if_next)
+ ;
+ i->if_next = intrfc;
+ }
+
+ if (ipxcfg_auto_select_primary && (ipx_primary_net == NULL))
+ ipx_primary_net = intrfc;
+}
+
+static int
+ipxitf_create_internal(ipx_interface_definition *idef)
+{
+ ipx_interface *intrfc;
+
+ /* Only one primary network allowed */
+ if (ipx_primary_net != NULL) return -EEXIST;
+
+ /* Must have a valid network number */
+ if (idef->ipx_network == 0L) return -EADDRNOTAVAIL;
+ if (ipxitf_find_using_net(idef->ipx_network) != NULL)
+ return -EADDRINUSE;
+
+ intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+ if (intrfc==NULL)
+ return -EAGAIN;
+ intrfc->if_dev=NULL;
+ intrfc->if_netnum=idef->ipx_network;
+ intrfc->if_dlink_type = 0;
+ intrfc->if_dlink = NULL;
+ intrfc->if_sklist = NULL;
+ intrfc->if_internal = 1;
+ intrfc->if_ipx_offset = 0;
+ intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
+ memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN);
+ ipx_internal_net = intrfc;
+ ipx_primary_net = intrfc;
+ ipxitf_insert(intrfc);
+ return ipxitf_add_local_route(intrfc);
+}
+
+static int
+ipx_map_frame_type(unsigned char type)
+{
+ switch (type) {
+ case IPX_FRAME_ETHERII: return htons(ETH_P_IPX);
+ case IPX_FRAME_8022: return htons(ETH_P_802_2);
+ case IPX_FRAME_SNAP: return htons(ETH_P_SNAP);
+ case IPX_FRAME_8023: return htons(ETH_P_802_3);
+ }
+ return 0;
+}
+
+static int
+ipxitf_create(ipx_interface_definition *idef)
+{
+ struct device *dev;
+ unsigned short dlink_type = 0;
+ struct datalink_proto *datalink = NULL;
+ ipx_interface *intrfc;
+
+ if (idef->ipx_special == IPX_INTERNAL)
+ return ipxitf_create_internal(idef);
+
+ if ((idef->ipx_special == IPX_PRIMARY) && (ipx_primary_net != NULL))
+ return -EEXIST;
+
+ if ((idef->ipx_network != 0L) &&
+ (ipxitf_find_using_net(idef->ipx_network) != NULL))
+ return -EADDRINUSE;
+
+ switch (idef->ipx_dlink_type) {
+ case IPX_FRAME_ETHERII:
+ dlink_type = htons(ETH_P_IPX);
+ datalink = pEII_datalink;
+ break;
+ case IPX_FRAME_8022:
+ dlink_type = htons(ETH_P_802_2);
+ datalink = p8022_datalink;
+ break;
+ case IPX_FRAME_SNAP:
+ dlink_type = htons(ETH_P_SNAP);
+ datalink = pSNAP_datalink;
+ break;
+ case IPX_FRAME_8023:
+ dlink_type = htons(ETH_P_802_3);
+ datalink = p8023_datalink;
+ break;
+ case IPX_FRAME_NONE:
+ default:
+ break;
+ }
+
+ if (datalink == NULL)
+ return -EPROTONOSUPPORT;
+
+ dev=dev_get(idef->ipx_device);
+ if (dev==NULL)
+ return -ENODEV;
+
+ if (!(dev->flags & IFF_UP))
+ return -ENETDOWN;
+
+ /* Check addresses are suitable */
+ if(dev->addr_len>IPX_NODE_LEN)
+ return -EINVAL;
+
+ if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) {
+
+ /* Ok now create */
+ intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+ if (intrfc==NULL)
+ return -EAGAIN;
+ intrfc->if_dev=dev;
+ intrfc->if_netnum=idef->ipx_network;
+ intrfc->if_dlink_type = dlink_type;
+ intrfc->if_dlink = datalink;
+ intrfc->if_sklist = NULL;
+ intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
+ /* Setup primary if necessary */
+ if ((idef->ipx_special == IPX_PRIMARY))
+ ipx_primary_net = intrfc;
+ intrfc->if_internal = 0;
+ intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length;
+ memset(intrfc->if_node, 0, IPX_NODE_LEN);
+ memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len);
+
+ ipxitf_insert(intrfc);
+ }
+
+ /* If the network number is known, add a route */
+ if (intrfc->if_netnum == 0L)
+ return 0;
+
+ return ipxitf_add_local_route(intrfc);
+}
+
+static int
+ipxitf_delete(ipx_interface_definition *idef)
+{
+ struct device *dev = NULL;
+ unsigned short dlink_type = 0;
+ ipx_interface *intrfc;
+
+ if (idef->ipx_special == IPX_INTERNAL) {
+ if (ipx_internal_net != NULL) {
+ ipxitf_down(ipx_internal_net);
+ return 0;
+ }
+ return -ENOENT;
+ }
+
+ dlink_type = ipx_map_frame_type(idef->ipx_dlink_type);
+ if (dlink_type == 0)
+ return -EPROTONOSUPPORT;
+
+ dev=dev_get(idef->ipx_device);
+ if(dev==NULL) return -ENODEV;
+
+ intrfc = ipxitf_find_using_phys(dev, dlink_type);
+ if (intrfc != NULL) {
+ ipxitf_down(intrfc);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static ipx_interface *
+ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
+{
+ struct datalink_proto *datalink = NULL;
+ ipx_interface *intrfc;
+
+ switch (htons(dlink_type)) {
+ case ETH_P_IPX: datalink = pEII_datalink; break;
+ case ETH_P_802_2: datalink = p8022_datalink; break;
+ case ETH_P_SNAP: datalink = pSNAP_datalink; break;
+ case ETH_P_802_3: datalink = p8023_datalink; break;
+ default: return NULL;
+ }
+
+ if (dev == NULL)
+ return NULL;
+
+ /* Check addresses are suitable */
+ if(dev->addr_len>IPX_NODE_LEN) return NULL;
+
+ intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+ if (intrfc!=NULL) {
+ intrfc->if_dev=dev;
+ intrfc->if_netnum=0L;
+ intrfc->if_dlink_type = dlink_type;
+ intrfc->if_dlink = datalink;
+ intrfc->if_sklist = NULL;
+ intrfc->if_internal = 0;
+ intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
+ intrfc->if_ipx_offset = dev->hard_header_len +
+ datalink->header_length;
+ memset(intrfc->if_node, 0, IPX_NODE_LEN);
+ memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
+ dev->dev_addr, dev->addr_len);
+ ipxitf_insert(intrfc);
+ }
+
+ return intrfc;
+}
+
+static int
+ipxitf_ioctl(unsigned int cmd, void *arg)
+{
+ int err;
+ switch(cmd)
+ {
+ case SIOCSIFADDR:
+ {
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx;
+ ipx_interface_definition f;
+ err=verify_area(VERIFY_READ,arg,sizeof(ifr));
+ if(err)
+ return err;
+ memcpy_fromfs(&ifr,arg,sizeof(ifr));
+ sipx=(struct sockaddr_ipx *)&ifr.ifr_addr;
+ if(sipx->sipx_family!=AF_IPX)
+ return -EINVAL;
+ f.ipx_network=sipx->sipx_network;
+ memcpy(f.ipx_device, ifr.ifr_name, sizeof(f.ipx_device));
+ memcpy(f.ipx_node, sipx->sipx_node, IPX_NODE_LEN);
+ f.ipx_dlink_type=sipx->sipx_type;
+ f.ipx_special=sipx->sipx_special;
+ if(sipx->sipx_action==IPX_DLTITF)
+ return ipxitf_delete(&f);
+ else
+ return ipxitf_create(&f);
+ }
+ case SIOCGIFADDR:
+ {
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx;
+ ipx_interface *ipxif;
+ struct device *dev;
+ err=verify_area(VERIFY_WRITE,arg,sizeof(ifr));
+ if(err)
+ return err;
+ memcpy_fromfs(&ifr,arg,sizeof(ifr));
+ sipx=(struct sockaddr_ipx *)&ifr.ifr_addr;
+ dev=dev_get(ifr.ifr_name);
+ if(!dev)
+ return -ENODEV;
+ ipxif=ipxitf_find_using_phys(dev, ipx_map_frame_type(sipx->sipx_type));
+ if(ipxif==NULL)
+ return -EADDRNOTAVAIL;
+ sipx->sipx_network=ipxif->if_netnum;
+ memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node));
+ memcpy_tofs(arg,&ifr,sizeof(ifr));
+ return 0;
+ }
+ case SIOCAIPXITFCRT:
+ err=verify_area(VERIFY_READ,arg,sizeof(char));
+ if(err)
+ return err;
+ return ipxcfg_set_auto_create(get_fs_byte(arg));
+ case SIOCAIPXPRISLT:
+ err=verify_area(VERIFY_READ,arg,sizeof(char));
+ if(err)
+ return err;
+ return ipxcfg_set_auto_select(get_fs_byte(arg));
+ default:
+ return -EINVAL;
+ }
+}
+
+/*******************************************************************************************************************\
+* *
+* Routing tables for the IPX socket layer *
+* *
+\*******************************************************************************************************************/
+
+static ipx_route *
+ipxrtr_lookup(unsigned long net)
+{
+ ipx_route *r;
+
+ for (r=ipx_routes; (r!=NULL) && (r->ir_net!=net); r=r->ir_next)
+ ;
+
+ return r;
+}
+
+static int
+ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
+{
+ ipx_route *rt;
+
+ /* Get a route structure; either existing or create */
+ rt = ipxrtr_lookup(network);
+ if (rt==NULL) {
+ rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);
+ if(rt==NULL)
+ return -EAGAIN;
+ rt->ir_next=ipx_routes;
+ ipx_routes=rt;
+ }
+
+ rt->ir_net = network;
+ rt->ir_intrfc = intrfc;
+ if (node == NULL) {
+ memset(rt->ir_router_node, '\0', IPX_NODE_LEN);
+ rt->ir_routed = 0;
+ } else {
+ memcpy(rt->ir_router_node, node, IPX_NODE_LEN);
+ rt->ir_routed=1;
+ }
+ return 0;
+}
+
+static void
+ipxrtr_del_routes(ipx_interface *intrfc)
+{
+ ipx_route **r, *tmp;
+
+ for (r = &ipx_routes; (tmp = *r) != NULL; ) {
+ if (tmp->ir_intrfc == intrfc) {
+ *r = tmp->ir_next;
+ kfree_s(tmp, sizeof(ipx_route));
+ } else {
+ r = &(tmp->ir_next);
+ }
+ }
+}
+
+static int
+ipxrtr_create(ipx_route_definition *rd)
+{
+ ipx_interface *intrfc;
+
+ /* Find the appropriate interface */
+ intrfc = ipxitf_find_using_net(rd->ipx_router_network);
+ if (intrfc == NULL)
+ return -ENETUNREACH;
+
+ return ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node);
+}
+
+
+static int
+ipxrtr_delete(long net)
+{
+ ipx_route **r;
+ ipx_route *tmp;
+
+ for (r = &ipx_routes; (tmp = *r) != NULL; ) {
+ if (tmp->ir_net == net) {
+ if (!(tmp->ir_routed)) {
+ /* Directly connected; can't lose route */
+ return -EPERM;
+ }
+ *r = tmp->ir_next;
+ kfree_s(tmp, sizeof(ipx_route));
+ return 0;
+ }
+ r = &(tmp->ir_next);
+ }
+
+ return -ENOENT;
+}
+
+static int
+ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, void *ubuf, int len)
+{
+ struct sk_buff *skb;
+ ipx_interface *intrfc;
+ ipx_packet *ipx;
+ int size;
+ int ipx_offset;
+ ipx_route *rt = NULL;
+
+ /* Find the appropriate interface on which to send packet */
+ if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL)) {
+ usipx->sipx_network = ipx_primary_net->if_netnum;
+ intrfc = ipx_primary_net;
+ } else {
+ rt = ipxrtr_lookup(usipx->sipx_network);
+ if (rt==NULL) {
+ return -ENETUNREACH;
+ }
+ intrfc = rt->ir_intrfc;
+ }
+
+ ipx_offset = intrfc->if_ipx_offset;
+ size=sizeof(ipx_packet)+len;
+ size += ipx_offset;
+
+ if(size+sk->wmem_alloc>sk->sndbuf) return -EAGAIN;
+
+ skb=alloc_skb(size,GFP_KERNEL);
+ if(skb==NULL) return -ENOMEM;
+
+ skb->sk=sk;
+ skb->len=size;
+ skb->free=1;
+ skb->arp=1;
+
+ /* Fill in IPX header */
+ ipx=(ipx_packet *)&(skb->data[ipx_offset]);
+ ipx->ipx_checksum=0xFFFF;
+ ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
+ ipx->ipx_tctrl=0;
+ ipx->ipx_type=usipx->sipx_type;
+ skb->h.raw = (unsigned char *)ipx;
+
+ ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
+ memcpy(ipx->ipx_source.node, sk->ipx_intrfc->if_node, IPX_NODE_LEN);
+ ipx->ipx_source.sock = sk->ipx_port;
+ ipx->ipx_dest.net=usipx->sipx_network;
+ memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN);
+ ipx->ipx_dest.sock=usipx->sipx_port;
+
+ memcpy_fromfs((char *)(ipx+1),ubuf,len);
+ return ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
+ rt->ir_router_node : ipx->ipx_dest.node);
+}
+
+static int
+ipxrtr_route_skb(struct sk_buff *skb)
+{
+ ipx_packet *ipx = (ipx_packet *) (skb->h.raw);
+ ipx_route *r;
+ ipx_interface *i;
+
+ r = ipxrtr_lookup(ipx->ipx_dest.net);
+ if (r == NULL) {
+ /* no known route */
+ kfree_skb(skb,FREE_READ);
+ return 0;
+ }
+ i = r->ir_intrfc;
+ (void)ipxitf_send(i, skb, (r->ir_routed) ?
+ r->ir_router_node : ipx->ipx_dest.node);
+ return 0;
+}
+
+/*
+ * We use a normal struct rtentry for route handling
+ */
+
+static int ipxrtr_ioctl(unsigned int cmd, void *arg)
+{
+ int err;
+ struct rtentry rt; /* Use these to behave like 'other' stacks */
+ struct sockaddr_ipx *sg,*st;
+
+ err=verify_area(VERIFY_READ,arg,sizeof(rt));
+ if(err)
+ return err;
+
+ memcpy_fromfs(&rt,arg,sizeof(rt));
+
+ sg=(struct sockaddr_ipx *)&rt.rt_gateway;
+ st=(struct sockaddr_ipx *)&rt.rt_dst;
+
+ if(!(rt.rt_flags&RTF_GATEWAY))
+ return -EINVAL; /* Direct routes are fixed */
+ if(sg->sipx_family!=AF_IPX)
+ return -EINVAL;
+ if(st->sipx_family!=AF_IPX)
+ return -EINVAL;
+
+ switch(cmd)
+ {
+ case SIOCDELRT:
+ return ipxrtr_delete(st->sipx_network);
+ case SIOCADDRT:
+ {
+ struct ipx_route_definition f;
+ f.ipx_network=st->sipx_network;
+ f.ipx_router_network=sg->sipx_network;
+ memcpy(f.ipx_router_node, sg->sipx_node, IPX_NODE_LEN);
+ return ipxrtr_create(&f);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static char *
+ipx_frame_name(unsigned short frame)
+{
+ switch (ntohs(frame)) {
+ case ETH_P_IPX: return "EtherII";
+ case ETH_P_802_2: return "802.2";
+ case ETH_P_SNAP: return "SNAP";
+ case ETH_P_802_3: return "802.3";
+ default: return "None";
+ }
+}
+
+static char *
+ipx_device_name(ipx_interface *intrfc)
+{
+ return (intrfc->if_internal ? "Internal" :
+ (intrfc->if_dev ? intrfc->if_dev->name : "Unknown"));
+}
+
+/* Called from proc fs */
+int
+ipx_get_interface_info(char *buffer, char **start, off_t offset, int length)
+{
+ ipx_interface *i;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
+ /* Theory.. Keep printing in the same place until we pass offset */
+
+ len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network",
+ "Node_Address", "Primary", "Device", "Frame_Type");
+ for (i = ipx_interfaces; i != NULL; i = i->if_next) {
+ len += sprintf(buffer+len, "%08lX ", ntohl(i->if_netnum));
+ len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X ",
+ i->if_node[0], i->if_node[1], i->if_node[2],
+ i->if_node[3], i->if_node[4], i->if_node[5]);
+ len += sprintf(buffer+len, "%-9s", (i == ipx_primary_net) ?
+ "Yes" : "No");
+ len += sprintf (buffer+len, "%-11s", ipx_device_name(i));
+ len += sprintf (buffer+len, "%s\n",
+ ipx_frame_name(i->if_dlink_type));
+
+ /* Are we still dumping unwanted data then discard the record */
+ pos=begin+len;
+
+ if(pos<offset) {
+ len=0; /* Keep dumping into the buffer start */
+ begin=pos;
+ }
+ if(pos>offset+length) /* We have dumped enough */
+ break;
+ }
+
+ /* The data in question runs from begin to begin+len */
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Remove unwanted header data from length */
+ if(len>length)
+ len=length; /* Remove unwanted tail data from length */
+
+ return len;
+}
+
+int
+ipx_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ ipx_socket *s;
+ ipx_interface *i;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
+ /* Theory.. Keep printing in the same place until we pass offset */
+
+ len += sprintf (buffer,"%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
+ "Remote_Address", "Tx_Queue", "Rx_Queue",
+ "State", "Uid");
+ for (i = ipx_interfaces; i != NULL; i = i->if_next) {
+ for (s = i->if_sklist; s != NULL; s = s->next) {
+ len += sprintf (buffer+len,"%08lX:%04X ",
+ htonl(i->if_netnum),
+ htons(s->ipx_port));
+ if (s->state!=TCP_ESTABLISHED) {
+ len += sprintf(buffer+len, "%-28s", "Not_Connected");
+ } else {
+ len += sprintf (buffer+len,
+ "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
+ htonl(s->ipx_dest_addr.net),
+ s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1],
+ s->ipx_dest_addr.node[2], s->ipx_dest_addr.node[3],
+ s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
+ htons(s->ipx_dest_addr.sock));
+ }
+ len += sprintf (buffer+len,"%08lX %08lX ",
+ s->wmem_alloc, s->rmem_alloc);
+ len += sprintf (buffer+len,"%02X %03d\n",
+ s->state, SOCK_INODE(s->socket)->i_uid);
+
+ /* Are we still dumping unwanted data then discard the record */
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0; /* Keep dumping into the buffer start */
+ begin=pos;
+ }
+ if(pos>offset+length) /* We have dumped enough */
+ break;
+ }
+ }
+
+ /* The data in question runs from begin to begin+len */
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Remove unwanted header data from length */
+ if(len>length)
+ len=length; /* Remove unwanted tail data from length */
+
+ return len;
+}
+
+int ipx_rt_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ ipx_route *rt;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
+ len += sprintf (buffer,"%-11s%-13s%s\n",
+ "Network", "Router_Net", "Router_Node");
+ for (rt = ipx_routes; rt != NULL; rt = rt->ir_next)
+ {
+ len += sprintf (buffer+len,"%08lX ", ntohl(rt->ir_net));
+ if (rt->ir_routed) {
+ len += sprintf (buffer+len,"%08lX %02X%02X%02X%02X%02X%02X\n",
+ ntohl(rt->ir_intrfc->if_netnum),
+ rt->ir_router_node[0], rt->ir_router_node[1],
+ rt->ir_router_node[2], rt->ir_router_node[3],
+ rt->ir_router_node[4], rt->ir_router_node[5]);
+ } else {
+ len += sprintf (buffer+len, "%-13s%s\n",
+ "Directly", "Connected");
+ }
+ pos=begin+len;
+ 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;
+}
+
+/*******************************************************************************************************************\
+* *
+* Handling for system calls applied via the various interfaces to an IPX socket object *
+* *
+\*******************************************************************************************************************/
+
+static int ipx_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ switch(cmd)
+ {
+ default:
+ return(-EINVAL);
+ }
+}
+
+static int ipx_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+ ipx_socket *sk;
+ int err,opt;
+
+ sk=(ipx_socket *)sock->data;
+
+ if(optval==NULL)
+ return(-EINVAL);
+
+ err=verify_area(VERIFY_READ,optval,sizeof(int));
+ if(err)
+ return err;
+ opt=get_fs_long((unsigned long *)optval);
+
+ switch(level)
+ {
+ case SOL_IPX:
+ switch(optname)
+ {
+ case IPX_TYPE:
+ sk->ipx_type=opt;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ case SOL_SOCKET:
+ return sock_setsockopt(sk,level,optname,optval,optlen);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ipx_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ ipx_socket *sk;
+ int val=0;
+ int err;
+
+ sk=(ipx_socket *)sock->data;
+
+ switch(level)
+ {
+
+ case SOL_IPX:
+ switch(optname)
+ {
+ case IPX_TYPE:
+ val=sk->ipx_type;
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+ break;
+
+ case SOL_SOCKET:
+ return sock_getsockopt(sk,level,optname,optval,optlen);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ 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));
+ put_fs_long(val,(unsigned long *)optval);
+ return(0);
+}
+
+static int ipx_listen(struct socket *sock, int backlog)
+{
+ return -EOPNOTSUPP;
+}
+
+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 int
+ipx_create(struct socket *sock, int protocol)
+{
+ ipx_socket *sk;
+ sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+ if(sk==NULL)
+ return(-ENOMEM);
+ switch(sock->type)
+ {
+ case SOCK_DGRAM:
+ break;
+ default:
+ kfree_s((void *)sk,sizeof(*sk));
+ return(-ESOCKTNOSUPPORT);
+ }
+ sk->dead=0;
+ sk->next=NULL;
+ sk->broadcast=0;
+ sk->rcvbuf=SK_RMEM_MAX;
+ sk->sndbuf=SK_WMEM_MAX;
+ sk->wmem_alloc=0;
+ sk->rmem_alloc=0;
+ sk->inuse=0;
+ sk->shutdown=0;
+ sk->prot=NULL; /* So we use default free mechanisms */
+ sk->broadcast=0;
+ sk->err=0;
+ skb_queue_head_init(&sk->receive_queue);
+ skb_queue_head_init(&sk->write_queue);
+ sk->send_head=NULL;
+ skb_queue_head_init(&sk->back_log);
+ sk->state=TCP_CLOSE;
+ sk->socket=sock;
+ sk->type=sock->type;
+ sk->ipx_type=0; /* General user level IPX */
+ sk->debug=0;
+ sk->ipx_intrfc = NULL;
+ memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
+ sk->ipx_port = 0;
+ sk->mtu=IPX_MTU;
+
+ if(sock!=NULL)
+ {
+ sock->data=(void *)sk;
+ sk->sleep=sock->wait;
+ }
+
+ sk->state_change=def_callback1;
+ sk->data_ready=def_callback2;
+ sk->write_space=def_callback1;
+ sk->error_report=def_callback1;
+
+ sk->zapped=1;
+ return 0;
+}
+
+static int ipx_release(struct socket *sock, struct socket *peer)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ if(sk==NULL)
+ return(0);
+ if(!sk->dead)
+ sk->state_change(sk);
+ sk->dead=1;
+ sock->data=NULL;
+ ipx_destroy_socket(sk);
+ return(0);
+}
+
+static int ipx_dup(struct socket *newsock,struct socket *oldsock)
+{
+ return(ipx_create(newsock,SOCK_DGRAM));
+}
+
+static unsigned short
+ipx_first_free_socketnum(ipx_interface *intrfc)
+{
+ unsigned short socketNum = intrfc->if_sknum;
+
+ if (socketNum < IPX_MIN_EPHEMERAL_SOCKET)
+ socketNum = IPX_MIN_EPHEMERAL_SOCKET;
+
+ while (ipxitf_find_socket(intrfc, ntohs(socketNum)) != NULL)
+ if (socketNum > IPX_MAX_EPHEMERAL_SOCKET)
+ socketNum = IPX_MIN_EPHEMERAL_SOCKET;
+ else
+ socketNum++;
+
+ intrfc->if_sknum = socketNum;
+ return ntohs(socketNum);
+}
+
+static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
+{
+ ipx_socket *sk;
+ ipx_interface *intrfc;
+ struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr;
+
+ sk=(ipx_socket *)sock->data;
+
+ if(sk->zapped==0)
+ return -EIO;
+
+ if(addr_len!=sizeof(struct sockaddr_ipx))
+ return -EINVAL;
+
+ intrfc = ipxitf_find_using_net(addr->sipx_network);
+ if (intrfc == NULL)
+ return -EADDRNOTAVAIL;
+
+ if (addr->sipx_port == 0) {
+ addr->sipx_port = ipx_first_free_socketnum(intrfc);
+ if (addr->sipx_port == 0)
+ return -EINVAL;
+ }
+
+ if(ntohs(addr->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser())
+ return -EPERM; /* protect IPX system stuff like routing/sap */
+
+ /* Source addresses are easy. It must be our network:node pair for
+ an interface routed to IPX with the ipx routing ioctl() */
+
+ if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
+ if(sk->debug)
+ printk("IPX: bind failed because port %X in use.\n",
+ (int)addr->sipx_port);
+ return -EADDRINUSE;
+ }
+
+ sk->ipx_port=addr->sipx_port;
+ ipxitf_insert_socket(intrfc, sk);
+ sk->zapped=0;
+ if(sk->debug)
+ printk("IPX: socket is bound.\n");
+ return 0;
+}
+
+static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sockaddr_ipx *addr;
+
+ sk->state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ if(addr_len!=sizeof(*addr))
+ return(-EINVAL);
+ addr=(struct sockaddr_ipx *)uaddr;
+
+ if(sk->ipx_port==0)
+ /* put the autobinding in */
+ {
+ struct sockaddr_ipx uaddr;
+ int ret;
+
+ uaddr.sipx_port = 0;
+ uaddr.sipx_network = 0L;
+ ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
+ if (ret != 0) return (ret);
+ }
+
+ if(ipxrtr_lookup(addr->sipx_network)==NULL)
+ return -ENETUNREACH;
+ sk->ipx_dest_addr.net=addr->sipx_network;
+ sk->ipx_dest_addr.sock=addr->sipx_port;
+ memcpy(sk->ipx_dest_addr.node,addr->sipx_node,IPX_NODE_LEN);
+ sk->ipx_type=addr->sipx_type;
+ sock->state = SS_CONNECTED;
+ sk->state=TCP_ESTABLISHED;
+ return 0;
+}
+
+static int ipx_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return(-EOPNOTSUPP);
+}
+
+static int ipx_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ if(newsock->data)
+ kfree_s(newsock->data,sizeof(ipx_socket));
+ return -EOPNOTSUPP;
+}
+
+static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ ipx_address *addr;
+ struct sockaddr_ipx sipx;
+ ipx_socket *sk;
+
+ sk=(ipx_socket *)sock->data;
+
+ *uaddr_len = sizeof(struct sockaddr_ipx);
+
+ if(peer) {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ addr=&sk->ipx_dest_addr;
+ sipx.sipx_network = addr->net;
+ memcpy(sipx.sipx_node,addr->node,IPX_NODE_LEN);
+ sipx.sipx_port = addr->sock;
+ } else {
+ if (sk->ipx_intrfc != NULL) {
+ sipx.sipx_network = sk->ipx_intrfc->if_netnum;
+ memcpy(sipx.sipx_node, sk->ipx_intrfc->if_node,
+ IPX_NODE_LEN);
+ } else {
+ sipx.sipx_network = 0L;
+ memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
+ }
+ sipx.sipx_port = sk->ipx_port;
+ }
+
+ sipx.sipx_family = AF_IPX;
+ sipx.sipx_type = sk->ipx_type;
+ memcpy(uaddr,&sipx,sizeof(sipx));
+ return 0;
+}
+
+#if 0
+/*
+ * User to dump IPX packets (debugging)
+ */
+void dump_data(char *str,unsigned char *d) {
+ static char h2c[] = "0123456789ABCDEF";
+ int l,i;
+ char *p, b[64];
+ for (l=0;l<16;l++) {
+ p = b;
+ for (i=0; i < 8 ; i++) {
+ *(p++) = h2c[d[i] & 0x0f];
+ *(p++) = h2c[(d[i] >> 4) & 0x0f];
+ *(p++) = ' ';
+ }
+ *(p++) = '-';
+ *(p++) = ' ';
+ for (i=0; i < 8 ; i++) *(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.';
+ *p = '\000';
+ d += i;
+ printk("%s-%04X: %s\n",str,l*8,b);
+ }
+}
+
+void dump_addr(char *str,ipx_address *p) {
+ printk("%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n",
+ str,ntohl(p->net),p->node[0],p->node[1],p->node[2],
+ p->node[3],p->node[4],p->node[5],ntohs(p->sock));
+}
+
+void dump_hdr(char *str,ipx_packet *p) {
+ printk("%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n",
+ str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize),
+ p->ipx_tctrl,p->ipx_tctrl,p->ipx_type);
+ dump_addr(" IPX-DST",&p->ipx_dest);
+ dump_addr(" IPX-SRC",&p->ipx_source);
+}
+
+void dump_pkt(char *str,ipx_packet *p) {
+ dump_hdr(str,p);
+ dump_data(str,(unsigned char *)p);
+}
+#endif
+
+int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ /* NULL here for pt means the packet was looped back */
+ ipx_interface *intrfc;
+ ipx_packet *ipx;
+
+ ipx=(ipx_packet *)skb->h.raw;
+
+ if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) {
+ /* We don't do checksum options. We can't really. Novell don't seem to have documented them.
+ If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be
+ the same... */
+ kfree_skb(skb,FREE_READ);
+ return 0;
+ }
+
+ /* Too small */
+ if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet)) {
+ kfree_skb(skb,FREE_READ);
+ return 0;
+ }
+
+ /* Determine what local ipx endpoint this is */
+ intrfc = ipxitf_find_using_phys(dev, pt->type);
+ if (intrfc == NULL) {
+ if (ipxcfg_auto_create_interfaces) {
+ intrfc = ipxitf_auto_create(dev, pt->type);
+ }
+
+ if (intrfc == NULL) {
+ /* Not one of ours */
+ kfree_skb(skb,FREE_READ);
+ return 0;
+ }
+ }
+
+ return ipxitf_rcv(intrfc, skb);
+}
+
+static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
+ unsigned flags, struct sockaddr *usip, int addr_len)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip;
+ struct sockaddr_ipx local_sipx;
+ int retval;
+
+ if (sk->zapped) return -EIO; /* Socket not bound */
+ if(flags) return -EINVAL;
+
+ if(usipx) {
+ if(sk->ipx_port == 0) {
+ struct sockaddr_ipx uaddr;
+ int ret;
+
+ uaddr.sipx_port = 0;
+ uaddr.sipx_network = 0L;
+ ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
+ if (ret != 0) return ret;
+ }
+
+ if(addr_len <sizeof(*usipx))
+ return -EINVAL;
+ if(usipx->sipx_family != AF_IPX)
+ return -EINVAL;
+ } else {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ usipx=&local_sipx;
+ usipx->sipx_family=AF_IPX;
+ usipx->sipx_type=sk->ipx_type;
+ usipx->sipx_port=sk->ipx_dest_addr.sock;
+ usipx->sipx_network=sk->ipx_dest_addr.net;
+ memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,IPX_NODE_LEN);
+ }
+
+ retval = ipxrtr_route_packet(sk, usipx, ubuf, len);
+ if (retval < 0) return retval;
+
+ return len;
+}
+
+static int ipx_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags)
+{
+ return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0);
+}
+
+static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
+ unsigned flags, struct sockaddr *sip, int *addr_len)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)sip;
+ struct ipx_packet *ipx = NULL;
+ int copied = 0;
+ int truesize;
+ struct sk_buff *skb;
+ int er;
+
+ if(sk->err)
+ {
+ er= -sk->err;
+ sk->err=0;
+ return er;
+ }
+
+ if (sk->zapped)
+ return -EIO;
+
+ if(addr_len)
+ *addr_len=sizeof(*sipx);
+
+ skb=skb_recv_datagram(sk,flags,noblock,&er);
+ if(skb==NULL)
+ return er;
+
+ ipx = (ipx_packet *)(skb->h.raw);
+ truesize=ntohs(ipx->ipx_pktsize) - sizeof(ipx_packet);
+ copied = (truesize > size) ? size : truesize;
+ skb_copy_datagram(skb,sizeof(struct ipx_packet),ubuf,copied);
+
+ if(sipx)
+ {
+ sipx->sipx_family=AF_IPX;
+ sipx->sipx_port=ipx->ipx_source.sock;
+ memcpy(sipx->sipx_node,ipx->ipx_source.node,IPX_NODE_LEN);
+ sipx->sipx_network=ipx->ipx_source.net;
+ sipx->sipx_type = ipx->ipx_type;
+ }
+ skb_free_datagram(skb);
+ return(truesize);
+}
+
+static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
+{
+ return ipx_send(sock,ubuf,size,noblock,0);
+}
+
+
+static int ipx_recv(struct socket *sock, void *ubuf, int size , int noblock,
+ unsigned flags)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ if(sk->zapped)
+ return -ENOTCONN;
+ return ipx_recvfrom(sock,ubuf,size,noblock,flags,NULL, NULL);
+}
+
+static int ipx_read(struct socket *sock, char *ubuf, int size, int noblock)
+{
+ return ipx_recv(sock,ubuf,size,noblock,0);
+}
+
+
+static int ipx_shutdown(struct socket *sk,int how)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ipx_select(struct socket *sock , int sel_type, select_table *wait)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+
+ return datagram_select(sk,sel_type,wait);
+}
+
+static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+ int err;
+ long amount=0;
+ ipx_socket *sk=(ipx_socket *)sock->data;
+
+ switch(cmd)
+ {
+ case TIOCOUTQ:
+ err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
+ if(err)
+ return err;
+ amount=sk->sndbuf-sk->wmem_alloc;
+ if(amount<0)
+ amount=0;
+ put_fs_long(amount,(unsigned long *)arg);
+ return 0;
+ case TIOCINQ:
+ {
+ struct sk_buff *skb;
+ /* These two are safe on a single CPU system as only user tasks fiddle here */
+ if((skb=skb_peek(&sk->receive_queue))!=NULL)
+ amount=skb->len;
+ err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
+ put_fs_long(amount,(unsigned long *)arg);
+ return 0;
+ }
+ case SIOCADDRT:
+ case SIOCDELRT:
+ if(!suser())
+ return -EPERM;
+ return(ipxrtr_ioctl(cmd,(void *)arg));
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCAIPXITFCRT:
+ case SIOCAIPXPRISLT:
+ if(!suser())
+ return -EPERM;
+ return(ipxitf_ioctl(cmd,(void *)arg));
+ case SIOCIPXCFGDATA:
+ {
+ err=verify_area(VERIFY_WRITE,(void *)arg,
+ sizeof(ipx_config_data));
+ if(err) return err;
+ return(ipxcfg_get_config_data((void *)arg));
+ }
+ case SIOCGSTAMP:
+ if (sk)
+ {
+ 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;
+ }
+ return -EINVAL;
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ return -EINVAL;
+ default:
+ return(dev_ioctl(cmd,(void *) arg));
+ }
+ /*NOTREACHED*/
+ return(0);
+}
+
+static struct proto_ops ipx_proto_ops = {
+ AF_IPX,
+
+ ipx_create,
+ ipx_dup,
+ ipx_release,
+ ipx_bind,
+ ipx_connect,
+ ipx_socketpair,
+ ipx_accept,
+ ipx_getname,
+ ipx_read,
+ ipx_write,
+ ipx_select,
+ ipx_ioctl,
+ ipx_listen,
+ ipx_send,
+ ipx_recv,
+ ipx_sendto,
+ ipx_recvfrom,
+ ipx_shutdown,
+ ipx_setsockopt,
+ ipx_getsockopt,
+ ipx_fcntl,
+};
+
+/* Called by ddi.c on kernel start up */
+
+static struct packet_type ipx_8023_packet_type =
+
+{
+ 0, /* MUTTER ntohs(ETH_P_8023),*/
+ NULL, /* All devices */
+ ipx_rcv,
+ NULL,
+ NULL,
+};
+
+static struct packet_type ipx_dix_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_IPX),*/
+ NULL, /* All devices */
+ ipx_rcv,
+ NULL,
+ NULL,
+};
+
+static struct notifier_block ipx_dev_notifier={
+ ipxitf_device_event,
+ NULL,
+ 0
+};
+
+
+extern struct datalink_proto *make_EII_client(void);
+extern struct datalink_proto *make_8023_client(void);
+
+void ipx_proto_init(struct net_proto *pro)
+{
+ unsigned char val = 0xE0;
+ unsigned char snapval[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
+
+ (void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
+
+ pEII_datalink = make_EII_client();
+ ipx_dix_packet_type.type=htons(ETH_P_IPX);
+ dev_add_pack(&ipx_dix_packet_type);
+
+ p8023_datalink = make_8023_client();
+ ipx_8023_packet_type.type=htons(ETH_P_802_3);
+ dev_add_pack(&ipx_8023_packet_type);
+
+ if ((p8022_datalink = register_8022_client(val, ipx_rcv)) == NULL)
+ printk("IPX: Unable to register with 802.2\n");
+
+ if ((pSNAP_datalink = register_snap_client(snapval, ipx_rcv)) == NULL)
+ printk("IPX: Unable to register with SNAP\n");
+
+ register_netdevice_notifier(&ipx_dev_notifier);
+
+ printk("Swansea University Computer Society IPX 0.29 BETA for NET3.019\n");
+ printk("IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
+}
+#endif
diff --git a/pfinet/linux-inet/ipx.h b/pfinet/linux-inet/ipx.h
new file mode 100644
index 00000000..6842c832
--- /dev/null
+++ b/pfinet/linux-inet/ipx.h
@@ -0,0 +1,84 @@
+
+/*
+ * The following information is in its entirety obtained from:
+ *
+ * Novell 'IPX Router Specification' Version 1.10
+ * Part No. 107-000029-001
+ *
+ * Which is available from ftp.novell.com
+ */
+
+#ifndef _NET_INET_IPX_H_
+#define _NET_INET_IPX_H_
+
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/ipx.h>
+
+typedef struct
+{
+ unsigned long net;
+ unsigned char node[IPX_NODE_LEN];
+ unsigned short sock;
+} ipx_address;
+
+#define ipx_broadcast_node "\377\377\377\377\377\377"
+
+typedef struct ipx_packet
+{
+ unsigned short ipx_checksum;
+#define IPX_NO_CHECKSUM 0xFFFF
+ unsigned short ipx_pktsize;
+ unsigned char ipx_tctrl;
+ unsigned char ipx_type;
+#define IPX_TYPE_UNKNOWN 0x00
+#define IPX_TYPE_RIP 0x01 /* may also be 0 */
+#define IPX_TYPE_SAP 0x04 /* may also be 0 */
+#define IPX_TYPE_SPX 0x05 /* Not yet implemented */
+#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */
+#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */
+ ipx_address ipx_dest __attribute__ ((packed));
+ ipx_address ipx_source __attribute__ ((packed));
+} ipx_packet;
+
+
+typedef struct sock ipx_socket;
+
+#include "ipxcall.h"
+extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
+extern void ipxrtr_device_down(struct device *dev);
+
+typedef struct ipx_interface {
+ /* IPX address */
+ unsigned long if_netnum;
+ unsigned char if_node[IPX_NODE_LEN];
+
+ /* physical device info */
+ struct device *if_dev;
+ struct datalink_proto *if_dlink;
+ unsigned short if_dlink_type;
+
+ /* socket support */
+ unsigned short if_sknum;
+ ipx_socket *if_sklist;
+
+ /* administrative overhead */
+ int if_ipx_offset;
+ unsigned char if_internal;
+ unsigned char if_primary;
+
+ struct ipx_interface *if_next;
+} ipx_interface;
+
+typedef struct ipx_route {
+ unsigned long ir_net;
+ ipx_interface *ir_intrfc;
+ unsigned char ir_routed;
+ unsigned char ir_router_node[IPX_NODE_LEN];
+ struct ipx_route *ir_next;
+} ipx_route;
+
+#define IPX_MIN_EPHEMERAL_SOCKET 0x4000
+#define IPX_MAX_EPHEMERAL_SOCKET 0x7fff
+
+#endif
diff --git a/pfinet/linux-inet/ipxcall.h b/pfinet/linux-inet/ipxcall.h
new file mode 100644
index 00000000..eb5bd2bd
--- /dev/null
+++ b/pfinet/linux-inet/ipxcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void ipx_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-inet/p8022.c b/pfinet/linux-inet/p8022.c
new file mode 100644
index 00000000..8ff3ec60
--- /dev/null
+++ b/pfinet/linux-inet/p8022.c
@@ -0,0 +1,98 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static struct datalink_proto *p8022_list = NULL;
+
+static struct datalink_proto *
+find_8022_client(unsigned char type)
+{
+ struct datalink_proto *proto;
+
+ for (proto = p8022_list;
+ ((proto != NULL) && (*(proto->type) != type));
+ proto = proto->next)
+ ;
+
+ return proto;
+}
+
+int
+p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct datalink_proto *proto;
+
+ proto = find_8022_client(*(skb->h.raw));
+ if (proto != NULL) {
+ skb->h.raw += 3;
+ skb->len -= 3;
+ return proto->rcvfunc(skb, dev, pt);
+ }
+
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+static void
+p8022_datalink_header(struct datalink_proto *dl,
+ struct sk_buff *skb, unsigned char *dest_node)
+{
+ struct device *dev = skb->dev;
+ unsigned long len = skb->len;
+ unsigned long hard_len = dev->hard_header_len;
+ unsigned char *rawp;
+
+ dev->hard_header(skb->data, dev, len - hard_len,
+ dest_node, NULL, len - hard_len, skb);
+ rawp = skb->data + hard_len;
+ *rawp = dl->type[0];
+ rawp++;
+ *rawp = dl->type[0];
+ rawp++;
+ *rawp = 0x03; /* UI */
+ rawp++;
+ skb->h.raw = rawp;
+}
+
+static struct packet_type p8022_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_IPX),*/
+ NULL, /* All devices */
+ p8022_rcv,
+ NULL,
+ NULL,
+};
+
+
+void p8022_proto_init(struct net_proto *pro)
+{
+ p8022_packet_type.type=htons(ETH_P_802_2);
+ dev_add_pack(&p8022_packet_type);
+}
+
+struct datalink_proto *
+register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+{
+ struct datalink_proto *proto;
+
+ if (find_8022_client(type) != NULL)
+ return NULL;
+
+ proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+ if (proto != NULL) {
+ proto->type[0] = type;
+ proto->type_len = 1;
+ proto->rcvfunc = rcvfunc;
+ proto->header_length = 3;
+ proto->datalink_header = p8022_datalink_header;
+ proto->string_name = "802.2";
+ proto->next = p8022_list;
+ p8022_list = proto;
+ }
+
+ return proto;
+}
+
diff --git a/pfinet/linux-inet/p8022.h b/pfinet/linux-inet/p8022.h
new file mode 100644
index 00000000..52c676be
--- /dev/null
+++ b/pfinet/linux-inet/p8022.h
@@ -0,0 +1,2 @@
+struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+
diff --git a/pfinet/linux-inet/p8022call.h b/pfinet/linux-inet/p8022call.h
new file mode 100644
index 00000000..14f0c2ce
--- /dev/null
+++ b/pfinet/linux-inet/p8022call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void p8022_proto_init(struct net_proto *);
diff --git a/pfinet/linux-inet/p8023.c b/pfinet/linux-inet/p8023.c
new file mode 100644
index 00000000..7c76223d
--- /dev/null
+++ b/pfinet/linux-inet/p8023.c
@@ -0,0 +1,35 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static void
+p8023_datalink_header(struct datalink_proto *dl,
+ struct sk_buff *skb, unsigned char *dest_node)
+{
+ struct device *dev = skb->dev;
+ unsigned long len = skb->len;
+ unsigned long hard_len = dev->hard_header_len;
+
+ dev->hard_header(skb->data, dev, len - hard_len,
+ dest_node, NULL, len - hard_len, skb);
+ skb->h.raw = skb->data + hard_len;
+}
+
+struct datalink_proto *
+make_8023_client(void)
+{
+ struct datalink_proto *proto;
+
+ proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+ if (proto != NULL) {
+ proto->type_len = 0;
+ proto->header_length = 0;
+ proto->datalink_header = p8023_datalink_header;
+ proto->string_name = "802.3";
+ }
+
+ return proto;
+}
+
diff --git a/pfinet/linux-inet/packet.c b/pfinet/linux-inet/packet.c
new file mode 100644
index 00000000..ab031c81
--- /dev/null
+++ b/pfinet/linux-inet/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_
+ *
+ * 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 "ip.h"
+#include "protocol.h"
+#include <linux/skbuff.h>
+#include "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.
+ */
+
+ release_sock(sk); /* This is now effectively surplus in this layer */
+ 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 */
+
+ /*
+ * 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,
+ {NULL,},
+ "PACKET",
+ 0, 0
+};
diff --git a/pfinet/linux-inet/pe2.c b/pfinet/linux-inet/pe2.c
new file mode 100644
index 00000000..856e454b
--- /dev/null
+++ b/pfinet/linux-inet/pe2.c
@@ -0,0 +1,35 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static void
+pEII_datalink_header(struct datalink_proto *dl,
+ struct sk_buff *skb, unsigned char *dest_node)
+{
+ struct device *dev = skb->dev;
+ unsigned long len = skb->len;
+ unsigned long hard_len = dev->hard_header_len;
+
+ dev->hard_header(skb->data, dev, ETH_P_IPX,
+ dest_node, NULL, len - hard_len, skb);
+ skb->h.raw = skb->data + hard_len;
+}
+
+struct datalink_proto *
+make_EII_client(void)
+{
+ struct datalink_proto *proto;
+
+ proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+ if (proto != NULL) {
+ proto->type_len = 0;
+ proto->header_length = 0;
+ proto->datalink_header = pEII_datalink_header;
+ proto->string_name = "EtherII";
+ }
+
+ return proto;
+}
+
diff --git a/pfinet/linux-inet/protocol.c b/pfinet/linux-inet/protocol.c
new file mode 100644
index 00000000..a47d27cd
--- /dev/null
+++ b/pfinet/linux-inet/protocol.c
@@ -0,0 +1,177 @@
+/*
+ * 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 "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "icmp.h"
+#include "udp.h"
+#include <linux/igmp.h>
+
+
+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 */
+ NULL, /* next */
+ 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/pfinet/linux-inet/protocol.h b/pfinet/linux-inet/protocol.h
new file mode 100644
index 00000000..3e0b6fb3
--- /dev/null
+++ b/pfinet/linux-inet/protocol.h
@@ -0,0 +1,59 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the protocol dispatcher.
+ *
+ * Version: @(#)protocol.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.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.
+ *
+ * Changes:
+ * Alan Cox : Added a name field and a frag handler
+ * field for later.
+ */
+
+#ifndef _PROTOCOL_H
+#define _PROTOCOL_H
+
+
+#define MAX_INET_PROTOS 32 /* Must be a power of 2 */
+
+
+/* This is used to register protocols. */
+struct inet_protocol {
+ int (*handler)(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);
+ int (*frag_handler)(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);
+ void (*err_handler)(int err, unsigned char *buff,
+ unsigned long daddr,
+ unsigned long saddr,
+ struct inet_protocol *protocol);
+ struct inet_protocol *next;
+ unsigned char protocol;
+ unsigned char copy:1;
+ void *data;
+ char *name;
+};
+
+
+extern struct inet_protocol *inet_protocol_base;
+extern struct inet_protocol *inet_protos[MAX_INET_PROTOS];
+
+
+extern void inet_add_protocol(struct inet_protocol *prot);
+extern int inet_del_protocol(struct inet_protocol *prot);
+
+
+#endif /* _PROTOCOL_H */
diff --git a/pfinet/linux-inet/psnap.c b/pfinet/linux-inet/psnap.c
new file mode 100644
index 00000000..287b3353
--- /dev/null
+++ b/pfinet/linux-inet/psnap.c
@@ -0,0 +1,123 @@
+/*
+ * SNAP data link layer. Derived from 802.2
+ *
+ * Alan Cox <Alan.Cox@linux.org>, from the 802.2 layer by Greg Page.
+ * Merged in additions from Greg Page's psnap.c.
+ *
+ * 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/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include "p8022.h"
+#include "psnap.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static struct datalink_proto *snap_list = NULL;
+static struct datalink_proto *snap_dl = NULL; /* 802.2 DL for SNAP */
+
+/*
+ * Find a snap client by matching the 5 bytes.
+ */
+
+static struct datalink_proto *find_snap_client(unsigned char *desc)
+{
+ struct datalink_proto *proto;
+
+ for (proto = snap_list; proto != NULL && memcmp(proto->type, desc, 5) ; proto = proto->next);
+ return proto;
+}
+
+/*
+ * A SNAP packet has arrived
+ */
+
+int snap_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ static struct packet_type psnap_packet_type =
+ {
+ 0,
+ NULL, /* All Devices */
+ snap_rcv,
+ NULL,
+ NULL,
+ };
+
+ struct datalink_proto *proto;
+
+ proto = find_snap_client(skb->h.raw);
+ if (proto != NULL)
+ {
+ /*
+ * Pass the frame on.
+ */
+
+ skb->h.raw += 5;
+ skb->len -= 5;
+ if (psnap_packet_type.type == 0)
+ psnap_packet_type.type=htons(ETH_P_SNAP);
+ return proto->rcvfunc(skb, dev, &psnap_packet_type);
+ }
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+/*
+ * Put a SNAP header on a frame and pass to 802.2
+ */
+
+static void snap_datalink_header(struct datalink_proto *dl, struct sk_buff *skb, unsigned char *dest_node)
+{
+ struct device *dev = skb->dev;
+ unsigned char *rawp;
+
+ rawp = skb->data + snap_dl->header_length+dev->hard_header_len;
+ memcpy(rawp,dl->type,5);
+ skb->h.raw = rawp+5;
+ snap_dl->datalink_header(snap_dl, skb, dest_node);
+}
+
+/*
+ * Set up the SNAP layer
+ */
+
+void snap_proto_init(struct net_proto *pro)
+{
+ snap_dl=register_8022_client(0xAA, snap_rcv);
+ if(snap_dl==NULL)
+ printk("SNAP - unable to register with 802.2\n");
+}
+
+/*
+ * Register SNAP clients. We don't yet use this for IP or IPX.
+ */
+
+struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+{
+ struct datalink_proto *proto;
+
+ if (find_snap_client(desc) != NULL)
+ return NULL;
+
+ proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+ if (proto != NULL)
+ {
+ memcpy(proto->type, desc,5);
+ proto->type_len = 5;
+ proto->rcvfunc = rcvfunc;
+ proto->header_length = 5+snap_dl->header_length;
+ proto->datalink_header = snap_datalink_header;
+ proto->string_name = "SNAP";
+ proto->next = snap_list;
+ snap_list = proto;
+ }
+
+ return proto;
+}
+
diff --git a/pfinet/linux-inet/psnap.h b/pfinet/linux-inet/psnap.h
new file mode 100644
index 00000000..b69859db
--- /dev/null
+++ b/pfinet/linux-inet/psnap.h
@@ -0,0 +1,2 @@
+struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+
diff --git a/pfinet/linux-inet/psnapcall.h b/pfinet/linux-inet/psnapcall.h
new file mode 100644
index 00000000..9da5763c
--- /dev/null
+++ b/pfinet/linux-inet/psnapcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void snap_proto_init(struct net_proto *);
diff --git a/pfinet/linux-inet/rarp.c b/pfinet/linux-inet/rarp.c
new file mode 100644
index 00000000..72924bb2
--- /dev/null
+++ b/pfinet/linux-inet/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 "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+#include "rarp.h"
+#ifdef CONFIG_AX25
+#include "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/pfinet/linux-inet/rarp.h b/pfinet/linux-inet/rarp.h
new file mode 100644
index 00000000..02ee7784
--- /dev/null
+++ b/pfinet/linux-inet/rarp.h
@@ -0,0 +1,14 @@
+/* linux/net/inet/rarp.h */
+#ifndef _RARP_H
+#define _RARP_H
+
+extern int rarp_ioctl(unsigned int cmd, void *arg);
+extern int rarp_rcv(struct sk_buff *skb,
+ struct device *dev,
+ struct packet_type *pt);
+extern int rarp_get_info(char *buffer,
+ char **start,
+ off_t offset,
+ int length);
+#endif /* _RARP_H */
+
diff --git a/pfinet/linux-inet/raw.h b/pfinet/linux-inet/raw.h
new file mode 100644
index 00000000..8f1cf0c2
--- /dev/null
+++ b/pfinet/linux-inet/raw.h
@@ -0,0 +1,34 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the RAW-IP module.
+ *
+ * Version: @(#)raw.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.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.
+ */
+#ifndef _RAW_H
+#define _RAW_H
+
+
+extern struct proto raw_prot;
+
+
+extern void raw_err(int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol);
+extern int raw_recvfrom(struct sock *sk, unsigned char *to,
+ int len, int noblock, unsigned flags,
+ struct sockaddr_in *sin, int *addr_len);
+extern int raw_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags);
+extern int raw_rcv(struct sock *, struct sk_buff *, struct device *,
+ long, long);
+
+#endif /* _RAW_H */
diff --git a/pfinet/linux-inet/route.h b/pfinet/linux-inet/route.h
new file mode 100644
index 00000000..a693ffb4
--- /dev/null
+++ b/pfinet/linux-inet/route.h
@@ -0,0 +1,53 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP router.
+ *
+ * Version: @(#)route.h 1.0.4 05/27/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Fixes:
+ * Alan Cox : Reformatted. Added ip_rt_local()
+ * Alan Cox : Support for TCP parameters.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ROUTE_H
+#define _ROUTE_H
+
+
+#include <linux/route.h>
+
+
+/* This is an entry in the IP routing table. */
+struct rtable
+{
+ struct rtable *rt_next;
+ unsigned long rt_dst;
+ unsigned long rt_mask;
+ unsigned long rt_gateway;
+ unsigned char rt_flags;
+ unsigned char rt_metric;
+ short rt_refcnt;
+ unsigned long rt_use;
+ unsigned short rt_mss;
+ unsigned long rt_window;
+ struct device *rt_dev;
+};
+
+
+extern void ip_rt_flush(struct device *dev);
+extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask,
+ unsigned long gw, struct device *dev, unsigned short mss, unsigned long window);
+extern struct rtable *ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr);
+extern struct rtable *ip_rt_local(unsigned long daddr, struct options *opt, unsigned long *src_addr);
+extern int rt_get_info(char * buffer, char **start, off_t offset, int length);
+extern int ip_rt_ioctl(unsigned int cmd, void *arg);
+
+#endif /* _ROUTE_H */
diff --git a/pfinet/linux-inet/skbuff.c b/pfinet/linux-inet/skbuff.c
new file mode 100644
index 00000000..e4e1d247
--- /dev/null
+++ b/pfinet/linux-inet/skbuff.c
@@ -0,0 +1,573 @@
+/*
+ * Routines having to do with the 'struct sk_buff' memory handlers.
+ *
+ * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
+ * Florian La Roche <rzsfl@rz.uni-sb.de>
+ *
+ * Fixes:
+ * Alan Cox : Fixed the worst of the load balancer bugs.
+ * Dave Platt : Interrupt stacking fix
+ *
+ * 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.
+ */
+
+/*
+ * Note: There are a load of cli()/sti() pairs protecting the net_memory type
+ * variables. Without them for some reason the ++/-- operators do not come out
+ * atomic. Also with gcc 2.4.5 these counts can come out wrong anyway - use 2.5.8!!
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include <linux/string.h>
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+
+
+/*
+ * Resource tracking variables
+ */
+
+volatile unsigned long net_memory = 0;
+volatile unsigned long net_skbcount = 0;
+volatile unsigned long net_locked = 0;
+volatile unsigned long net_allocs = 0;
+volatile unsigned long net_fails = 0;
+volatile unsigned long net_free_locked = 0;
+
+void show_net_buffers(void)
+{
+ printk("Networking buffers in use : %lu\n",net_skbcount);
+ printk("Memory committed to network buffers: %lu\n",net_memory);
+ printk("Network buffers locked by drivers : %lu\n",net_locked);
+ printk("Total network buffer allocations : %lu\n",net_allocs);
+ printk("Total failed network buffer allocs : %lu\n",net_fails);
+ printk("Total free while locked events : %lu\n",net_free_locked);
+}
+
+#if CONFIG_SKB_CHECK
+
+/*
+ * Debugging paranoia. Can go later when this crud stack works
+ */
+
+int skb_check(struct sk_buff *skb, int head, int line, char *file)
+{
+ if (head) {
+ if (skb->magic_debug_cookie != SK_HEAD_SKB) {
+ printk("File: %s Line %d, found a bad skb-head\n",
+ file,line);
+ return -1;
+ }
+ if (!skb->next || !skb->prev) {
+ printk("skb_check: head without next or prev\n");
+ return -1;
+ }
+ if (skb->next->magic_debug_cookie != SK_HEAD_SKB
+ && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad next head-skb member\n",
+ file,line);
+ return -1;
+ }
+ if (skb->prev->magic_debug_cookie != SK_HEAD_SKB
+ && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad prev head-skb member\n",
+ file,line);
+ return -1;
+ }
+#if 0
+ {
+ struct sk_buff *skb2 = skb->next;
+ int i = 0;
+ while (skb2 != skb && i < 5) {
+ if (skb_check(skb2, 0, line, file) < 0) {
+ printk("bad queue element in whole queue\n");
+ return -1;
+ }
+ i++;
+ skb2 = skb2->next;
+ }
+ }
+#endif
+ return 0;
+ }
+ if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
+ && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad next skb member\n",
+ file,line);
+ return -1;
+ }
+ if (skb->prev != NULL && skb->prev->magic_debug_cookie != SK_HEAD_SKB
+ && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad prev skb member\n",
+ file,line);
+ return -1;
+ }
+
+
+ if(skb->magic_debug_cookie==SK_FREED_SKB)
+ {
+ printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
+ file,line);
+ printk("skb=%p, real size=%ld, claimed size=%ld, free=%d\n",
+ skb,skb->truesize,skb->mem_len,skb->free);
+ return -1;
+ }
+ if(skb->magic_debug_cookie!=SK_GOOD_SKB)
+ {
+ printk("File: %s Line %d, passed a non skb!\n", file,line);
+ printk("skb=%p, real size=%ld, claimed size=%ld, free=%d\n",
+ skb,skb->truesize,skb->mem_len,skb->free);
+ return -1;
+ }
+ if(skb->mem_len!=skb->truesize)
+ {
+ printk("File: %s Line %d, Dubious size setting!\n",file,line);
+ printk("skb=%p, real size=%ld, claimed size=%ld\n",
+ skb,skb->truesize,skb->mem_len);
+ return -1;
+ }
+ /* Guess it might be acceptable then */
+ return 0;
+}
+#endif
+
+
+#ifdef CONFIG_SKB_CHECK
+void skb_queue_head_init(struct sk_buff_head *list)
+{
+ list->prev = (struct sk_buff *)list;
+ list->next = (struct sk_buff *)list;
+ list->magic_debug_cookie = SK_HEAD_SKB;
+}
+
+
+/*
+ * Insert an sk_buff at the start of a list.
+ */
+void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
+{
+ unsigned long flags;
+ struct sk_buff *list = (struct sk_buff *)list_;
+
+ save_flags(flags);
+ cli();
+
+ IS_SKB(newsk);
+ IS_SKB_HEAD(list);
+ if (newsk->next || newsk->prev)
+ printk("Suspicious queue head: sk_buff on list!\n");
+
+ newsk->next = list->next;
+ newsk->prev = list;
+
+ newsk->next->prev = newsk;
+ newsk->prev->next = newsk;
+
+ restore_flags(flags);
+}
+
+/*
+ * Insert an sk_buff at the end of a list.
+ */
+void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
+{
+ unsigned long flags;
+ struct sk_buff *list = (struct sk_buff *)list_;
+
+ save_flags(flags);
+ cli();
+
+ if (newsk->next || newsk->prev)
+ printk("Suspicious queue tail: sk_buff on list!\n");
+ IS_SKB(newsk);
+ IS_SKB_HEAD(list);
+
+ newsk->next = list;
+ newsk->prev = list->prev;
+
+ newsk->next->prev = newsk;
+ newsk->prev->next = newsk;
+
+ restore_flags(flags);
+}
+
+/*
+ * Remove an sk_buff from a list. This routine is also interrupt safe
+ * so you can grab read and free buffers as another process adds them.
+ */
+
+struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
+{
+ long flags;
+ struct sk_buff *result;
+ struct sk_buff *list = (struct sk_buff *)list_;
+
+ save_flags(flags);
+ cli();
+
+ IS_SKB_HEAD(list);
+
+ result = list->next;
+ if (result == list) {
+ restore_flags(flags);
+ return NULL;
+ }
+
+ result->next->prev = list;
+ list->next = result->next;
+
+ result->next = NULL;
+ result->prev = NULL;
+
+ restore_flags(flags);
+
+ IS_SKB(result);
+ return result;
+}
+
+/*
+ * Insert a packet before another one in a list.
+ */
+void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ IS_SKB(old);
+ IS_SKB(newsk);
+
+ if(!old->next || !old->prev)
+ printk("insert before unlisted item!\n");
+ if(newsk->next || newsk->prev)
+ printk("inserted item is already on a list.\n");
+
+ save_flags(flags);
+ cli();
+ newsk->next = old;
+ newsk->prev = old->prev;
+ old->prev = newsk;
+ newsk->prev->next = newsk;
+
+ restore_flags(flags);
+}
+
+/*
+ * Place a packet after a given packet in a list.
+ */
+void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ IS_SKB(old);
+ IS_SKB(newsk);
+
+ if(!old->next || !old->prev)
+ printk("append before unlisted item!\n");
+ if(newsk->next || newsk->prev)
+ printk("append item is already on a list.\n");
+
+ save_flags(flags);
+ cli();
+
+ newsk->prev = old;
+ newsk->next = old->next;
+ newsk->next->prev = newsk;
+ old->next = newsk;
+
+ restore_flags(flags);
+}
+
+/*
+ * Remove an sk_buff from its list. Works even without knowing the list it
+ * is sitting on, which can be handy at times. It also means that THE LIST
+ * MUST EXIST when you unlink. Thus a list must have its contents unlinked
+ * _FIRST_.
+ */
+void skb_unlink(struct sk_buff *skb)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ IS_SKB(skb);
+
+ if(skb->prev && skb->next)
+ {
+ skb->next->prev = skb->prev;
+ skb->prev->next = skb->next;
+ skb->next = NULL;
+ skb->prev = NULL;
+ }
+#ifdef PARANOID_BUGHUNT_MODE /* This is legal but we sometimes want to watch it */
+ else
+ printk("skb_unlink: not a linked element\n");
+#endif
+ restore_flags(flags);
+}
+
+#endif
+
+/*
+ * Free an sk_buff. This still knows about things it should
+ * not need to like protocols and sockets.
+ */
+
+void kfree_skb(struct sk_buff *skb, int rw)
+{
+ if (skb == NULL)
+ {
+ printk("kfree_skb: skb = NULL (from %p)\n",
+ __builtin_return_address(0));
+ return;
+ }
+#ifdef CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ if (skb->lock)
+ {
+ skb->free = 3; /* Free when unlocked */
+ net_free_locked++;
+ return;
+ }
+ if (skb->free == 2)
+ printk("Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p)\n",
+ __builtin_return_address(0));
+ if (skb->next)
+ printk("Warning: kfree_skb passed an skb still on a list (from %p).\n",
+ __builtin_return_address(0));
+ if (skb->sk)
+ {
+ if(skb->sk->prot!=NULL)
+ {
+ if (rw)
+ skb->sk->prot->rfree(skb->sk, skb, skb->mem_len);
+ else
+ skb->sk->prot->wfree(skb->sk, skb, skb->mem_len);
+
+ }
+ else
+ {
+ unsigned long flags;
+ /* Non INET - default wmalloc/rmalloc handler */
+ save_flags(flags);
+ cli();
+ if (rw)
+ skb->sk->rmem_alloc-=skb->mem_len;
+ else
+ skb->sk->wmem_alloc-=skb->mem_len;
+ restore_flags(flags);
+ if(!skb->sk->dead)
+ skb->sk->write_space(skb->sk);
+ kfree_skbmem(skb,skb->mem_len);
+ }
+ }
+ else
+ kfree_skbmem(skb, skb->mem_len);
+}
+
+/*
+ * Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
+ * fields and also do memory statistics to find all the [BEEP] leaks.
+ */
+struct sk_buff *alloc_skb(unsigned int size,int priority)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ if (intr_count && priority!=GFP_ATOMIC) {
+ static int count = 0;
+ if (++count < 5) {
+ printk("alloc_skb called nonatomically from interrupt %p\n",
+ __builtin_return_address(0));
+ priority = GFP_ATOMIC;
+ }
+ }
+
+ size+=sizeof(struct sk_buff);
+ skb=(struct sk_buff *)kmalloc(size,priority);
+ if (skb == NULL)
+ {
+ net_fails++;
+ return NULL;
+ }
+#ifdef PARANOID_BUGHUNT_MODE
+ if(skb->magic_debug_cookie == SK_GOOD_SKB)
+ printk("Kernel kmalloc handed us an existing skb (%p)\n",skb);
+#endif
+
+ net_allocs++;
+
+ skb->free = 2; /* Invalid so we pick up forgetful users */
+ skb->lock = 0;
+ skb->pkt_type = PACKET_HOST; /* Default type */
+ skb->truesize = size;
+ skb->mem_len = size;
+ skb->mem_addr = skb;
+#ifdef CONFIG_SLAVE_BALANCING
+ skb->in_dev_queue = 0;
+#endif
+ skb->fraglist = NULL;
+ skb->prev = skb->next = NULL;
+ skb->link3 = NULL;
+ skb->sk = NULL;
+ skb->localroute=0;
+ skb->stamp.tv_sec=0; /* No idea about time */
+ skb->localroute = 0;
+ save_flags(flags);
+ cli();
+ net_memory += size;
+ net_skbcount++;
+ restore_flags(flags);
+#if CONFIG_SKB_CHECK
+ skb->magic_debug_cookie = SK_GOOD_SKB;
+#endif
+ skb->users = 0;
+ return skb;
+}
+
+/*
+ * Free an skbuff by memory
+ */
+
+void kfree_skbmem(struct sk_buff *skb,unsigned size)
+{
+ unsigned long flags;
+#ifdef CONFIG_SLAVE_BALANCING
+ save_flags(flags);
+ cli();
+ if(skb->in_dev_queue && skb->dev!=NULL)
+ skb->dev->pkt_queue--;
+ restore_flags(flags);
+#endif
+#ifdef CONFIG_SKB_CHECK
+ IS_SKB(skb);
+ if(size!=skb->truesize)
+ printk("kfree_skbmem: size mismatch.\n");
+
+ if(skb->magic_debug_cookie == SK_GOOD_SKB)
+ {
+ save_flags(flags);
+ cli();
+ IS_SKB(skb);
+ skb->magic_debug_cookie = SK_FREED_SKB;
+ kfree_s((void *)skb,size);
+ net_skbcount--;
+ net_memory -= size;
+ restore_flags(flags);
+ }
+ else
+ printk("kfree_skbmem: bad magic cookie\n");
+#else
+ save_flags(flags);
+ cli();
+ kfree_s((void *)skb,size);
+ net_skbcount--;
+ net_memory -= size;
+ restore_flags(flags);
+#endif
+}
+
+/*
+ * Duplicate an sk_buff. The new one is not owned by a socket or locked
+ * and will be freed on deletion.
+ */
+
+struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
+{
+ struct sk_buff *n;
+ unsigned long offset;
+
+ n=alloc_skb(skb->mem_len-sizeof(struct sk_buff),priority);
+ if(n==NULL)
+ return NULL;
+
+ offset=((char *)n)-((char *)skb);
+
+ memcpy(n->data,skb->data,skb->mem_len-sizeof(struct sk_buff));
+ n->len=skb->len;
+ n->link3=NULL;
+ n->sk=NULL;
+ n->when=skb->when;
+ n->dev=skb->dev;
+ n->h.raw=skb->h.raw+offset;
+ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
+ n->fraglen=skb->fraglen;
+ n->fraglist=skb->fraglist;
+ n->saddr=skb->saddr;
+ n->daddr=skb->daddr;
+ n->raddr=skb->raddr;
+ n->acked=skb->acked;
+ n->used=skb->used;
+ n->free=1;
+ n->arp=skb->arp;
+ n->tries=0;
+ n->lock=0;
+ n->users=0;
+ n->pkt_type=skb->pkt_type;
+ return n;
+}
+
+
+/*
+ * Skbuff device locking
+ */
+
+void skb_device_lock(struct sk_buff *skb)
+{
+ if(skb->lock)
+ printk("double lock on device queue!\n");
+ else
+ net_locked++;
+ skb->lock++;
+}
+
+void skb_device_unlock(struct sk_buff *skb)
+{
+ if(skb->lock==0)
+ printk("double unlock on device queue!\n");
+ skb->lock--;
+ if(skb->lock==0)
+ net_locked--;
+}
+
+void dev_kfree_skb(struct sk_buff *skb, int mode)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if(skb->lock==1)
+ net_locked--;
+
+ if (!--skb->lock && (skb->free == 1 || skb->free == 3))
+ {
+ restore_flags(flags);
+ kfree_skb(skb,mode);
+ }
+ else
+ restore_flags(flags);
+}
+
+int skb_device_locked(struct sk_buff *skb)
+{
+ return skb->lock? 1 : 0;
+}
+
diff --git a/pfinet/linux-inet/snmp.h b/pfinet/linux-inet/snmp.h
new file mode 100644
index 00000000..552292be
--- /dev/null
+++ b/pfinet/linux-inet/snmp.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * SNMP MIB entries for the IP subsystem.
+ *
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ * We don't chose to implement SNMP in the kernel (this would
+ * be silly as SNMP is a pain in the backside in places). We do
+ * however need to collect the MIB statistics and export them
+ * out of /proc (eventually)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _SNMP_H
+#define _SNMP_H
+
+/*
+ * We use all unsigned longs. Linux will soon be so reliable that even these
+ * will rapidly get too small 8-). Seriously consider the IpInReceives count
+ * on the 20Gb/s + networks people expect in a few years time!
+ */
+
+struct ip_mib
+{
+ unsigned long IpForwarding;
+ unsigned long IpDefaultTTL;
+ unsigned long IpInReceives;
+ unsigned long IpInHdrErrors;
+ unsigned long IpInAddrErrors;
+ unsigned long IpForwDatagrams;
+ unsigned long IpInUnknownProtos;
+ unsigned long IpInDiscards;
+ unsigned long IpInDelivers;
+ unsigned long IpOutRequests;
+ unsigned long IpOutDiscards;
+ unsigned long IpOutNoRoutes;
+ unsigned long IpReasmTimeout;
+ unsigned long IpReasmReqds;
+ unsigned long IpReasmOKs;
+ unsigned long IpReasmFails;
+ unsigned long IpFragOKs;
+ unsigned long IpFragFails;
+ unsigned long IpFragCreates;
+};
+
+
+struct icmp_mib
+{
+ unsigned long IcmpInMsgs;
+ unsigned long IcmpInErrors;
+ unsigned long IcmpInDestUnreachs;
+ unsigned long IcmpInTimeExcds;
+ unsigned long IcmpInParmProbs;
+ unsigned long IcmpInSrcQuenchs;
+ unsigned long IcmpInRedirects;
+ unsigned long IcmpInEchos;
+ unsigned long IcmpInEchoReps;
+ unsigned long IcmpInTimestamps;
+ unsigned long IcmpInTimestampReps;
+ unsigned long IcmpInAddrMasks;
+ unsigned long IcmpInAddrMaskReps;
+ unsigned long IcmpOutMsgs;
+ unsigned long IcmpOutErrors;
+ unsigned long IcmpOutDestUnreachs;
+ unsigned long IcmpOutTimeExcds;
+ unsigned long IcmpOutParmProbs;
+ unsigned long IcmpOutSrcQuenchs;
+ unsigned long IcmpOutRedirects;
+ unsigned long IcmpOutEchos;
+ unsigned long IcmpOutEchoReps;
+ unsigned long IcmpOutTimestamps;
+ unsigned long IcmpOutTimestampReps;
+ unsigned long IcmpOutAddrMasks;
+ unsigned long IcmpOutAddrMaskReps;
+};
+
+struct tcp_mib
+{
+ unsigned long TcpRtoAlgorithm;
+ unsigned long TcpRtoMin;
+ unsigned long TcpRtoMax;
+ unsigned long TcpMaxConn;
+ unsigned long TcpActiveOpens;
+ unsigned long TcpPassiveOpens;
+ unsigned long TcpAttemptFails;
+ unsigned long TcpEstabResets;
+ unsigned long TcpCurrEstab;
+ unsigned long TcpInSegs;
+ unsigned long TcpOutSegs;
+ unsigned long TcpRetransSegs;
+};
+
+struct udp_mib
+{
+ unsigned long UdpInDatagrams;
+ unsigned long UdpNoPorts;
+ unsigned long UdpInErrors;
+ unsigned long UdpOutDatagrams;
+};
+
+
+#endif
diff --git a/pfinet/linux-inet/sock.c b/pfinet/linux-inet/sock.c
new file mode 100644
index 00000000..40d4a8f4
--- /dev/null
+++ b/pfinet/linux-inet/sock.c
@@ -0,0 +1,574 @@
+/*
+ * 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.
+ *
+ * Generic socket support routines. Memory allocators, sk->inuse/release
+ * handler for protocols to use and generic option handler.
+ *
+ *
+ * Version: @(#)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>
+ *
+ * Fixes:
+ * Alan Cox : Numerous verify_area() problems
+ * Alan Cox : Connecting on a connecting socket
+ * now returns an error for tcp.
+ * Alan Cox : sock->protocol is set correctly.
+ * and is not sometimes left as 0.
+ * Alan Cox : connect handles icmp errors on a
+ * connect properly. Unfortunately there
+ * is a restart syscall nasty there. I
+ * can't match BSD without hacking the C
+ * library. Ideas urgently sought!
+ * Alan Cox : Disallow bind() to addresses that are
+ * not ours - especially broadcast ones!!
+ * Alan Cox : Socket 1024 _IS_ ok for users. (fencepost)
+ * Alan Cox : sock_wfree/sock_rfree don't destroy sockets,
+ * instead they leave that for the DESTROY timer.
+ * Alan Cox : Clean up error flag in accept
+ * Alan Cox : TCP ack handling is buggy, the DESTROY timer
+ * was buggy. Put a remove_sock() in the handler
+ * for memory when we hit 0. Also altered the timer
+ * code. The ACK stuff can wait and needs major
+ * TCP layer surgery.
+ * Alan Cox : Fixed TCP ack bug, removed remove sock
+ * and fixed timer/inet_bh race.
+ * Alan Cox : Added zapped flag for TCP
+ * Alan Cox : Move kfree_skb into skbuff.c and tidied up surplus code
+ * Alan Cox : for new sk_buff allocations wmalloc/rmalloc now call alloc_skb
+ * Alan Cox : kfree_s calls now are kfree_skbmem so we can track skb resources
+ * Alan Cox : Supports socket option broadcast now as does udp. Packet and raw need fixing.
+ * Alan Cox : Added RCVBUF,SNDBUF size setting. It suddenly occurred to me how easy it was so...
+ * Rick Sladkey : Relaxed UDP rules for matching packets.
+ * C.E.Hawkins : IFF_PROMISC/SIOCGHWADDR support
+ * Pauline Middelink : identd support
+ * Alan Cox : Fixed connect() taking signals I think.
+ * Alan Cox : SO_LINGER supported
+ * Alan Cox : Error reporting fixes
+ * Anonymous : inet_create tidied up (sk->reuse setting)
+ * Alan Cox : inet sockets don't set sk->type!
+ * Alan Cox : Split socket option code
+ * Alan Cox : Callbacks
+ * Alan Cox : Nagle flag for Charles & Johannes stuff
+ * Alex : Removed restriction on inet fioctl
+ * Alan Cox : Splitting INET from NET core
+ * Alan Cox : Fixed bogus SO_TYPE handling in getsockopt()
+ * Adam Caldwell : Missing return in SO_DONTROUTE/SO_DEBUG code
+ * Alan Cox : Split IP from generic code
+ * Alan Cox : New kfree_skbmem()
+ * Alan Cox : Make SO_DEBUG superuser only.
+ * Alan Cox : Allow anyone to clear SO_DEBUG
+ * (compatibility fix)
+ *
+ * To Fix:
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "arp.h"
+#include "rarp.h"
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "raw.h"
+#include "icmp.h"
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+/*
+ * This is meant for all protocols to use and covers goings on
+ * at the socket level. Everything here is generic.
+ */
+
+int sock_setsockopt(struct sock *sk, int level, int optname,
+ char *optval, int optlen)
+{
+ int val;
+ int err;
+ struct linger ling;
+
+ 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 SO_TYPE:
+ case SO_ERROR:
+ return(-ENOPROTOOPT);
+
+ case SO_DEBUG:
+ if(val && !suser())
+ return(-EPERM);
+ sk->debug=val?1:0;
+ return 0;
+ case SO_DONTROUTE:
+ sk->localroute=val?1:0;
+ return 0;
+ case SO_BROADCAST:
+ sk->broadcast=val?1:0;
+ return 0;
+ case SO_SNDBUF:
+ if(val>32767)
+ val=32767;
+ if(val<256)
+ val=256;
+ sk->sndbuf=val;
+ return 0;
+ case SO_LINGER:
+ err=verify_area(VERIFY_READ,optval,sizeof(ling));
+ if(err)
+ return err;
+ memcpy_fromfs(&ling,optval,sizeof(ling));
+ if(ling.l_onoff==0)
+ sk->linger=0;
+ else
+ {
+ sk->lingertime=ling.l_linger;
+ sk->linger=1;
+ }
+ return 0;
+ case SO_RCVBUF:
+ if(val>32767)
+ val=32767;
+ if(val<256)
+ val=256;
+ sk->rcvbuf=val;
+ return(0);
+
+ case SO_REUSEADDR:
+ if (val)
+ sk->reuse = 1;
+ else
+ sk->reuse = 0;
+ return(0);
+
+ case SO_KEEPALIVE:
+ if (val)
+ sk->keepopen = 1;
+ else
+ sk->keepopen = 0;
+ return(0);
+
+ case SO_OOBINLINE:
+ if (val)
+ sk->urginline = 1;
+ else
+ sk->urginline = 0;
+ return(0);
+
+ case SO_NO_CHECK:
+ if (val)
+ sk->no_check = 1;
+ else
+ sk->no_check = 0;
+ return(0);
+
+ case SO_PRIORITY:
+ if (val >= 0 && val < DEV_NUMBUFFS)
+ {
+ sk->priority = val;
+ }
+ else
+ {
+ return(-EINVAL);
+ }
+ return(0);
+
+ default:
+ return(-ENOPROTOOPT);
+ }
+}
+
+
+int sock_getsockopt(struct sock *sk, int level, int optname,
+ char *optval, int *optlen)
+{
+ int val;
+ int err;
+ struct linger ling;
+
+ switch(optname)
+ {
+ case SO_DEBUG:
+ val = sk->debug;
+ break;
+
+ case SO_DONTROUTE:
+ val = sk->localroute;
+ break;
+
+ case SO_BROADCAST:
+ val= sk->broadcast;
+ break;
+
+ case SO_LINGER:
+ err=verify_area(VERIFY_WRITE,optval,sizeof(ling));
+ if(err)
+ return err;
+ err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
+ if(err)
+ return err;
+ put_fs_long(sizeof(ling),(unsigned long *)optlen);
+ ling.l_onoff=sk->linger;
+ ling.l_linger=sk->lingertime;
+ memcpy_tofs(optval,&ling,sizeof(ling));
+ return 0;
+
+ case SO_SNDBUF:
+ val=sk->sndbuf;
+ break;
+
+ case SO_RCVBUF:
+ val =sk->rcvbuf;
+ break;
+
+ case SO_REUSEADDR:
+ val = sk->reuse;
+ break;
+
+ case SO_KEEPALIVE:
+ val = sk->keepopen;
+ break;
+
+ case SO_TYPE:
+#if 0
+ if (sk->prot == &tcp_prot)
+ val = SOCK_STREAM;
+ else
+ val = SOCK_DGRAM;
+#endif
+ val = sk->type;
+ break;
+
+ case SO_ERROR:
+ val = sk->err;
+ sk->err = 0;
+ break;
+
+ case SO_OOBINLINE:
+ val = sk->urginline;
+ break;
+
+ case SO_NO_CHECK:
+ val = sk->no_check;
+ break;
+
+ case SO_PRIORITY:
+ val = sk->priority;
+ 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 sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
+{
+ if (sk)
+ {
+ if (sk->wmem_alloc + size < sk->sndbuf || force)
+ {
+ struct sk_buff * c = alloc_skb(size, priority);
+ if (c)
+ {
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ sk->wmem_alloc+= c->mem_len;
+ restore_flags(flags); /* was sti(); */
+ }
+ return c;
+ }
+ return(NULL);
+ }
+ return(alloc_skb(size, priority));
+}
+
+
+struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
+{
+ if (sk)
+ {
+ if (sk->rmem_alloc + size < sk->rcvbuf || force)
+ {
+ struct sk_buff *c = alloc_skb(size, priority);
+ if (c)
+ {
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ sk->rmem_alloc += c->mem_len;
+ restore_flags(flags); /* was sti(); */
+ }
+ return(c);
+ }
+ return(NULL);
+ }
+ return(alloc_skb(size, priority));
+}
+
+
+unsigned long sock_rspace(struct sock *sk)
+{
+ int amt;
+
+ if (sk != NULL)
+ {
+ if (sk->rmem_alloc >= sk->rcvbuf-2*MIN_WINDOW)
+ return(0);
+ amt = min((sk->rcvbuf-sk->rmem_alloc)/2-MIN_WINDOW, MAX_WINDOW);
+ if (amt < 0)
+ return(0);
+ return(amt);
+ }
+ return(0);
+}
+
+
+unsigned long sock_wspace(struct sock *sk)
+{
+ if (sk != NULL)
+ {
+ if (sk->shutdown & SEND_SHUTDOWN)
+ return(0);
+ if (sk->wmem_alloc >= sk->sndbuf)
+ return(0);
+ return(sk->sndbuf-sk->wmem_alloc );
+ }
+ return(0);
+}
+
+
+void sock_wfree(struct sock *sk, struct sk_buff *skb, unsigned long size)
+{
+#ifdef CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ kfree_skbmem(skb, size);
+ if (sk)
+ {
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ sk->wmem_alloc -= size;
+ restore_flags(flags);
+ /* In case it might be waiting for more memory. */
+ if (!sk->dead)
+ sk->write_space(sk);
+ return;
+ }
+}
+
+
+void sock_rfree(struct sock *sk, struct sk_buff *skb, unsigned long size)
+{
+#ifdef CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ kfree_skbmem(skb, size);
+ if (sk)
+ {
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ sk->rmem_alloc -= size;
+ restore_flags(flags);
+ }
+}
+
+/*
+ * Generic send/receive buffer handlers
+ */
+
+struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, int noblock, int *errcode)
+{
+ struct sk_buff *skb;
+ int err;
+
+ sk->inuse=1;
+
+ do
+ {
+ if(sk->err!=0)
+ {
+ cli();
+ err= -sk->err;
+ sk->err=0;
+ sti();
+ *errcode=err;
+ return NULL;
+ }
+
+ if(sk->shutdown&SEND_SHUTDOWN)
+ {
+ *errcode=-EPIPE;
+ return NULL;
+ }
+
+ skb = sock_wmalloc(sk, size, 0, GFP_KERNEL);
+
+ if(skb==NULL)
+ {
+ unsigned long tmp;
+
+ sk->socket->flags |= SO_NOSPACE;
+ if(noblock)
+ {
+ *errcode=-EAGAIN;
+ return NULL;
+ }
+ if(sk->shutdown&SEND_SHUTDOWN)
+ {
+ *errcode=-EPIPE;
+ return NULL;
+ }
+ tmp = sk->wmem_alloc;
+ cli();
+ if(sk->shutdown&SEND_SHUTDOWN)
+ {
+ sti();
+ *errcode=-EPIPE;
+ return NULL;
+ }
+
+ if( tmp <= sk->wmem_alloc)
+ {
+ sk->socket->flags &= ~SO_NOSPACE;
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked)
+ {
+ sti();
+ *errcode = -ERESTARTSYS;
+ return NULL;
+ }
+ }
+ sti();
+ }
+ }
+ while(skb==NULL);
+
+ return skb;
+}
+
+/*
+ * Queue a received datagram if it will fit. Stream and sequenced protocols
+ * can't normally use this as they need to fit buffers in and play with them.
+ */
+
+int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ unsigned long flags;
+ if(sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
+ return -ENOMEM;
+ save_flags(flags);
+ cli();
+ sk->rmem_alloc+=skb->mem_len;
+ skb->sk=sk;
+ restore_flags(flags);
+ skb_queue_tail(&sk->receive_queue,skb);
+ if(!sk->dead)
+ sk->data_ready(sk,skb->len);
+ return 0;
+}
+
+void release_sock(struct sock *sk)
+{
+ unsigned long flags;
+#ifdef CONFIG_INET
+ struct sk_buff *skb;
+#endif
+
+ if (!sk->prot)
+ return;
+ /*
+ * Make the backlog atomic. If we don't do this there is a tiny
+ * window where a packet may arrive between the sk->blog being
+ * tested and then set with sk->inuse still 0 causing an extra
+ * unwanted re-entry into release_sock().
+ */
+
+ save_flags(flags);
+ cli();
+ if (sk->blog)
+ {
+ restore_flags(flags);
+ return;
+ }
+ sk->blog=1;
+ sk->inuse = 1;
+ restore_flags(flags);
+#ifdef CONFIG_INET
+ /* See if we have any packets built up. */
+ while((skb = skb_dequeue(&sk->back_log)) != NULL)
+ {
+ sk->blog = 1;
+ if (sk->prot->rcv)
+ sk->prot->rcv(skb, skb->dev, sk->opt,
+ skb->saddr, skb->len, skb->daddr, 1,
+ /* Only used for/by raw sockets. */
+ (struct inet_protocol *)sk->pair);
+ }
+#endif
+ sk->blog = 0;
+ sk->inuse = 0;
+#ifdef CONFIG_INET
+ if (sk->dead && sk->state == TCP_CLOSE)
+ {
+ /* Should be about 2 rtt's */
+ reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
+ }
+#endif
+}
+
+
diff --git a/pfinet/linux-inet/tcp.h b/pfinet/linux-inet/tcp.h
new file mode 100644
index 00000000..016fa6dd
--- /dev/null
+++ b/pfinet/linux-inet/tcp.h
@@ -0,0 +1,142 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP module.
+ *
+ * Version: @(#)tcp.h 1.0.5 05/23/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.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.
+ */
+#ifndef _TCP_H
+#define _TCP_H
+
+#include <linux/tcp.h>
+
+#define MAX_SYN_SIZE 44 + MAX_HEADER
+#define MAX_FIN_SIZE 40 + MAX_HEADER
+#define MAX_ACK_SIZE 40 + MAX_HEADER
+#define MAX_RESET_SIZE 40 + MAX_HEADER
+#define MAX_WINDOW 16384
+#define MIN_WINDOW 2048
+#define MAX_ACK_BACKLOG 2
+#define MIN_WRITE_SPACE 2048
+#define TCP_WINDOW_DIFF 2048
+
+/* urg_data states */
+#define URG_VALID 0x0100
+#define URG_NOTYET 0x0200
+#define URG_READ 0x0400
+
+#define TCP_RETR1 7 /*
+ * This is how many retries it does before it
+ * tries to figure out if the gateway is
+ * down.
+ */
+
+#define TCP_RETR2 15 /*
+ * This should take at least
+ * 90 minutes to time out.
+ */
+
+#define TCP_TIMEOUT_LEN (15*60*HZ) /* should be about 15 mins */
+#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully
+ * close the socket, about 60 seconds */
+#define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */
+#define TCP_ACK_TIME (3*HZ) /* time to delay before sending an ACK */
+#define TCP_DONE_TIME 250 /* maximum time to wait before actually
+ * destroying a socket */
+#define TCP_WRITE_TIME 3000 /* initial time to wait for an ACK,
+ * after last transmit */
+#define TCP_TIMEOUT_INIT (3*HZ) /* RFC 1122 initial timeout value */
+#define TCP_SYN_RETRIES 5 /* number of times to retry opening a
+ * connection */
+#define TCP_PROBEWAIT_LEN 100 /* time to wait between probes when
+ * I've got something to write and
+ * there is no window */
+
+#define TCP_NO_CHECK 0 /* turn to one if you want the default
+ * to be no checksum */
+
+
+/*
+ * TCP option
+ */
+
+#define TCPOPT_NOP 1 /* Padding */
+#define TCPOPT_EOL 0 /* End of options */
+#define TCPOPT_MSS 2 /* Segment size negotiating */
+/*
+ * We don't use these yet, but they are for PAWS and big windows
+ */
+#define TCPOPT_WINDOW 3 /* Window scaling */
+#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
+
+
+/*
+ * The next routines deal with comparing 32 bit unsigned ints
+ * and worry about wraparound (automatic with unsigned arithmetic).
+ */
+
+extern __inline int before(unsigned long seq1, unsigned long seq2)
+{
+ return (long)(seq1-seq2) < 0;
+}
+
+extern __inline int after(unsigned long seq1, unsigned long seq2)
+{
+ return (long)(seq1-seq2) > 0;
+}
+
+
+/* is s2<=s1<=s3 ? */
+extern __inline int between(unsigned long seq1, unsigned long seq2, unsigned long seq3)
+{
+ return (after(seq1+1, seq2) && before(seq1, seq3+1));
+}
+
+
+/*
+ * List all states of a TCP socket that can be viewed as a "connected"
+ * state. This now includes TCP_SYN_RECV, although I am not yet fully
+ * convinced that this is the solution for the 'getpeername(2)'
+ * problem. Thanks to Stephen A. Wood <saw@cebaf.gov> -FvK
+ */
+extern __inline const int
+tcp_connected(const int state)
+{
+ return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT ||
+ state == TCP_FIN_WAIT1 || state == TCP_FIN_WAIT2 ||
+ state == TCP_SYN_RECV);
+}
+
+
+extern struct proto tcp_prot;
+
+
+extern void tcp_err(int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol);
+extern void tcp_shutdown (struct sock *sk, int how);
+extern 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);
+
+extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
+extern int tcp_select_window(struct sock *sk);
+extern void tcp_send_check(struct tcphdr *th, unsigned long saddr,
+ unsigned long daddr, int len, struct sock *sk);
+extern void tcp_send_probe0(struct sock *sk);
+extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
+extern struct sk_buff * tcp_dequeue_partial(struct sock *);
+
+
+#endif /* _TCP_H */
diff --git a/pfinet/linux-inet/timer.c b/pfinet/linux-inet/timer.c
new file mode 100644
index 00000000..4fbbc74b
--- /dev/null
+++ b/pfinet/linux-inet/timer.c
@@ -0,0 +1,264 @@
+/*
+ * 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 "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "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;
+#if 0
+ case TIME_PROBE0:
+ tcp_send_probe0(sk);
+ release_sock (sk);
+ break;
+ case TIME_WRITE: /* try to retransmit. */
+ /* 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
+ {
+ if (jiffies < skb->when + sk->rto)
+ {
+ reset_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
+ restore_flags(flags);
+ release_sock (sk);
+ break;
+ }
+ restore_flags(flags);
+ /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
+ sk->retransmits, sk->packets_out, sk->cong_window); */
+ sk->prot->retransmit (sk, 0);
+ if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+ || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1))
+ {
+ arp_destroy (sk->daddr, 0);
+ ip_route_check (sk->daddr);
+ }
+ if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2)
+ {
+ sk->err = ETIMEDOUT;
+ if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING)
+ {
+ sk->state = TCP_TIME_WAIT;
+ reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+ }
+ else
+ {
+ sk->prot->close (sk, 1);
+ break;
+ }
+ }
+ }
+ release_sock (sk);
+ break;
+ }
+ case TIME_KEEPOPEN:
+ /*
+ * this reset_timer() call is a hack, this is not
+ * how KEEPOPEN is supposed to work.
+ */
+ reset_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++;
+ if (sk->shutdown == SHUTDOWN_MASK)
+ {
+ sk->prot->close (sk, 1);
+ sk->state = TCP_CLOSE;
+ }
+ if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+ || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1))
+ {
+ arp_destroy (sk->daddr, 0);
+ ip_route_check (sk->daddr);
+ release_sock (sk);
+ break;
+ }
+ if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2)
+ {
+ arp_destroy (sk->daddr, 0);
+ sk->err = ETIMEDOUT;
+ if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2)
+ {
+ sk->state = TCP_TIME_WAIT;
+ if (!sk->dead)
+ sk->state_change(sk);
+ release_sock (sk);
+ }
+ else
+ {
+ sk->prot->close (sk, 1);
+ }
+ break;
+ }
+ release_sock (sk);
+ break;
+#endif
+ default:
+ printk ("net_timer: timer expired - reason %d is unknown\n", why);
+ release_sock (sk);
+ break;
+ }
+}
+
diff --git a/pfinet/linux-inet/udp.h b/pfinet/linux-inet/udp.h
new file mode 100644
index 00000000..6bfbb3cb
--- /dev/null
+++ b/pfinet/linux-inet/udp.h
@@ -0,0 +1,50 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the UDP module.
+ *
+ * Version: @(#)udp.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ * Alan Cox : Turned on udp checksums. I don't want to
+ * chase 'memory corruption' bugs that aren't!
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _UDP_H
+#define _UDP_H
+
+#include <linux/udp.h>
+
+
+#define UDP_NO_CHECK 0
+
+
+extern struct proto udp_prot;
+
+
+extern void udp_err(int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol);
+extern int udp_recvfrom(struct sock *sk, unsigned char *to,
+ int len, int noblock, unsigned flags,
+ struct sockaddr_in *sin, int *addr_len);
+extern int udp_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags);
+extern int udp_connect(struct sock *sk,
+ struct sockaddr_in *usin, int addr_len);
+extern 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);
+extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
+
+#endif /* _UDP_H */
diff --git a/pfinet/linux-inet/utils.c b/pfinet/linux-inet/utils.c
new file mode 100644
index 00000000..60bbb9f8
--- /dev/null
+++ b/pfinet/linux-inet/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 "ip.h"
+#include "protocol.h"
+#include "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 *) &in;
+ 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));
+}
+