patch-2.1.15 linux/net/netbeui/netbeui.c
Next file: linux/net/netbeui/netbeui_llc.c
Previous file: linux/net/netbeui/README
Back to the patch index
Back to the overall index
- Lines: 967
- Date:
Thu Dec 12 16:54:26 1996
- Orig file:
v2.1.14/linux/net/netbeui/netbeui.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.14/linux/net/netbeui/netbeui.c linux/net/netbeui/netbeui.c
@@ -0,0 +1,966 @@
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.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/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+
+
+#undef NETBEUI_DEBUG
+
+
+#ifdef NETBEUI_DEBUG
+#define DPRINT(x) print(x)
+#else
+#define DPRINT(x)
+#endif
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+/***********************************************************************************************************************\
+* *
+* Handlers for the socket list. *
+* *
+\***********************************************************************************************************************/
+
+static netbeui_socket *volatile netbeui_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 netbeui_remove_socket(netbeui_socket *sk)
+{
+ unsigned long flags;
+ netbeui_socket *s;
+
+ save_flags(flags);
+ cli();
+
+ s=netbeui_socket_list;
+ if(s==sk)
+ {
+ netbeui_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 netbeui_insert_socket(netbeui_socket *sk)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ sk->next=netbeui_socket_list;
+ netbeui_socket_list=sk;
+ restore_flags(flags);
+}
+
+/*
+ * This is only called from user mode. Thus it protects itself against
+ * interrupt users but doesn't worry about being called during work.
+ * Once it is removed from the queue no interrupt or bottom half will
+ * touch it and we are (fairly 8-) ) safe.
+ */
+
+static void netbeui_destroy_socket(netbeui_socket *sk);
+
+/*
+ * Handler for deferred kills.
+ */
+
+static void netbeui_destroy_timer(unsigned long data)
+{
+ netbeui_destroy_socket((netbeui_socket *)data);
+}
+
+static void netbeui_destroy_socket(netbeui_socket *sk)
+{
+ struct sk_buff *skb;
+ netbeui_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=netbeui_destroy_timer;
+ sk->timer.data = (unsigned long)sk;
+ add_timer(&sk->timer);
+ }
+}
+
+
+/*
+ * Called from proc fs
+ */
+
+int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ netbeui_socket *s;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
+ /*
+ * Output the netbeui data for the /proc virtual fs.
+ */
+
+ len += sprintf (buffer,"Type local_addr remote_addr tx_queue rx_queue st uid\n");
+ for (s = netbeui_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;
+}
+
+/*
+ * A device event has occurred. Watch for devices going down and
+ * delete our use of them (iface and route).
+ */
+
+static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ if(event==NETDEV_DOWN)
+ {
+ /* Discard any use of this */
+ netbeui_drop_device((struct device *)ptr);
+ }
+ return NOTIFY_DONE;
+}
+
+/*******************************************************************************************************************\
+* *
+* Handling for system calls applied via the various interfaces to a netbeui socket object *
+* *
+\*******************************************************************************************************************/
+
+/*
+ * 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 netbeui_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+/* netbeui_socket *sk=(netbeui_socket *)sock->data;*/
+ switch(cmd)
+ {
+ default:
+ return(-EINVAL);
+ }
+}
+
+/*
+ * Set 'magic' options for netbeui. If we don't have any this is fine
+ * as it is.
+ */
+
+static int netbeui_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+ netbeui_socket *sk;
+ int err,opt;
+
+ sk=(netbeui_socket *)sock->data;
+
+ if(optval==NULL)
+ return(-EINVAL);
+
+ err = get_user(opt, (int *)optval);
+ if (err)
+ return err;
+
+ switch(level)
+ {
+ case SOL_NETBEUI:
+ 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 netbeui_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ netbeui_socket *sk;
+ int val=0;
+ int err;
+
+ sk=(netbeui_socket *)sock->data;
+
+ switch(level)
+ {
+
+ case SOL_NETBEUI:
+ switch(optname)
+ {
+ default:
+ return -ENOPROTOOPT;
+ }
+ break;
+
+ case SOL_SOCKET:
+ return sock_getsockopt(sk,level,optname,optval,optlen);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ err = put_user(sizeof(int),optlen);
+ if (!err)
+ err = put_user(val, (int *) optval);
+ return err;
+}
+
+/*
+ * Only for connection oriented sockets - ignore
+ */
+
+static int netbeui_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk=(netbeui_socket *)sock->data;
+ if(sk->state!=TCP_CLOSED)
+ return -EINVAL;
+ if(backlog<0)
+ return -EINVAL;
+ if(backlog<128)
+ sk->backlog=backlog;
+ else
+ sk->backlog=128;
+ sk->state=TCP_LISTEN;
+ sk->state_change(sk);
+ netbeui_llc_listen(sk);
+ return 0;
+}
+
+/*
+ * 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 netbeui_create(struct socket *sock, int protocol)
+{
+ netbeui_socket *sk;
+ sk=(netbeui_socket *)sk_alloc(GFP_KERNEL);
+ if(sk==NULL)
+ return(-ENOBUFS);
+ switch(sock->type)
+ {
+ case SOCK_DGRAM:
+ break;
+ case SOCK_SEQPACKET:
+ break;
+ default:
+ sk_free((void *)sk);
+ return(-ESOCKTNOSUPPORT);
+ }
+
+ sk->llc802=llc_alloc(GFP_KERNEL);
+ if(sk->llc802==NULL)
+ {
+ sk_free((void *)sk);
+ return -ENOBUFS:
+ }
+
+ MOD_INC_USE_COUNT;
+
+ sk->allocation=GFP_KERNEL;
+ sk->rcvbuf=SK_RMEM_MAX;
+ sk->sndbuf=SK_WMEM_MAX;
+ sk->pair=NULL;
+ sk->priority=SOPRI_NORMAL;
+ 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=1500;
+
+ 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 netbeui_dup(struct socket *newsock,struct socket *oldsock)
+{
+ return(netbeui_create(newsock,oldsock->type));
+}
+
+/*
+ * Free a socket. No work needed
+ */
+
+static int netbeui_release(struct socket *sock, struct socket *peer)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ if(sk==NULL)
+ return(0);
+ if(!sk->dead)
+ sk->state_change(sk);
+ sk->dead=1;
+ sock->data=NULL;
+ netbeui_destroy_socket(sk);
+ return(0);
+}
+
+/*
+ * Set the address 'our end' of the connection.
+ */
+
+static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len)
+{
+ netbeui_socket *sk;
+ struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+
+ sk=(netbeui_socket *)sock->data;
+
+ if(sk->zapped==0)
+ return(-EINVAL);
+
+ if(addr_len!=sizeof(struct sockaddr_at))
+ return -EINVAL;
+
+ if(addr->sat_family!=AF_NETBEUI)
+ return -EAFNOSUPPORT;
+
+ if(netbeui_find_socket(addr)!=NULL)
+ return -EADDRINUSE;
+
+ netbeui_insert_socket(sk);
+ sk->zapped=0;
+ return(0);
+}
+
+/*
+ * Set the address we talk to.
+ */
+
+static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr,
+ size_t addr_len, int flags)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ struct sockaddr_netbeui *addr;
+
+ sk->state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ if(addr_len!=sizeof(*addr))
+ return(-EINVAL);
+ addr=(struct sockaddr_netbeui *)uaddr;
+
+ if(addr->sat_family!=AF_NETBEUI)
+ return -EAFNOSUPPORT;
+
+/* FIXME - Netbios broadcast semantics ?? */
+ if(addr->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
+ return -EACCES;
+
+ if(sk->zapped)
+ return -EINVAL;
+
+ if(atrtr_get_dev(&addr->sat_addr)==NULL)
+ return -ENETUNREACH;
+
+}
+
+/*
+ * Not relevant
+ */
+
+static int netbeui_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return(-EOPNOTSUPP);
+}
+
+/*
+ * WRITE ME
+ */
+
+static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ if(newsock->data)
+ sk_free(newsock->data);
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Find the name of a netbeui socket. Just copy the right
+ * fields into the sockaddr.
+ */
+
+static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr,
+ size_t *uaddr_len, int peer)
+{
+ struct sockaddr_netbeui snb;
+ netbeui_socket *sk;
+
+ sk=(netbeui_socket *)sock->data;
+ if(sk->zapped)
+ {
+ return -EINVAL;
+ }
+
+ *uaddr_len = sizeof(struct sockaddr_netbeui);
+
+ if(peer)
+ {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ }
+ else
+ {
+ }
+ snb.snb_family = AF_NETBEUI;
+ memcpy(uaddr,&snb,sizeof(snb));
+ return(0);
+}
+
+/*
+ * Receive a packet (in skb) from device dev.
+ */
+
+static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ netbeui_socket *sock;
+}
+
+static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ struct sockaddr_at *usat=(struct sockaddr_at *)msg->msg_name;
+ struct sockaddr_at local_snetbeui, gsat;
+ struct sk_buff *skb;
+ struct device *dev;
+ struct ddpehdr *ddp;
+ int size;
+ struct netbeui_route *rt;
+ int loopback=0;
+ int err;
+
+ if(flags)
+ return -EINVAL;
+
+ if(len>587)
+ return -EMSGSIZE;
+
+ if(usat)
+ {
+ if(sk->zapped)
+ {
+ if(netbeui_autobind(sk)<0)
+ return -EBUSY;
+ }
+
+ if(msg->msg_namelen <sizeof(*usat))
+ return(-EINVAL);
+ if(usat->sat_family != AF_NETBEUI)
+ return -EINVAL;
+#if 0 /* netnetbeui 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_snetbeui;
+ usat->sat_family=AF_NETBEUI;
+ 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+nb_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,nb_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);
+
+ err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
+ if (err)
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return -EFAULT;
+ }
+
+ if(sk->no_check==1)
+ ddp->deh_sum=0;
+ else
+ ddp->deh_sum=netbeui_checksum(ddp, len+sizeof(*ddp));
+
+#ifdef CONFIG_FIREWALL
+
+ if(call_out_firewall(AF_NETBEUI, 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=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 */
+ atomic_sub(skb->truesize, &sk->wmem_alloc);
+ nb_dl->datalink_header(nb_dl, skb, dev->dev_addr);
+ skb->sk = NULL;
+ skb->mac.raw=skb->data;
+ skb->h.raw = skb->data + nb_dl->header_length + dev->hard_header_len;
+ skb_pull(skb,dev->hard_header_len);
+ skb_pull(skb,nb_dl->header_length);
+ netbeui_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(nb_send_low(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 netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+{
+ netbeui_socket *sk=(netbeui_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 = 0;
+
+ 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;
+ msg->msg_flags|=MSG_TRUNC;
+ }
+ er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
+ if (er)
+ goto out;
+ }
+ else
+ {
+ copied=ddp->deh_len - sizeof(*ddp);
+ if (copied > size)
+ {
+ copied = size;
+ msg->msg_flags|=MSG_TRUNC;
+ }
+ er = skb_copy_datagram_iovec(skb,sizeof(*ddp),msg->msg_iov,copied);
+ if (er)
+ goto out;
+ }
+ if(sat)
+ {
+ sat->sat_family=AF_NETBEUI;
+ sat->sat_port=ddp->deh_sport;
+ sat->sat_addr.s_node=ddp->deh_snode;
+ sat->sat_addr.s_net=ddp->deh_snet;
+ }
+out:
+ skb_free_datagram(sk, skb);
+ return er ? er : (copied);
+}
+
+
+static int netbeui_shutdown(struct socket *sk,int how)
+{
+ return -EOPNOTSUPP;
+}
+
+static int netbeui_select(struct socket *sock , int sel_type, select_table *wait)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+
+ return datagram_select(sk,sel_type,wait);
+}
+
+/*
+ * Netbeui ioctl calls.
+ */
+
+static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+ long amount=0;
+ netbeui_socket *sk=(netbeui_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;
+ return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 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 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;
+ }
+ return put_user(amount, (int *)arg);
+}
+
+static struct proto_ops netbeui_proto_ops = {
+ AF_NETBEUI,
+
+ netbeui_create,
+ netbeui_dup,
+ netbeui_release,
+ netbeui_bind,
+ netbeui_connect,
+ netbeui_socketpair,
+ netbeui_accept,
+ netbeui_getname,
+ netbeui_select,
+ netbeui_ioctl,
+ netbeui_listen,
+ netbeui_shutdown,
+ netbeui_setsockopt,
+ netbeui_getsockopt,
+ netbeui_fcntl,
+ netbeui_sendmsg,
+ netbeui_recvmsg
+};
+
+static struct notifier_block nb_notifier={
+ nb_device_event,
+ NULL,
+ 0
+};
+
+static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_netbeui = {
+ PROC_NET_NETBEUI, 9, "netbeui",
+ S_IFREG | S_IRUGO, 1, 0, 0
+ 0, &proc_net_inode_operations,
+ netbeui_get_info
+};
+#endif
+
+/* Called by proto.c on kernel start up */
+
+void netbeui_proto_init(struct net_proto *pro)
+{
+ (void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);
+/* ddp? isn't it atalk too? 8) */
+ if ((nb_dl = register_snap_client(nb_snap_id, netbeui_rcv)) == NULL)
+ printk(KERN_CRIT "Unable to register DDP with SNAP.\n");
+
+ register_netdevice_notifier(&nb_notifier);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_netbeui);
+#endif
+
+ printk(KERN_INFO "NetBEUI 0.02 for Linux NET3.037\n");
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ netbeui_proto_init(NULL);
+ register_symtab(0);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unsigned long flags;
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_NETBEUI);
+#endif
+ unregister_netdevice_notifier(&nb_notifier);
+ unregister_snap_client(nb_snap_id);
+ sock_unregister(netbeui_proto_ops.family);
+}
+
+#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov