diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /net/appletalk | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'net/appletalk')
-rw-r--r-- | net/appletalk/Makefile | 35 | ||||
-rw-r--r-- | net/appletalk/aarp.c | 721 | ||||
-rw-r--r-- | net/appletalk/ddp.c | 1843 |
3 files changed, 2599 insertions, 0 deletions
diff --git a/net/appletalk/Makefile b/net/appletalk/Makefile new file mode 100644 index 000000000..a14da6dd9 --- /dev/null +++ b/net/appletalk/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the Linux TCP/IP (INET) layer. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S $< + + +OBJS := aarp.o ddp.o + + +appletalk.o: $(OBJS) + $(LD) -r -o appletalk.o $(OBJS) + +dep: + $(CPP) -M *.c > .depend + +tar: + tar -cvf /dev/f1 . + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c new file mode 100644 index 000000000..52a46347a --- /dev/null +++ b/net/appletalk/aarp.c @@ -0,0 +1,721 @@ +/* + * AARP: An implementation of the Appletalk aarp protocol for + * ethernet 'ELAP'. + * + * Alan Cox <Alan.Cox@linux.org> + * <iialan@www.linux.org.uk> + * + * This doesn't fit cleanly with the IP arp. This isn't a problem as + * the IP arp wants extracting from the device layer in 1.3.x anyway. + * [see the pre-1.3 test code for details 8)] + * + * FIXME: + * We ought to handle the retransmits with a single list and a + * seperate fast timer for when it is needed. + * + * 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. + * + * + * References: + * Inside Appletalk (2nd Ed). + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/if_ether.h> +#include <linux/inet.h> +#include <linux/notifier.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/datalink.h> +#include <net/psnap.h> +#include <net/atalk.h> + +#ifdef CONFIG_ATALK +/* + * Lists of aarp entries + */ + +struct aarp_entry +{ + /* These first two are only used for unresolved entries */ + unsigned long last_sent; /* Last time we xmitted the aarp request */ + struct sk_buff_head packet_queue; /* Queue of frames wait for resolution */ + unsigned long expires_at; /* Entry expiry time */ + struct at_addr target_addr; /* DDP Address */ + struct device *dev; /* Device to use */ + char hwaddr[6]; /* Physical i/f address of target/router */ + unsigned short xmit_count; /* When this hits 10 we give up */ + struct aarp_entry *next; /* Next entry in chain */ +}; + + +/* + * Hashed list of resolved and unresolved entries + */ + +static struct aarp_entry *resolved[AARP_HASH_SIZE], *unresolved[AARP_HASH_SIZE]; +static int unresolved_count=0; + +/* + * Used to walk the list and purge/kick entries. + */ + +static struct timer_list aarp_timer; + +/* + * Delete an aarp queue + */ + +static void aarp_expire(struct aarp_entry *a) +{ + struct sk_buff *skb; + + while((skb=skb_dequeue(&a->packet_queue))!=NULL) + kfree_skb(skb, FREE_WRITE); + kfree_s(a,sizeof(*a)); +} + +/* + * Send an aarp queue entry request + */ + +static void aarp_send_query(struct aarp_entry *a) +{ + static char aarp_eth_multicast[ETH_ALEN]={ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + struct device *dev=a->dev; + int len=dev->hard_header_len+sizeof(struct elapaarp)+aarp_dl->header_length; + struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC); + struct elapaarp *eah=(struct elapaarp *)(skb->data+dev->hard_header_len+aarp_dl->header_length); + struct at_addr *sat=atalk_find_dev_addr(dev); + + if(skb==NULL || sat==NULL) + return; + + /* + * Set up the buffer. + */ + + skb->arp = 1; + skb->free = 1; + skb->len = len; + skb->dev = a->dev; + + /* + * Set up the ARP. + */ + + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_REQUEST); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero= 0; + eah->pa_src_net = sat->s_net; + eah->pa_src_node= sat->s_node; + + memset(eah->hw_dst, '\0', ETH_ALEN); + + eah->pa_dst_zero= 0; + eah->pa_dst_net = a->target_addr.s_net; + eah->pa_dst_node= a->target_addr.s_node; + + /* + * Add ELAP headers and set target to the AARP multicast. + */ + + aarp_dl->datalink_header(aarp_dl, skb, aarp_eth_multicast); + + /* + * Send it. + */ + + + dev_queue_xmit(skb, dev, SOPRI_NORMAL); + + /* + * Update the sending count + */ + + a->xmit_count++; +} + +static void aarp_send_reply(struct device *dev, struct at_addr *us, struct at_addr *them, unsigned char *sha) +{ + int len=dev->hard_header_len+sizeof(struct elapaarp)+aarp_dl->header_length; + struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC); + struct elapaarp *eah=(struct elapaarp *)(skb->data+dev->hard_header_len+aarp_dl->header_length); + + if(skb==NULL) + return; + + /* + * Set up the buffer. + */ + + skb->arp = 1; + skb->free = 1; + skb->len = len; + skb->dev = dev; + + /* + * Set up the ARP. + */ + + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_REPLY); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero= 0; + eah->pa_src_net = us->s_net; + eah->pa_src_node= us->s_node; + + if(sha==NULL) + memset(eah->hw_dst, '\0', ETH_ALEN); + else + memcpy(eah->hw_dst, sha, ETH_ALEN); + + eah->pa_dst_zero= 0; + eah->pa_dst_net = them->s_net; + eah->pa_dst_node= them->s_node; + + /* + * Add ELAP headers and set target to the AARP multicast. + */ + + aarp_dl->datalink_header(aarp_dl, skb, sha); + + /* + * Send it. + */ + + dev_queue_xmit(skb, dev, SOPRI_NORMAL); + +} + +/* + * Send probe frames. Called from atif_probe_device. + */ + +void aarp_send_probe(struct device *dev, struct at_addr *us) +{ + int len=dev->hard_header_len+sizeof(struct elapaarp)+aarp_dl->header_length; + struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC); + struct elapaarp *eah=(struct elapaarp *)(skb->data+dev->hard_header_len+aarp_dl->header_length); + static char aarp_eth_multicast[ETH_ALEN]={ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + + if(skb==NULL) + return; + + /* + * Set up the buffer. + */ + + skb->arp = 1; + skb->free = 1; + skb->len = len; + skb->dev = dev; + + /* + * Set up the ARP. + */ + + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_PROBE); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero= 0; + eah->pa_src_net = us->s_net; + eah->pa_src_node= us->s_node; + + memset(eah->hw_dst, '\0', ETH_ALEN); + + eah->pa_dst_zero= 0; + eah->pa_dst_net = us->s_net; + eah->pa_dst_node= us->s_node; + + /* + * Add ELAP headers and set target to the AARP multicast. + */ + + aarp_dl->datalink_header(aarp_dl, skb, aarp_eth_multicast); + + /* + * Send it. + */ + + dev_queue_xmit(skb, dev, SOPRI_NORMAL); + +} + +/* + * Handle an aarp timer expire + */ + +static void aarp_expire_timer(struct aarp_entry **n) +{ + struct aarp_entry *t; + while((*n)!=NULL) + { + /* Expired ? */ + if((*n)->expires_at < jiffies) + { + t= *n; + *n=(*n)->next; + aarp_expire(t); + } + else + n=&((*n)->next); + } +} + +/* + * Kick all pending requests 5 times a second. + */ + +static void aarp_kick(struct aarp_entry **n) +{ + struct aarp_entry *t; + while((*n)!=NULL) + { + /* Expired - if this will be the 11th transmit, we delete + instead */ + if((*n)->xmit_count>=AARP_RETRANSMIT_LIMIT) + { + t= *n; + *n=(*n)->next; + aarp_expire(t); + } + else + { + aarp_send_query(*n); + n=&((*n)->next); + } + } +} + +/* + * A device has gone down. Take all entries referring to the device + * and remove them. + */ + +static void aarp_expire_device(struct aarp_entry **n, struct device *dev) +{ + struct aarp_entry *t; + while((*n)!=NULL) + { + if((*n)->dev==dev) + { + t= *n; + *n=(*n)->next; + aarp_expire(t); + } + else + n=&((*n)->next); + } +} + +/* + * Handle the timer event + */ + +static void aarp_expire_timeout(unsigned long unused) +{ + int ct=0; + for(ct=0;ct<AARP_HASH_SIZE;ct++) + { + aarp_expire_timer(&resolved[ct]); + aarp_kick(&unresolved[ct]); + aarp_expire_timer(&unresolved[ct]); + } + del_timer(&aarp_timer); + if(unresolved_count==0) + aarp_timer.expires=AARP_EXPIRY_TIME; + else + aarp_timer.expires=AARP_TICK_TIME; + add_timer(&aarp_timer); +} + +/* + * Network device notifier chain handler. + */ + +static int aarp_device_event(unsigned long event, void *ptr) +{ + int ct=0; + if(event==NETDEV_DOWN) + { + for(ct=0;ct<AARP_HASH_SIZE;ct++) + { + aarp_expire_device(&resolved[ct],ptr); + aarp_expire_device(&unresolved[ct],ptr); + } + } + return NOTIFY_DONE; +} + +/* + * Create a new aarp entry. + */ + +static struct aarp_entry *aarp_alloc(void) +{ + struct aarp_entry *a=kmalloc(sizeof(struct aarp_entry), GFP_ATOMIC); + if(a==NULL) + return NULL; + skb_queue_head_init(&a->packet_queue); + return a; +} + +/* + * Find an entry. We might return an expired but not yet purged entry. We + * don't care as it will do no harm. + */ + +static struct aarp_entry *aarp_find_entry(struct aarp_entry *list, struct device *dev, struct at_addr *sat) +{ + unsigned long flags; + save_flags(flags); + cli(); + while(list) + { + if(list->target_addr.s_net==sat->s_net && + list->target_addr.s_node==sat->s_node && list->dev==dev) + break; + list=list->next; + } + restore_flags(flags); + return list; +} + +/* + * Send a DDP frame + */ + +int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, void *hwaddr) +{ + static char ddp_eth_multicast[ETH_ALEN]={ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + int hash; + struct aarp_entry *a; + unsigned long flags; + + /* + * Non ELAP we cannot do. + */ + if(dev->type!=ARPHRD_ETHER) + { + return -1; + } + + skb->dev = dev; + + hash=sa->s_node%(AARP_HASH_SIZE-1); + save_flags(flags); + cli(); + + /* + * Do we have a resolved entry ? + */ + + if(sa->s_node==ATADDR_BCAST) + { + ddp_dl->datalink_header(ddp_dl, skb, ddp_eth_multicast); + if(skb->sk==NULL) + dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL); + else + dev_queue_xmit(skb, skb->dev, skb->sk->priority); + restore_flags(flags); + return 1; + } + a=aarp_find_entry(resolved[hash],dev,sa); + if(a!=NULL) + { + /* + * Return 1 and fill in the address + */ + a->expires_at=jiffies+AARP_EXPIRY_TIME*10; + ddp_dl->datalink_header(ddp_dl, skb, a->hwaddr); + if(skb->sk==NULL) + dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL); + else + dev_queue_xmit(skb, skb->dev, skb->sk->priority); + restore_flags(flags); + return 1; + } + /* + * Do we have an unresolved entry: This is the less common path + */ + a=aarp_find_entry(unresolved[hash],dev,sa); + if(a!=NULL) + { + /* + * Queue onto the unresolved queue + */ + skb_queue_tail(&a->packet_queue, skb); + restore_flags(flags); + return 0; + } + /* + * Allocate a new entry + */ + a=aarp_alloc(); + if(a==NULL) + { + /* + * Whoops slipped... good job it's an unreliable + * protocol 8) + */ + restore_flags(flags); + return -1; + } + /* + * Set up the queue + */ + skb_queue_tail(&a->packet_queue, skb); + a->expires_at=jiffies+AARP_RESOLVE_TIME; + a->dev=dev; + a->next=unresolved[hash]; + a->target_addr= *sa; + a->xmit_count=0; + unresolved[hash]=a; + unresolved_count++; + restore_flags(flags); + /* + * Send an initial request for the address + */ + aarp_send_query(a); + /* + * Switch to fast timer if needed (That is if this is the + * first unresolved entry to get added) + */ + if(unresolved_count==1) + { + del_timer(&aarp_timer); + aarp_timer.expires=AARP_TICK_TIME; + add_timer(&aarp_timer); + } + /* + * Tell the ddp layer we have taken over for this frame. + */ + return 0; +} + +static void aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, int hash) +{ + struct sk_buff *skb; + while(*list!=NULL) + { + if(*list==a) + { + unresolved_count--; + *list=a->next; + /* Move into the resolved list */ + a->next=resolved[hash]; + resolved[hash]=a; + /* Kick frames off */ + while((skb=skb_dequeue(&a->packet_queue))!=NULL) + { + a->expires_at=jiffies+AARP_EXPIRY_TIME*10; + ddp_dl->datalink_header(ddp_dl,skb,a->hwaddr); + if(skb->sk==NULL) + dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL); + else + dev_queue_xmit(skb, skb->dev, skb->sk->priority); + } + } + else + list=&((*list)->next); + } +} + +static int aarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ + struct elapaarp *ea=(struct elapaarp *)skb->h.raw; + struct aarp_entry *a; + struct at_addr sa, *ma; + unsigned long flags; + int hash; + struct atalk_iface *ifa; + + + /* + * We only do ethernet SNAP AARP + */ + + if(dev->type!=ARPHRD_ETHER) + { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* + * Frame size ok ? + */ + + if(skb->len<sizeof(*ea)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + + ea->function=ntohs(ea->function); + + /* + * Sanity check fields. + */ + + if(ea->function<AARP_REQUEST || ea->function > AARP_PROBE || ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN || + ea->pa_src_zero != 0 || ea->pa_dst_zero != 0) + { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* + * Looks good + */ + + hash=ea->pa_src_node%(AARP_HASH_SIZE-1); + + /* + * Build an address + */ + + sa.s_node=ea->pa_src_node; + sa.s_net=ea->pa_src_net; + + /* + * Process the packet + */ + + save_flags(flags); + + /* + * Check for replies of me + */ + + ifa=atalk_find_dev(dev); + if(ifa==NULL) + { + restore_flags(flags); + kfree_skb(skb, FREE_READ); + return 1; + } + if(ifa->status&ATIF_PROBE) + { + if(ifa->address.s_node==ea->pa_dst_node && ifa->address.s_net==ea->pa_dst_net) + { + /* + * Fail the probe (in use) + */ + ifa->status|=ATIF_PROBE_FAIL; + restore_flags(flags); + kfree_skb(skb, FREE_READ); + return 1; + } + } + + switch(ea->function) + { + case AARP_REPLY: + if(unresolved_count==0) /* Speed up */ + break; + /* + * Find the entry + */ + + cli(); + if((a=aarp_find_entry(unresolved[hash],dev,&sa))==NULL || dev != a->dev) + break; + /* + * We can fill one in - this is good + */ + memcpy(a->hwaddr,ea->hw_src,ETH_ALEN); + aarp_resolved(&unresolved[hash],a,hash); + if(unresolved_count==0) + { + del_timer(&aarp_timer); + aarp_timer.expires=AARP_EXPIRY_TIME; + add_timer(&aarp_timer); + } + break; + + case AARP_REQUEST: + case AARP_PROBE: + /* + * If it is my address set ma to my address and reply. We can treat probe and + * request the same. Probe simply means we shouldn't cache the querying host, + * as in a probe they are proposing an address not using one. + */ + + ma=&ifa->address; + sa.s_node=ea->pa_dst_node; + sa.s_net=ea->pa_dst_net; + + if(sa.s_node!=ma->s_node) + break; + if(sa.s_net && ma->s_net && sa.s_net!=ma->s_net) + break; + + sa.s_node=ea->pa_src_node; + sa.s_net=ea->pa_src_net; + + /* + * aarp_my_address has found the address to use for us. + */ + aarp_send_reply(dev,ma,&sa,ea->hw_src); + break; + } + restore_flags(flags); + kfree_skb(skb, FREE_READ); + return 1; +} + +static struct notifier_block aarp_notifier={ + aarp_device_event, + NULL, + 0 +}; + + +void aarp_proto_init(void) +{ + static char aarp_snap_id[]={0x00,0x00,0x00,0x80,0xF3}; + if((aarp_dl=register_snap_client(aarp_snap_id, aarp_rcv))==NULL) + printk("Unable to register AARP with SNAP.\n"); + init_timer(&aarp_timer); + aarp_timer.function=aarp_expire_timeout; + aarp_timer.data=0; + aarp_timer.expires=AARP_EXPIRY_TIME; + add_timer(&aarp_timer); + register_netdevice_notifier(&aarp_notifier); +} +#endif diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c new file mode 100644 index 000000000..67ad1bf22 --- /dev/null +++ b/net/appletalk/ddp.c @@ -0,0 +1,1843 @@ +/* + * DDP: An implementation of the Appletalk DDP protocol for + * ethernet 'ELAP'. + * + * Alan Cox <Alan.Cox@linux.org> + * <iialan@www.linux.org.uk> + * + * With more than a little assistance from + * + * Wesley Craig <netatalk@umich.edu> + * + * 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. + * + * TODO + * ASYNC I/O + * Testing. + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/if_ether.h> +#include <linux/inet.h> +#include <linux/notifier.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/termios.h> /* For TIOCOUTQ/INQ */ +#include <net/datalink.h> +#include <net/p8022.h> +#include <net/psnap.h> +#include <net/sock.h> +#include <net/atalk.h> + +#ifdef CONFIG_ATALK + +#define APPLETALK_DEBUG + + +#ifdef APPLETALK_DEBUG +#define DPRINT(x) print(x) +#else +#define DPRINT(x) +#endif + +struct datalink_proto *ddp_dl, *aarp_dl; + +#define min(a,b) (((a)<(b))?(a):(b)) + +/***********************************************************************************************************************\ +* * +* Handlers for the socket list. * +* * +\***********************************************************************************************************************/ + +static atalk_socket *volatile atalk_socket_list=NULL; + +/* + * 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 atalk_remove_socket(atalk_socket *sk) +{ + unsigned long flags; + atalk_socket *s; + + save_flags(flags); + cli(); + + s=atalk_socket_list; + if(s==sk) + { + atalk_socket_list=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); +} + +static void atalk_insert_socket(atalk_socket *sk) +{ + unsigned long flags; + save_flags(flags); + cli(); + sk->next=atalk_socket_list; + atalk_socket_list=sk; + restore_flags(flags); +} + +static atalk_socket *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif) +{ + atalk_socket *s; + + for( s = atalk_socket_list; s != NULL; s = s->next ) { + if ( to->sat_port != s->at.src_port ) { + continue; + } + + if ( to->sat_addr.s_net == 0 && + to->sat_addr.s_node == ATADDR_BCAST && + s->at.src_net == atif->address.s_net ) { + break; + } + + if ( to->sat_addr.s_net == s->at.src_net && + to->sat_addr.s_node == s->at.src_node ) { + break; + } + + /* XXXX.0 */ + } + return( s ); +} + +/* + * Find a socket in the list. + */ + +static atalk_socket *atalk_find_socket(struct sockaddr_at *sat) +{ + atalk_socket *s; + + for ( s = atalk_socket_list; s != NULL; s = s->next ) { + if ( s->at.src_net != sat->sat_addr.s_net ) { + continue; + } + if ( s->at.src_node != sat->sat_addr.s_node ) { + continue; + } + if ( s->at.src_port != sat->sat_port ) { + continue; + } + break; + } + return( s ); +} + +/* + * 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 atalk_destroy_socket(atalk_socket *sk); + +/* + * Handler for deferred kills. + */ + +static void atalk_destroy_timer(unsigned long data) +{ + atalk_destroy_socket((atalk_socket *)data); +} + +static void atalk_destroy_socket(atalk_socket *sk) +{ + struct sk_buff *skb; + atalk_remove_socket(sk); + + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + { + kfree_skb(skb,FREE_READ); + } + + if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead) + kfree_s(sk,sizeof(*sk)); + else + { + /* + * Someone is using our buffers still.. defer + */ + init_timer(&sk->timer); + sk->timer.expires=10*HZ; + sk->timer.function=atalk_destroy_timer; + sk->timer.data = (unsigned long)sk; + add_timer(&sk->timer); + } +} + + +/* Called from proc fs */ +int atalk_get_info(char *buffer, char **start, off_t offset, int length) +{ + atalk_socket *s; + int len=0; + off_t pos=0; + off_t begin=0; + + /* + * Fill this in to print out the appletalk info you want + */ + + /* Theory.. Keep printing in the same place until we pass offset */ + + len += sprintf (buffer,"Type local_addr remote_addr tx_queue rx_queue st uid\n"); + for (s = atalk_socket_list; s != NULL; s = s->next) + { + len += sprintf (buffer+len,"%02X ", s->type); + len += sprintf (buffer+len,"%04X:%02X:%02X ", + s->at.src_net,s->at.src_node,s->at.src_port); + len += sprintf (buffer+len,"%04X:%02X:%02X ", + s->at.dest_net,s->at.dest_node,s->at.dest_port); + len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc); + len += sprintf (buffer+len,"%02X %d\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; +} + +/*******************************************************************************************************************\ +* * +* Routing tables for the Appletalk socket layer * +* * +\*******************************************************************************************************************/ + + +static struct atalk_route *atalk_router_list=NULL; +static struct atalk_route atrtr_default; /* For probing devices or in a routerless network */ +static struct atalk_iface *atalk_iface_list=NULL; + +/* + * Appletalk interface control + */ + +/* + * Drop a device. Doesn't drop any of its routes - that is the + * the callers problem. Called when we down the interface or + * delete the address. + */ + +static void atif_drop_device(struct device *dev) +{ + struct atalk_iface **iface = &atalk_iface_list; + struct atalk_iface *tmp; + + while ((tmp = *iface) != NULL) + { + if (tmp->dev == dev) + { + *iface = tmp->next; + kfree_s(tmp, sizeof(struct atalk_iface)); + } + else + iface = &tmp->next; + } +} + +static struct atalk_iface *atif_add_device(struct device *dev, struct at_addr *sa) +{ + struct atalk_iface *iface=(struct atalk_iface *) + kmalloc(sizeof(*iface), GFP_KERNEL); + unsigned long flags; + if(iface==NULL) + return NULL; + iface->dev=dev; + iface->address= *sa; + iface->status=0; + save_flags(flags); + cli(); + iface->next=atalk_iface_list; + atalk_iface_list=iface; + restore_flags(flags); + return iface; +} + +/* + * Perform phase 2 AARP probing on our tentative address. + */ + +static int atif_probe_device(struct atalk_iface *atif) +{ + int ct; + int netrange=ntohs(atif->nets.nr_lastnet)-ntohs(atif->nets.nr_firstnet)+1; + int probe_net=ntohs(atif->address.s_net); + int netct; + int nodect; + + + /* + * Offset the network we start probing with. + */ + + if(probe_net==ATADDR_ANYNET) + { + if(!netrange) + probe_net=ntohs(atif->nets.nr_firstnet); + else + probe_net=ntohs(atif->nets.nr_firstnet) + (jiffies%netrange); + } + + + /* + * Scan the networks. + */ + + for(netct=0;netct<=netrange;netct++) + { + /* + * Sweep the available nodes from a random start. + */ + int nodeoff=jiffies&255; + + atif->address.s_net=htons(probe_net); + for(nodect=0;nodect<256;nodect++) + { + atif->address.s_node=((nodect+nodeoff)&0xFF); + if(atif->address.s_node>0&&atif->address.s_node<254) + { + /* + * Probe a proposed address. + */ + for(ct=0;ct<AARP_RETRANSMIT_LIMIT;ct++) + { + aarp_send_probe(atif->dev, &atif->address); + /* + * Defer 1/10th + */ + current->timeout = jiffies + (HZ/10); + current->state = TASK_INTERRUPTIBLE; + schedule(); + if(atif->status&ATIF_PROBE_FAIL) + break; + } + if(!(atif->status&ATIF_PROBE_FAIL)) + return 0; + } + atif->status&=~ATIF_PROBE_FAIL; + } + probe_net++; + if(probe_net>ntohs(atif->nets.nr_lastnet)) + probe_net=ntohs(atif->nets.nr_firstnet); + } + return -EADDRINUSE; /* Network is full... */ +} + +struct at_addr *atalk_find_dev_addr(struct device *dev) +{ + struct atalk_iface *iface; + for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) + if(iface->dev==dev) + return &iface->address; + return NULL; +} + +static struct at_addr *atalk_find_primary(void) +{ + struct atalk_iface *iface; + for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) + if(!(iface->dev->flags&IFF_LOOPBACK)) + return &iface->address; + if ( atalk_iface_list != NULL ) { + return &atalk_iface_list->address; + } else { + return NULL; + } +} + +/* + * Give a device find its atif control structure + */ + +struct atalk_iface *atalk_find_dev(struct device *dev) +{ + struct atalk_iface *iface; + for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) + if(iface->dev==dev) + return iface; + return NULL; +} + +/* + * Find a match for 'any network' - ie any of our interfaces with that + * node number will do just nicely. + */ + +static struct atalk_iface *atalk_find_anynet(int node, struct device *dev) +{ + struct atalk_iface *iface; + for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) { + if ( iface->dev != dev || ( iface->status & ATIF_PROBE )) { + continue; + } + if ( node == ATADDR_BCAST || iface->address.s_node == node ) { + return iface; + } + } + return NULL; +} + +/* + * Find a match for a specific network:node pair + */ + +static struct atalk_iface *atalk_find_interface(int net, int node) +{ + struct atalk_iface *iface; + for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) + { + if((node==ATADDR_BCAST || iface->address.s_node==node) + && iface->address.s_net==net && !(iface->status&ATIF_PROBE)) + return iface; + } + return NULL; +} + + +/* + * Find a route for an appletalk packet. This ought to get cached in + * the socket (later on...). We know about host routes and the fact + * that a route must be direct to broadcast. + */ + +static struct atalk_route *atrtr_find(struct at_addr *target) +{ + struct atalk_route *r; + for(r=atalk_router_list;r!=NULL;r=r->next) + { + if(!(r->flags&RTF_UP)) + continue; + if(r->target.s_net==target->s_net) + { + if(!(r->flags&RTF_HOST) || r->target.s_node==target->s_node) + return r; + } + } + if(atrtr_default.dev) + return &atrtr_default; + return NULL; +} + + +/* + * Given an appletalk network find the device to use. This can be + * a simple lookup. Funny stuff like routers can wait 8) + */ + +static struct device *atrtr_get_dev(struct at_addr *sa) +{ + struct atalk_route *atr=atrtr_find(sa); + if(atr==NULL) + return NULL; + else + return atr->dev; +} + +/* + * Set up a default router. + */ + +static void atrtr_set_default(struct device *dev) +{ + atrtr_default.dev=dev; + atrtr_default.flags= RTF_UP; + atrtr_default.gateway.s_net=htons(0); + atrtr_default.gateway.s_node=0; +} + +/* + * Add a router. Basically make sure it looks valid and stuff the + * entry in the list. While it uses netranges we always set them to one + * entry to work like netatalk. + */ + +static int atrtr_create(struct rtentry *r, struct device *devhint) +{ + struct sockaddr_at *ta=(struct sockaddr_at *)&r->rt_dst; + struct sockaddr_at *ga=(struct sockaddr_at *)&r->rt_gateway; + struct atalk_route *rt; + struct atalk_iface *iface, *riface; + unsigned long flags; + + save_flags(flags); + + /* + * Fixme: Raise/Lower a routing change semaphore for these + * operations. + */ + + /* + * Validate the request + */ + if(ta->sat_family!=AF_APPLETALK) + return -EINVAL; + if(devhint == NULL && ga->sat_family != AF_APPLETALK) + return -EINVAL; + + /* + * Now walk the routing table and make our decisions + */ + + for(rt=atalk_router_list;rt!=NULL;rt=rt->next) + { + if(r->rt_flags != rt->flags) + continue; + + if(ta->sat_addr.s_net == rt->target.s_net) { + if(!(rt->flags&RTF_HOST)) + break; + if(ta->sat_addr.s_node == rt->target.s_node) + break; + } + } + + if ( devhint == NULL ) { + for ( riface = NULL, iface = atalk_iface_list; iface; + iface = iface->next ) { + if ( riface == NULL && ntohs( ga->sat_addr.s_net ) >= + ntohs( iface->nets.nr_firstnet ) && + ntohs( ga->sat_addr.s_net ) <= + ntohs( iface->nets.nr_lastnet )) + riface = iface; + if ( ga->sat_addr.s_net == iface->address.s_net && + ga->sat_addr.s_node == iface->address.s_node ) + riface = iface; + } + if ( riface == NULL ) + return -ENETUNREACH; + devhint = riface->dev; + } + + if(rt==NULL) + { + rt=(struct atalk_route *)kmalloc(sizeof(struct atalk_route), GFP_KERNEL); + if(rt==NULL) + return -ENOBUFS; + cli(); + rt->next=atalk_router_list; + atalk_router_list=rt; + } + + /* + * Fill in the entry. + */ + rt->target=ta->sat_addr; + rt->dev=devhint; + rt->flags=r->rt_flags; + rt->gateway=ga->sat_addr; + + restore_flags(flags); + return 0; +} + + +/* + * Delete a route. Find it and discard it. + */ + +static int atrtr_delete( struct at_addr *addr ) +{ + struct atalk_route **r = &atalk_router_list; + struct atalk_route *tmp; + + while ((tmp = *r) != NULL) { + if (tmp->target.s_net == addr->s_net && + (!(tmp->flags&RTF_GATEWAY) || + tmp->target.s_node == addr->s_node )) { + *r = tmp->next; + kfree_s(tmp, sizeof(struct atalk_route)); + return 0; + } + r = &tmp->next; + } + return -ENOENT; +} + +/* + * Called when a device is downed. Just throw away any routes + * via it. + */ + +void atrtr_device_down(struct device *dev) +{ + struct atalk_route **r = &atalk_router_list; + struct atalk_route *tmp; + + while ((tmp = *r) != NULL) { + if (tmp->dev == dev) { + *r = tmp->next; + kfree_s(tmp, sizeof(struct atalk_route)); + } + else + r = &tmp->next; + } + if(atrtr_default.dev==dev) + atrtr_set_default(NULL); +} + +/* + * A device event has occured. Watch for devices going down and + * delete our use of them (iface and route). + */ + +static int ddp_device_event(unsigned long event, void *ptr) +{ + if(event==NETDEV_DOWN) + { + /* Discard any use of this */ + atrtr_device_down((struct device *)ptr); + atif_drop_device((struct device *)ptr); + } + return NOTIFY_DONE; +} + +/* + * ioctl calls. Shouldn't even need touching. + */ + +/* + * Device configuration ioctl calls. + */ + +int atif_ioctl(int cmd, void *arg) +{ + struct ifreq atreq; + static char aarp_mcast[6]={0x09,0x00,0x00,0xFF,0xFF,0xFF}; + struct netrange *nr; + struct sockaddr_at *sa; + struct device *dev; + struct atalk_iface *atif; + int ro=(cmd==SIOCSIFADDR); + int err=verify_area(ro?VERIFY_READ:VERIFY_WRITE, arg,sizeof(atreq)); + int ct; + int limit; + struct rtentry rtdef; + + if(err) + return err; + + memcpy_fromfs(&atreq,arg,sizeof(atreq)); + + if((dev=dev_get(atreq.ifr_name))==NULL) + return -ENODEV; + + sa=(struct sockaddr_at*)&atreq.ifr_addr; + atif=atalk_find_dev(dev); + + switch(cmd) + { + case SIOCSIFADDR: + if(!suser()) + return -EPERM; + if(sa->sat_family!=AF_APPLETALK) + return -EINVAL; + if(dev->type!=ARPHRD_ETHER) + return -EPROTONOSUPPORT; + nr=(struct netrange *)&sa->sat_zero[0]; + if(nr->nr_phase!=2) + return -EPROTONOSUPPORT; + if(sa->sat_addr.s_node==ATADDR_BCAST || sa->sat_addr.s_node == 254) + return -EINVAL; + if(atif) + { + /* + * Already setting address. + */ + if(atif->status&ATIF_PROBE) + return -EBUSY; + + atif->address.s_net=sa->sat_addr.s_net; + atif->address.s_node=sa->sat_addr.s_node; + atrtr_device_down(dev); /* Flush old routes */ + } + else + { + atif=atif_add_device(dev, &sa->sat_addr); + } + atif->nets= *nr; + + /* + * Check if the chosen address is used. If so we + * error and atalkd will try another. + */ + + if(!(dev->flags&IFF_LOOPBACK) && atif_probe_device(atif)<0) + { + atif_drop_device(dev); + return -EADDRINUSE; + } + + /* + * Hey it worked - add the direct + * routes. + */ + + sa=(struct sockaddr_at *)&rtdef.rt_gateway; + sa->sat_family=AF_APPLETALK; + sa->sat_addr.s_net=atif->address.s_net; + sa->sat_addr.s_node=atif->address.s_node; + sa=(struct sockaddr_at *)&rtdef.rt_dst; + rtdef.rt_flags=RTF_UP; + sa->sat_family=AF_APPLETALK; + sa->sat_addr.s_node=ATADDR_ANYNODE; + if(dev->flags&IFF_LOOPBACK) + rtdef.rt_flags|=RTF_HOST; + /* + * Routerless initial state. + */ + if(nr->nr_firstnet==htons(0) && nr->nr_lastnet==htons(0xFFFE)) { + sa->sat_addr.s_net=atif->address.s_net; + atrtr_create(&rtdef, dev); + atrtr_set_default(dev); + } else { + limit=ntohs(nr->nr_lastnet); + if(limit-ntohs(nr->nr_firstnet) > 256) + { + printk("Too many routes/iface.\n"); + return -EINVAL; + } + for(ct=ntohs(nr->nr_firstnet);ct<=limit;ct++) + { + sa->sat_addr.s_net=htons(ct); + atrtr_create(&rtdef, dev); + } + } + dev_mc_add(dev, aarp_mcast, 6, 1); + return 0; + case SIOCGIFADDR: + if(atif==NULL) + return -EADDRNOTAVAIL; + ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_family=AF_APPLETALK; + ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr=atif->address; + break; + case SIOCGIFBRDADDR: + if(atif==NULL) + return -EADDRNOTAVAIL; + ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_family=AF_APPLETALK; + ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_net=atif->address.s_net; + ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_node=ATADDR_BCAST; + break; + } + memcpy_tofs(arg,&atreq,sizeof(atreq)); + return 0; +} + +/* + * Routing ioctl() calls + */ + +static int atrtr_ioctl(unsigned int cmd, void *arg) +{ + int err; + struct rtentry rt; + + err=verify_area(VERIFY_READ, arg, sizeof(rt)); + if(err) + return err; + memcpy_fromfs(&rt,arg,sizeof(rt)); + + switch(cmd) + { + case SIOCDELRT: + if(rt.rt_dst.sa_family!=AF_APPLETALK) + return -EINVAL; + return atrtr_delete(&((struct sockaddr_at *)&rt.rt_dst)->sat_addr); + case SIOCADDRT: + return atrtr_create(&rt, NULL); + default: + return -EINVAL; + } +} + +/* Called from proc fs - just make it print the ifaces neatly */ + +int atalk_if_get_info(char *buffer, char **start, off_t offset, int length) +{ + struct atalk_iface *iface; + int len=0; + off_t pos=0; + off_t begin=0; + + len += sprintf (buffer,"Interface Address Networks Status\n"); + for (iface = atalk_iface_list; iface != NULL; iface = iface->next) + { + len += sprintf (buffer+len,"%-16s %04X:%02X %04X-%04X %d\n", + iface->dev->name, + ntohs(iface->address.s_net),iface->address.s_node, + ntohs(iface->nets.nr_firstnet),ntohs(iface->nets.nr_lastnet), + iface->status); + 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; +} + +/* Called from proc fs - just make it print the routes neatly */ + +int atalk_rt_get_info(char *buffer, char **start, off_t offset, int length) +{ + struct atalk_route *rt; + int len=0; + off_t pos=0; + off_t begin=0; + + len += sprintf (buffer,"Target Router Flags Dev\n"); + if(atrtr_default.dev) + { + rt=&atrtr_default; + len += sprintf (buffer+len,"Default %5d:%-3d %-4d %s\n", + ntohs(rt->gateway.s_net), rt->gateway.s_node, rt->flags, + rt->dev->name); + } + for (rt = atalk_router_list; rt != NULL; rt = rt->next) + { + len += sprintf (buffer+len,"%04X:%02X %5d:%-3d %-4d %s\n", + ntohs(rt->target.s_net),rt->target.s_node, + ntohs(rt->gateway.s_net), rt->gateway.s_node, rt->flags, + rt->dev->name); + 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 Appletalk socket object * +* * +\*******************************************************************************************************************/ + +/* + * Checksum: This is 'optional'. It's quite likely also a good + * candidate for assembler hackery 8) + */ + +unsigned short atalk_checksum(struct ddpehdr *ddp, int len) +{ + unsigned long sum=0; /* Assume unsigned long is >16 bits */ + unsigned char *data=(unsigned char *)ddp; + + len-=4; /* skip header 4 bytes */ + data+=4; + + /* This ought to be unwrapped neatly. I'll trust gcc for now */ + while(len--) + { + sum+=*data; + sum<<=1; + if(sum&0x10000) + { + sum++; + sum&=0xFFFF; + } + data++; + } + if(sum) + return htons((unsigned short)sum); + return 0xFFFF; /* Use 0xFFFF for 0. 0 itself means none */ +} + +/* + * Generic fcntl calls are already dealt with. If we don't need funny ones + * this is the all you need. Async I/O is also seperate. + */ + +static int atalk_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ +/* atalk_socket *sk=(atalk_socket *)sock->data;*/ + switch(cmd) + { + default: + return(-EINVAL); + } +} + +/* + * Set 'magic' options for appletalk. If we don't have any this is fine + * as it is. + */ + +static int atalk_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) +{ + atalk_socket *sk; + int err,opt; + + sk=(atalk_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_ATALK: + switch(optname) + { + default: + return -EOPNOTSUPP; + } + break; + + case SOL_SOCKET: + return sock_setsockopt(sk,level,optname,optval,optlen); + + default: + return -EOPNOTSUPP; + } +} + + +/* + * Get any magic options. Comment above applies. + */ + +static int atalk_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + atalk_socket *sk; + int val=0; + int err; + + sk=(atalk_socket *)sock->data; + + switch(level) + { + + case SOL_ATALK: + switch(optname) + { + 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); +} + +/* + * Only for connection oriented sockets - ignore + */ + +static int atalk_listen(struct socket *sock, int backlog) +{ + return -EOPNOTSUPP; +} + +/* + * These are standard. + */ + +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,0); + } +} + +/* + * Create a socket. Initialise the socket, blank the addresses + * set the state. + */ + +static int atalk_create(struct socket *sock, int protocol) +{ + atalk_socket *sk; + sk=(atalk_socket *)kmalloc(sizeof(*sk),GFP_KERNEL); + if(sk==NULL) + return(-ENOMEM); + switch(sock->type) + { + /* This RAW is an extension. It is trivial to do and gives you + the full ELAP frame. Should be handy for CAP 8) */ + case SOCK_RAW: + /* We permit DDP datagram sockets */ + case SOCK_DGRAM: + break; + default: + kfree_s((void *)sk,sizeof(*sk)); + return(-ESOCKTNOSUPPORT); + } + sk->dead=0; + sk->next=NULL; + sk->broadcast=0; + sk->no_check=0; /* Checksums on by default */ + sk->rcvbuf=SK_RMEM_MAX; + sk->sndbuf=SK_WMEM_MAX; + sk->pair=NULL; + sk->wmem_alloc=0; + sk->rmem_alloc=0; + sk->inuse=0; + sk->proc=0; + sk->priority=1; + 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->debug=0; + + sk->at.src_net=0; + sk->at.src_node=0; + sk->at.src_port=0; + + sk->at.dest_net=0; + sk->at.dest_node=0; + sk->at.dest_port=0; + + sk->mtu=DDP_MAXSZ; + + 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); +} + +/* + * Copy a socket. No work needed. + */ + +static int atalk_dup(struct socket *newsock,struct socket *oldsock) +{ + return(atalk_create(newsock,SOCK_DGRAM)); +} + +/* + * Free a socket. No work needed + */ + +static int atalk_release(struct socket *sock, struct socket *peer) +{ + atalk_socket *sk=(atalk_socket *)sock->data; + if(sk==NULL) + return(0); + if(!sk->dead) + sk->state_change(sk); + sk->dead=1; + sock->data=NULL; + atalk_destroy_socket(sk); + return(0); +} + +/* + * Pick a source address if one is not given. Just return + * an error if not supportable. + */ + +static int atalk_pick_port(struct sockaddr_at *sat) +{ + for ( sat->sat_port = ATPORT_RESERVED; sat->sat_port < ATPORT_LAST; + sat->sat_port++ ) + if ( atalk_find_socket( sat ) == NULL ) + return sat->sat_port; + return -EBUSY; +} + +static int atalk_autobind(atalk_socket *sk) +{ + struct at_addr *ap = atalk_find_primary(); + struct sockaddr_at sat; + int n; + + if ( ap == NULL || ap->s_net == htons( ATADDR_ANYNET )) + return -EADDRNOTAVAIL; + sk->at.src_net = sat.sat_addr.s_net = ap->s_net; + sk->at.src_node = sat.sat_addr.s_node = ap->s_node; + + if (( n = atalk_pick_port( &sat )) < 0 ) + return( n ); + sk->at.src_port=n; + atalk_insert_socket(sk); + sk->zapped=0; + return 0; +} + +/* + * Set the address 'our end' of the connection. + */ + +static int atalk_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) +{ + atalk_socket *sk; + struct sockaddr_at *addr=(struct sockaddr_at *)uaddr; + + sk=(atalk_socket *)sock->data; + + if(sk->zapped==0) + return(-EIO); + + if(addr_len!=sizeof(struct sockaddr_at)) + return -EINVAL; + + if(addr->sat_family!=AF_APPLETALK) + return -EAFNOSUPPORT; + + if(addr->sat_addr.s_net==htons(ATADDR_ANYNET)) + { + struct at_addr *ap=atalk_find_primary(); + if(ap==NULL) + return -EADDRNOTAVAIL; + sk->at.src_net=addr->sat_addr.s_net=ap->s_net; + sk->at.src_node=addr->sat_addr.s_node=ap->s_node; + } + else + { + if ( atalk_find_interface( addr->sat_addr.s_net, + addr->sat_addr.s_node ) == NULL ) + return -EADDRNOTAVAIL; + sk->at.src_net=addr->sat_addr.s_net; + sk->at.src_node=addr->sat_addr.s_node; + } + + if(addr->sat_port == ATADDR_ANYPORT) + { + int n = atalk_pick_port(addr); + if(n < 0) + return n; + sk->at.src_port=addr->sat_port=n; + } + else + sk->at.src_port=addr->sat_port; + + if(atalk_find_socket(addr)!=NULL) + return -EADDRINUSE; + + atalk_insert_socket(sk); + sk->zapped=0; + return(0); +} + +/* + * Set the address we talk to. + */ + +static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + atalk_socket *sk=(atalk_socket *)sock->data; + struct sockaddr_at *addr; + + sk->state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + + if(addr_len!=sizeof(*addr)) + return(-EINVAL); + addr=(struct sockaddr_at *)uaddr; + + if(addr->sat_family!=AF_APPLETALK) + return -EAFNOSUPPORT; +#if 0 /* Netatalk doesnt check this */ + if(addr->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast) + return -EPERM; +#endif + if(sk->zapped) + { + if(atalk_autobind(sk)<0) + return -EBUSY; + } + + if(atrtr_get_dev(&addr->sat_addr)==NULL) + return -ENETUNREACH; + + sk->at.dest_port=addr->sat_port; + sk->at.dest_net=addr->sat_addr.s_net; + sk->at.dest_node=addr->sat_addr.s_node; + sock->state = SS_CONNECTED; + sk->state=TCP_ESTABLISHED; + return(0); +} + +/* + * Not relevant + */ + +static int atalk_socketpair(struct socket *sock1, struct socket *sock2) +{ + return(-EOPNOTSUPP); +} + +/* + * Not relevant + */ + +static int atalk_accept(struct socket *sock, struct socket *newsock, int flags) +{ + if(newsock->data) + kfree_s(newsock->data,sizeof(atalk_socket)); + return -EOPNOTSUPP; +} + +/* + * Find the name of an appletalk socket. Just copy the right + * fields into the sockaddr. + */ + +static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_at sat; + atalk_socket *sk; + + sk=(atalk_socket *)sock->data; + if(sk->zapped) + { + if(atalk_autobind(sk)<0) + return -EBUSY; + } + + *uaddr_len = sizeof(struct sockaddr_at); + + if(peer) + { + if(sk->state!=TCP_ESTABLISHED) + return -ENOTCONN; + sat.sat_addr.s_net=sk->at.dest_net; + sat.sat_addr.s_node=sk->at.dest_node; + sat.sat_port=sk->at.dest_port; + } + else + { + sat.sat_addr.s_net=sk->at.src_net; + sat.sat_addr.s_node=sk->at.src_node; + sat.sat_port=sk->at.src_port; + } + sat.sat_family = AF_APPLETALK; + memcpy(uaddr,&sat,sizeof(sat)); + return(0); +} + +/* + * Receive a packet (in skb) from device dev. This has come from the SNAP decoder, and on entry + * skb->h.raw is the DDP header, skb->len is the DDP length. The physical headers have been + * extracted. + */ + +int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ + atalk_socket *sock; + struct ddpehdr *ddp=(void *)skb->h.raw; + struct atalk_iface *atif; + struct sockaddr_at tosat; + + /* Size check */ + if(skb->len<sizeof(*ddp)) + { + kfree_skb(skb,FREE_READ); + return(0); + } + + + /* + * Fix up the length field [Ok this is horrible but otherwise + * I end up with unions of bit fields and messy bit field order + * compiler/endian dependancies..] + */ + + *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); + + /* + * Trim buffer in case of stray trailing data + */ + + skb->len=min(skb->len,ddp->deh_len); + + /* + * Size check to see if ddp->deh_len was crap + * (Otherwise we'll detonate most spectacularly + * in the middle of recvfrom()). + */ + + if(skb->len<sizeof(*ddp)) + { + kfree_skb(skb,FREE_READ); + return(0); + } + + /* + * Any checksums. Note we don't do htons() on this == is assumed to be + * valid for net byte orders all over the networking code... + */ + + if(ddp->deh_sum && atalk_checksum(ddp, ddp->deh_len)!= ddp->deh_sum) + { + /* Not a valid appletalk frame - dustbin time */ + kfree_skb(skb,FREE_READ); + return(0); + } + + /* Check the packet is aimed at us */ + + if(ddp->deh_dnet == 0) /* Net 0 is 'this network' */ + atif=atalk_find_anynet(ddp->deh_dnode, dev); + else + atif=atalk_find_interface(ddp->deh_dnet,ddp->deh_dnode); + + /* Not ours */ + if(atif==NULL) + { + struct atalk_route *rt; + struct at_addr ta; + ta.s_net=ddp->deh_dnet; + ta.s_node=ddp->deh_dnode; + /* Route the packet */ + rt=atrtr_find(&ta); + if(rt==NULL || ddp->deh_hops==15) + { + kfree_skb(skb, FREE_READ); + return(0); + } + ddp->deh_hops++; + *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* Mend the byte order */ + /* + * Send the buffer onwards + */ + if(aarp_send_ddp(dev,skb, &ta, NULL)==-1) + kfree_skb(skb, FREE_READ); + return 0; + } + + /* Which socket - atalk_search_socket() looks for a *full match* + of the <net,node,port> tuple */ + tosat.sat_addr.s_net = ddp->deh_dnet; + tosat.sat_addr.s_node = ddp->deh_dnode; + tosat.sat_port = ddp->deh_dport; + + sock=atalk_search_socket( &tosat, atif ); + + if(sock==NULL) /* But not one of our sockets */ + { + kfree_skb(skb,FREE_READ); + return(0); + } + + + /* + * Queue packet (standard) + */ + + skb->sk = sock; + + if(sock_queue_rcv_skb(sock,skb)<0) + { + skb->sk=NULL; + kfree_skb(skb, FREE_WRITE); + } + return(0); +} + +static int atalk_sendto(struct socket *sock, void *ubuf, int len, int noblock, + unsigned flags, struct sockaddr *sat, int addr_len) +{ + atalk_socket *sk=(atalk_socket *)sock->data; + struct sockaddr_at *usat=(struct sockaddr_at *)sat; + struct sockaddr_at local_satalk, gsat; + struct sk_buff *skb; + struct device *dev; + struct ddpehdr *ddp; + int size; + struct atalk_route *rt; + int loopback=0; + int err; + + if(flags) + return -EINVAL; + + if(len>587) + return -EMSGSIZE; + + if(usat) + { + if(sk->zapped) + /* put the autobinding in */ + { + if(atalk_autobind(sk)<0) + return -EBUSY; + } + + if(addr_len <sizeof(*usat)) + return(-EINVAL); + if(usat->sat_family != AF_APPLETALK) + return -EINVAL; +#if 0 /* netatalk doesnt implement this check */ + if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast) + return -EPERM; +#endif + } + else + { + if(sk->state!=TCP_ESTABLISHED) + return -ENOTCONN; + usat=&local_satalk; + usat->sat_family=AF_APPLETALK; + usat->sat_port=sk->at.dest_port; + usat->sat_addr.s_node=sk->at.dest_node; + usat->sat_addr.s_net=sk->at.dest_net; + } + + /* Build a packet */ + + if(sk->debug) + printk("SK %p: Got address.\n",sk); + + size=sizeof(struct ddpehdr)+len+ddp_dl->header_length; /* For headers */ + + if(usat->sat_addr.s_net!=0 || usat->sat_addr.s_node == ATADDR_ANYNODE) + { + rt=atrtr_find(&usat->sat_addr); + if(rt==NULL) + return -ENETUNREACH; + dev=rt->dev; + } + else + { + struct at_addr at_hint; + at_hint.s_node=0; + at_hint.s_net=sk->at.src_net; + rt=atrtr_find(&at_hint); + if(rt==NULL) + return -ENETUNREACH; + dev=rt->dev; + } + + if(sk->debug) + printk("SK %p: Size needed %d, device %s\n", sk, size, dev->name); + + size += dev->hard_header_len; + + skb = sock_alloc_send_skb(sk, size, 0 , &err); + if(skb==NULL) + return err; + + skb->sk=sk; + skb->free=1; + skb->arp=1; + skb->len=size; + + skb->dev=dev; + + if(sk->debug) + printk("SK %p: Begin build.\n", sk); + + skb->h.raw=skb->data+ddp_dl->header_length+dev->hard_header_len; + + ddp=(struct ddpehdr *)skb->h.raw; + ddp->deh_pad=0; + ddp->deh_hops=0; + ddp->deh_len=len+sizeof(*ddp); + /* + * Fix up the length field [Ok this is horrible but otherwise + * I end up with unions of bit fields and messy bit field order + * compiler/endian dependancies.. + */ + *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); + + ddp->deh_dnet=usat->sat_addr.s_net; + ddp->deh_snet=sk->at.src_net; + ddp->deh_dnode=usat->sat_addr.s_node; + ddp->deh_snode=sk->at.src_node; + ddp->deh_dport=usat->sat_port; + ddp->deh_sport=sk->at.src_port; + + if(sk->debug) + printk("SK %p: Copy user data (%d bytes).\n", sk, len); + + memcpy_fromfs((char *)(ddp+1),ubuf,len); + + if(sk->no_check==1) + ddp->deh_sum=0; + else + ddp->deh_sum=atalk_checksum(ddp, len+sizeof(*ddp)); + + /* + * Loopback broadcast packets to non gateway targets (ie routes + * to group we are in) + */ + + if(ddp->deh_dnode==ATADDR_BCAST) + { + if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK))) + { + struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL); + if(skb2) + { + loopback=1; + if(sk->debug) + printk("SK %p: send out(copy).\n", sk); + if(aarp_send_ddp(dev,skb2,&usat->sat_addr, NULL)==-1) + kfree_skb(skb2, FREE_WRITE); + /* else queued/sent above in the aarp queue */ + } + } + } + + if((dev->flags&IFF_LOOPBACK) || loopback) + { + if(sk->debug) + printk("SK %p: Loop back.\n", sk); + /* loop back */ + sk->wmem_alloc-=skb->mem_len; + ddp_dl->datalink_header(ddp_dl, skb, dev->dev_addr); + skb->sk = NULL; + skb->h.raw = skb->data + ddp_dl->header_length + dev->hard_header_len; + skb->len -= ddp_dl->header_length ; + skb->len -= dev->hard_header_len ; + atalk_rcv(skb,dev,NULL); + } + else + { + if(sk->debug) + printk("SK %p: send out.\n", sk); + + if ( rt->flags & RTF_GATEWAY ) { + gsat.sat_addr = rt->gateway; + usat = &gsat; + } + + if(aarp_send_ddp(dev,skb,&usat->sat_addr, NULL)==-1) + kfree_skb(skb, FREE_WRITE); + /* else queued/sent above in the aarp queue */ + } + if(sk->debug) + printk("SK %p: Done write (%d).\n", sk, len); + return len; +} + +static int atalk_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags) +{ + return atalk_sendto(sock,ubuf,size,noblock,flags,NULL,0); +} + +static int atalk_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags, struct sockaddr *sip, int *addr_len) +{ + atalk_socket *sk=(atalk_socket *)sock->data; + struct sockaddr_at *sat=(struct sockaddr_at *)sip; + struct ddpehdr *ddp = NULL; + int copied = 0; + struct sk_buff *skb; + int er; + + if(sk->err) + { + er= -sk->err; + sk->err=0; + return er; + } + + if(addr_len) + *addr_len=sizeof(*sat); + + skb=skb_recv_datagram(sk,flags,noblock,&er); + if(skb==NULL) + return er; + + ddp = (struct ddpehdr *)(skb->h.raw); + if(sk->type==SOCK_RAW) + { + copied=ddp->deh_len; + if(copied > size) + copied=size; + skb_copy_datagram(skb,0,ubuf,copied); + } + else + { + copied=ddp->deh_len - sizeof(*ddp); + if (copied > size) + copied = size; + skb_copy_datagram(skb,sizeof(*ddp),ubuf,copied); + } + if(sat) + { + sat->sat_family=AF_APPLETALK; + sat->sat_port=ddp->deh_sport; + sat->sat_addr.s_node=ddp->deh_snode; + sat->sat_addr.s_net=ddp->deh_snet; + } + skb_free_datagram(skb); + return(copied); +} + + +static int atalk_write(struct socket *sock, char *ubuf, int size, int noblock) +{ + return atalk_send(sock,ubuf,size,noblock,0); +} + + +static int atalk_recv(struct socket *sock, void *ubuf, int size , int noblock, + unsigned flags) +{ + atalk_socket *sk=(atalk_socket *)sock->data; + if(sk->zapped) + return -ENOTCONN; + return atalk_recvfrom(sock,ubuf,size,noblock,flags,NULL, NULL); +} + +static int atalk_read(struct socket *sock, char *ubuf, int size, int noblock) +{ + return atalk_recv(sock,ubuf,size,noblock,0); +} + + +static int atalk_shutdown(struct socket *sk,int how) +{ + return -EOPNOTSUPP; +} + +static int atalk_select(struct socket *sock , int sel_type, select_table *wait) +{ + atalk_socket *sk=(atalk_socket *)sock->data; + + return datagram_select(sk,sel_type,wait); +} + +/* + * Appletalk ioctl calls. + */ + +static int atalk_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg) +{ + int err; + long amount=0; + atalk_socket *sk=(atalk_socket *)sock->data; + int v; + + switch(cmd) + { + /* + * Protocol layer + */ + case TIOCOUTQ: + v=sk->sndbuf-sk->wmem_alloc; + if(v<0) + v=0; + break; + 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) + v=skb->len-sizeof(struct ddpehdr); + break; + } + 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; + /* + * Routing + */ + case SIOCADDRT: + case SIOCDELRT: + if(!suser()) + return -EPERM; + return(atrtr_ioctl(cmd,(void *)arg)); + /* + * Interface + */ + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFBRDADDR: + return atif_ioctl(cmd,(void *)arg); + /* + * Physical layer ioctl calls + */ + case SIOCSIFLINK: + case SIOCGIFHWADDR: + case SIOCSIFHWADDR: + case OLD_SIOCGIFHWADDR: + case SIOCGIFFLAGS: + case SIOCSIFFLAGS: + case SIOCGIFMTU: + case SIOCGIFCONF: + case SIOCADDMULTI: + case SIOCDELMULTI: + + return(dev_ioctl(cmd,(void *) arg)); + + case SIOCSIFMETRIC: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMEM: + case SIOCSIFMEM: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + return -EINVAL; + + default: + return -EINVAL; + } + err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long)); + if(err) + return err; + put_fs_long(amount,(unsigned long *)arg); + return(0); +} + +static struct proto_ops atalk_proto_ops = { + AF_APPLETALK, + + atalk_create, + atalk_dup, + atalk_release, + atalk_bind, + atalk_connect, + atalk_socketpair, + atalk_accept, + atalk_getname, + atalk_read, + atalk_write, + atalk_select, + atalk_ioctl, + atalk_listen, + atalk_send, + atalk_recv, + atalk_sendto, + atalk_recvfrom, + atalk_shutdown, + atalk_setsockopt, + atalk_getsockopt, + atalk_fcntl, +}; + +static struct notifier_block ddp_notifier={ + ddp_device_event, + NULL, + 0 +}; + +/* Called by proto.c on kernel start up */ + +void atalk_proto_init(struct net_proto *pro) +{ + static char ddp_snap_id[]={0x08,0x00,0x07,0x80,0x9B}; + (void) sock_register(atalk_proto_ops.family, &atalk_proto_ops); + if((ddp_dl=register_snap_client(ddp_snap_id, atalk_rcv))==NULL) + printk("Unable to register DDP with SNAP.\n"); + register_netdevice_notifier(&ddp_notifier); + aarp_proto_init(); + printk("Appletalk ALPHA 0.08 for Linux NET3.029\n"); + +} +#endif |