diff options
Diffstat (limited to 'pfinet/linux-inet')
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 *) ∈ + 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)); +} + |