URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [net/] [appletalk/] [ddp.c] - Rev 1772
Go to most recent revision | Compare with Previous | Blame | View Log
/* * 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> * * Fixes: * Michael Callahan : Made routing work * Wesley Craig : Fix probing to listen to a * passed node id. * Alan Cox : Added send/recvmsg support * Alan Cox : Moved at. to protinfo in * socket. * Alan Cox : Added firewall hooks. * Alan Cox : Supports new ARPHRD_LOOPBACK * Christer Weinigel : Routing and /proc fixes. * Bradford Johnson : Localtalk. * Tom Dyas : Module support. * Alan Cox : Hooks for PPP (based on the * localtalk hook). * * 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 */ #include <linux/config.h> #include <linux/module.h> #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/route.h> #include <linux/inet.h> #include <linux/notifier.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/if_arp.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 <linux/atalk.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/firewall.h> #undef 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->protinfo.af_at.src_port ) { continue; } if ( to->sat_addr.s_net == 0 && to->sat_addr.s_node == ATADDR_BCAST && s->protinfo.af_at.src_net == atif->address.s_net ) { break; } if ( to->sat_addr.s_net == s->protinfo.af_at.src_net && (to->sat_addr.s_node == s->protinfo.af_at.src_node ||to->sat_addr.s_node == ATADDR_BCAST )) { 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->protinfo.af_at.src_net != sat->sat_addr.s_net ) { continue; } if ( s->protinfo.af_at.src_node != sat->sat_addr.s_node ) { continue; } if ( s->protinfo.af_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) { sk_free(sk); MOD_DEC_USE_COUNT; } else { /* * Someone is using our buffers still.. defer */ init_timer(&sk->timer); sk->timer.expires=jiffies+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, int dummy) { atalk_socket *s; int len=0; off_t pos=0; off_t begin=0; /* * Output the appletalk data for the /proc virtual fs. */ 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 ", ntohs(s->protinfo.af_at.src_net), s->protinfo.af_at.src_node, s->protinfo.af_at.src_port); len += sprintf (buffer+len,"%04X:%02X:%02X ", ntohs(s->protinfo.af_at.dest_net), s->protinfo.af_at.dest_node, s->protinfo.af_at.dest_port); len += sprintf (buffer+len,"%08X:%08X ", 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 probe_node=atif->address.s_node; int netct; int nodect; struct ifreq atreq; struct sockaddr_at *sa; int err; /* * THIS IS A HACK: Farallon cards want to do their own picking of * addresses. This needs tidying up post 1.4, but we need it in * now for the 1.4 release as is. * */ if((atif->dev->type == ARPHRD_LOCALTLK || atif->dev->type == ARPHRD_PPP) && atif->dev->do_ioctl) { /* fake up the request and pass it down */ sa = (struct sockaddr_at*)&atreq.ifr_addr; sa->sat_addr.s_node = probe_node; sa->sat_addr.s_net = probe_net; if (!(err=atif->dev->do_ioctl(atif->dev,&atreq,SIOCSIFADDR))) { (void)atif->dev->do_ioctl(atif->dev,&atreq,SIOCGIFADDR); atif->address.s_net=htons(sa->sat_addr.s_net); atif->address.s_node=sa->sat_addr.s_node; return 0; } /* * If it didn't like our faked request then fail: * This should check against -ENOIOCTLCMD and fall * through. That needs us to fix all the devices up * properly. We can then also dump the localtalk test. */ return err; } /* * 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); } if(probe_node == ATADDR_ANYNODE) probe_node = jiffies&0xFF; /* * Scan the networks. */ atif->status |= ATIF_PROBE; for(netct=0;netct<=netrange;netct++) { /* * Sweep the available nodes from a given start. */ atif->address.s_net=htons(probe_net); for(nodect=0;nodect<256;nodect++) { atif->address.s_node=((nodect+probe_node)&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)) { atif->status &= ~ATIF_PROBE; return 0; } } atif->status&=~ATIF_PROBE_FAIL; } probe_net++; if(probe_net>ntohs(atif->nets.nr_lastnet)) probe_net=ntohs(atif->nets.nr_firstnet); } atif->status &= ~ATIF_PROBE; 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 || node==ATADDR_ANYNODE || 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 occurred. Watch for devices going down and * delete our use of them (iface and route). */ static int ddp_device_event(struct notifier_block *this, 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&&dev->type!=ARPHRD_LOOPBACK &&dev->type!=ARPHRD_LOCALTLK && dev->type!=ARPHRD_PPP) return -EPROTONOSUPPORT; nr=(struct netrange *)&sa->sat_zero[0]; /* * Phase 1 is fine on localtalk but we don't * do Ethertalk phase 1. Anyone wanting to add * it go ahead. */ if(dev->type==ARPHRD_ETHER && 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) > 4096) { printk(KERN_WARNING "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; case SIOCATALKDIFADDR: if(!suser()) return -EPERM; if(sa->sat_family!=AF_APPLETALK) return -EINVAL; if(atif==NULL) return -EADDRNOTAVAIL; atrtr_device_down(atif->dev); atif_drop_device(atif->dev); 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, int dummy) { 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, int dummy) { 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 %04X:%02X %-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 %04X:%02X %-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 separate. */ 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_user(sizeof(int),optlen); err=verify_area(VERIFY_WRITE,optval,sizeof(int)); if (err) return err; put_user(val,(int *)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 *)sk_alloc(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: sk_free((void *)sk); return(-ESOCKTNOSUPPORT); } MOD_INC_USE_COUNT; sk->no_check=0; /* Checksums on by default */ sk->no_check=1; /* Checksums off by default */ sk->allocation=GFP_KERNEL; sk->rcvbuf=SK_RMEM_MAX; sk->sndbuf=SK_WMEM_MAX; sk->pair=NULL; sk->priority=1; skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); skb_queue_head_init(&sk->back_log); sk->state=TCP_CLOSE; sk->socket=sock; sk->type=sock->type; 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->protinfo.af_at.src_net = sat.sat_addr.s_net = ap->s_net; sk->protinfo.af_at.src_node = sat.sat_addr.s_node = ap->s_node; if (( n = atalk_pick_port( &sat )) < 0 ) return( n ); sk->protinfo.af_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->protinfo.af_at.src_net=addr->sat_addr.s_net=ap->s_net; sk->protinfo.af_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->protinfo.af_at.src_net=addr->sat_addr.s_net; sk->protinfo.af_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->protinfo.af_at.src_port=addr->sat_port=n; } else sk->protinfo.af_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 doesn't 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->protinfo.af_at.dest_port=addr->sat_port; sk->protinfo.af_at.dest_net=addr->sat_addr.s_net; sk->protinfo.af_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) sk_free(newsock->data); 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->protinfo.af_at.dest_net; sat.sat_addr.s_node=sk->protinfo.af_at.dest_node; sat.sat_port=sk->protinfo.af_at.dest_port; } else { sat.sat_addr.s_net=sk->protinfo.af_at.src_net; sat.sat_addr.s_node=sk->protinfo.af_at.src_node; sat.sat_port=sk->protinfo.af_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. PPP should probably pass frames marked as for this layer * [ie ARPHRD_ETHERTALK] */ static 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; int origlen; /* 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 dependencies..] */ *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* * Trim buffer in case of stray trailing data */ origlen = skb->len; skb_trim(skb,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 recvmsg()). */ 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); } #ifdef CONFIG_FIREWALL if(call_in_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT) { kfree_skb(skb, FREE_READ); return 0; } #endif /* 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; /* Don't route multicast, etc., packets, or packets sent to "this network" */ if (skb->pkt_type != PACKET_HOST || ddp->deh_dnet == 0) { kfree_skb(skb, FREE_READ); return(0); } #ifdef CONFIG_FIREWALL /* * Check firewall allows this routing */ if(call_fw_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT) { kfree_skb(skb, FREE_READ); return(0); } #endif 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++; /* * Route goes through another gateway, so * set the target to the gateway instead. */ if(rt->flags&RTF_GATEWAY) { ta.s_net = rt->gateway.s_net; ta.s_node = rt->gateway.s_node; } /* Fix up skb->len field */ skb_trim(skb,min(origlen, rt->dev->hard_header_len + ddp_dl->header_length + ddp->deh_len)); *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* Mend the byte order */ /* * Send the buffer onwards */ skb->arp = 1; /* Resolved */ if(aarp_send_ddp(rt->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; if (ddp->deh_dnode == ATADDR_ANYNODE) tosat.sat_addr.s_node = atif->address.s_node; else 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); } /* * Receive a localtalk frame. We make some demands on the caller here. * Caller must provide enough headroom on the packet to pull the short * header and append a long one. */ static int ltalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { struct ddpehdr *ddp; struct at_addr *ap; /* * Expand any short form frames. */ if(skb->mac.raw[2]==1) { /* * Find our address. */ ap=atalk_find_dev_addr(dev); if(ap==NULL || skb->len<sizeof(struct ddpshdr)) { kfree_skb(skb, FREE_READ); return 0; } /* * The push leaves us with a ddephdr not an shdr, and * handily the port bytes in the right place preset. */ skb_push(skb, sizeof(*ddp)-4); ddp=(struct ddpehdr *)skb->data; /* * Now fill in the long header. */ /* * These two first. The mac overlays the new source/dest * network information so we MUST copy these before * we write the network numbers ! */ ddp->deh_dnode=skb->mac.raw[0]; /* From physical header */ ddp->deh_snode=skb->mac.raw[1]; /* From physical header */ ddp->deh_dnet=ap->s_net; /* Network number */ ddp->deh_snet=ap->s_net; ddp->deh_sum=0; /* No checksum */ /* * Not sure about this bit... */ ddp->deh_len=skb->len; ddp->deh_hops=15; /* Non routable, so force a drop if we slip up later */ *((__u16 *)ddp)=htons(*((__u16 *)ddp)); /* Mend the byte order */ } skb->h.raw = skb->data; return atalk_rcv(skb,dev,pt); } /* * This is slower, and copies the whole data area */ static struct sk_buff *ddp_skb_copy(struct sk_buff *skb, int priority) { struct sk_buff *n; unsigned long offset; /* * Allocate the copy buffer */ IS_SKB(skb); n=alloc_skb(skb->truesize-sizeof(struct sk_buff),priority); if(n==NULL) return NULL; /* * Shift between the two data areas in bytes */ offset=n->head-skb->head; /* Set the data pointer */ skb_reserve(n,skb->data-skb->head); /* Set the tail pointer and length */ skb_put(n,skb->len); /* Copy the bytes */ memcpy(n->head,skb->head,skb->end-skb->head); n->link3=NULL; n->list=NULL; n->sk=NULL; n->when=skb->when; n->dev=skb->dev; n->h.raw=skb->h.raw+offset; n->mac.raw=skb->mac.raw+offset; n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset); n->saddr=skb->saddr; n->daddr=skb->daddr; n->raddr=skb->raddr; n->seq=skb->seq; n->end_seq=skb->end_seq; n->ack_seq=skb->ack_seq; n->acked=skb->acked; memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); 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; n->stamp=skb->stamp; IS_SKB(n); return n; } static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags) { atalk_socket *sk=(atalk_socket *)sock->data; struct sockaddr_at *usat=(struct sockaddr_at *)msg->msg_name; 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(msg->msg_namelen <sizeof(*usat)) return(-EINVAL); if(usat->sat_family != AF_APPLETALK) return -EINVAL; #if 0 /* netatalk doesn't 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->protinfo.af_at.dest_port; usat->sat_addr.s_node=sk->protinfo.af_at.dest_node; usat->sat_addr.s_net=sk->protinfo.af_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->protinfo.af_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, 0 , &err); if(skb==NULL) return err; skb->sk=sk; skb->free=1; skb->arp=1; skb_reserve(skb,ddp_dl->header_length); skb_reserve(skb,dev->hard_header_len); skb->dev=dev; if(sk->debug) printk("SK %p: Begin build.\n", sk); ddp=(struct ddpehdr *)skb_put(skb,sizeof(struct ddpehdr)); 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 dependencies.. */ *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); ddp->deh_dnet=usat->sat_addr.s_net; ddp->deh_snet=sk->protinfo.af_at.src_net; ddp->deh_dnode=usat->sat_addr.s_node; ddp->deh_snode=sk->protinfo.af_at.src_node; ddp->deh_dport=usat->sat_port; ddp->deh_sport=sk->protinfo.af_at.src_port; if(sk->debug) printk("SK %p: Copy user data (%d bytes).\n", sk, len); memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len); if(sk->no_check==1) ddp->deh_sum=0; else ddp->deh_sum=atalk_checksum(ddp, len+sizeof(*ddp)); #ifdef CONFIG_FIREWALL if(call_out_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT) { kfree_skb(skb, FREE_WRITE); return -EPERM; } #endif /* * 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; /* Make a copy of the skbuf so that the loopback does not trash it * in the next block. * Added by Peter Skarpetis, Serendipity Software 24 June 1997 */ skb2 = ddp_skb_copy(skb, GFP_ATOMIC); if (skb2 == NULL) { printk("ddp.c: cannot allocate skb copy buffer\n"); return -1; } 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 */ atomic_sub(skb->truesize, &sk->wmem_alloc); ddp_dl->datalink_header(ddp_dl, skb, dev->dev_addr); skb->sk = NULL; skb->mac.raw=skb->data; skb->h.raw = skb->data + ddp_dl->header_length + dev->hard_header_len; skb_pull(skb,dev->hard_header_len); skb_pull(skb,ddp_dl->header_length); 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_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) { atalk_socket *sk=(atalk_socket *)sock->data; struct sockaddr_at *sat=(struct sockaddr_at *)msg->msg_name; struct ddpehdr *ddp = NULL; int copied = 0; struct sk_buff *skb; int er; if(sk->err) return sock_error(sk); 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_iovec(skb,0,msg->msg_iov,copied); } else { copied=ddp->deh_len - sizeof(*ddp); if (copied > size) copied = size; skb_copy_datagram_iovec(skb,sizeof(*ddp),msg->msg_iov,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(sk, skb); return(copied); } 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; switch(cmd) { /* * Protocol layer */ case TIOCOUTQ: amount=sk->sndbuf-sk->wmem_alloc; if(amount<0) amount=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) amount=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: case SIOCATALKDIFADDR: return atif_ioctl(cmd,(void *)arg); /* * Physical layer ioctl calls */ case SIOCSIFLINK: case SIOCGIFHWADDR: case SIOCSIFHWADDR: 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_select, atalk_ioctl, atalk_listen, atalk_shutdown, atalk_setsockopt, atalk_getsockopt, atalk_fcntl, atalk_sendmsg, atalk_recvmsg }; static struct notifier_block ddp_notifier={ ddp_device_event, NULL, 0 }; struct packet_type ltalk_packet_type= { 0, NULL, ltalk_rcv, NULL, NULL }; struct packet_type ppptalk_packet_type= { 0, NULL, atalk_rcv, NULL, NULL }; static char ddp_snap_id[]={0x08,0x00,0x07,0x80,0x9B}; /* Called by proto.c on kernel start up */ #ifdef CONFIG_PROC_FS static struct proc_dir_entry pde1 = { PROC_NET_ATALK, 9, "appletalk", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, atalk_get_info }; static struct proc_dir_entry pde2 = { PROC_NET_AT_ROUTE, 11,"atalk_route", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, atalk_rt_get_info }; static struct proc_dir_entry pde3 = { PROC_NET_ATIF, 11,"atalk_iface", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, atalk_if_get_info }; #endif void atalk_proto_init(struct net_proto *pro) { (void) sock_register(atalk_proto_ops.family, &atalk_proto_ops); if ((ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv)) == NULL) printk(KERN_CRIT "Unable to register DDP with SNAP.\n"); ltalk_packet_type.type=htons(ETH_P_LOCALTALK); dev_add_pack(<alk_packet_type); ppptalk_packet_type.type=htons(ETH_P_PPPTALK); dev_add_pack(&ppptalk_packet_type); register_netdevice_notifier(&ddp_notifier); aarp_proto_init(); #ifdef CONFIG_PROC_FS proc_net_register(&pde1); proc_net_register(&pde2); proc_net_register(&pde3); #endif printk(KERN_INFO "Appletalk 0.17 for Linux NET3.035\n"); } #ifdef MODULE int init_module(void) { atalk_proto_init(NULL); register_symtab(0); return 0; } /* * FIX THIS: If there are any routes/devices configured * for appletalk we must not be unloaded. */ /* Remove all route entries. Interrupts must be off. */ extern inline void free_route_list(void) { struct atalk_route *list = atalk_router_list, *tmp; while (list != NULL) { tmp = list->next; kfree_s(list, sizeof(struct atalk_route)); list = tmp; } } /* Remove all interface entries. Interrupts must be off. */ extern inline void free_interface_list(void) { struct atalk_iface *list = atalk_iface_list, *tmp; while (list != NULL) { tmp = list->next; kfree_s(list, sizeof(struct atalk_iface)); list = tmp; } } void cleanup_module(void) { unsigned long flags; save_flags(flags); cli(); aarp_cleanup_module(); #ifdef CONFIG_PROC_FS proc_net_unregister(PROC_NET_ATALK); proc_net_unregister(PROC_NET_AT_ROUTE); proc_net_unregister(PROC_NET_ATIF); #endif unregister_netdevice_notifier(&ddp_notifier); dev_remove_pack(<alk_packet_type); dev_remove_pack(&ppptalk_packet_type); unregister_snap_client(ddp_snap_id); sock_unregister(atalk_proto_ops.family); free_route_list(); free_interface_list(); restore_flags(flags); } #endif /* MODULE */
Go to most recent revision | Compare with Previous | Blame | View Log