diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/decnet |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/decnet')
-rw-r--r-- | net/decnet/Kconfig | 27 | ||||
-rw-r--r-- | net/decnet/Makefile | 10 | ||||
-rw-r--r-- | net/decnet/README | 8 | ||||
-rw-r--r-- | net/decnet/TODO | 41 | ||||
-rw-r--r-- | net/decnet/af_decnet.c | 2405 | ||||
-rw-r--r-- | net/decnet/dn_dev.c | 1481 | ||||
-rw-r--r-- | net/decnet/dn_fib.c | 802 | ||||
-rw-r--r-- | net/decnet/dn_neigh.c | 627 | ||||
-rw-r--r-- | net/decnet/dn_nsp_in.c | 934 | ||||
-rw-r--r-- | net/decnet/dn_nsp_out.c | 782 | ||||
-rw-r--r-- | net/decnet/dn_route.c | 1840 | ||||
-rw-r--r-- | net/decnet/dn_rules.c | 416 | ||||
-rw-r--r-- | net/decnet/dn_table.c | 825 | ||||
-rw-r--r-- | net/decnet/dn_timer.c | 109 | ||||
-rw-r--r-- | net/decnet/netfilter/Kconfig | 15 | ||||
-rw-r--r-- | net/decnet/netfilter/Makefile | 6 | ||||
-rw-r--r-- | net/decnet/netfilter/dn_rtmsg.c | 167 | ||||
-rw-r--r-- | net/decnet/sysctl_net_decnet.c | 480 |
18 files changed, 10975 insertions, 0 deletions
diff --git a/net/decnet/Kconfig b/net/decnet/Kconfig new file mode 100644 index 000000000000..2101da542ba8 --- /dev/null +++ b/net/decnet/Kconfig @@ -0,0 +1,27 @@ +# +# DECnet configuration +# +config DECNET_ROUTER + bool "DECnet: router support (EXPERIMENTAL)" + depends on DECNET && EXPERIMENTAL + ---help--- + Add support for turning your DECnet Endnode into a level 1 or 2 + router. This is an experimental, but functional option. If you + do say Y here, then make sure that you also say Y to "Kernel/User + network link driver", "Routing messages" and "Network packet + filtering". The first two are required to allow configuration via + rtnetlink (you will need Alexey Kuznetsov's iproute2 package + from <ftp://ftp.tux.org/pub/net/ip-routing/>). The "Network packet + filtering" option will be required for the forthcoming routing daemon + to work. + + See <file:Documentation/networking/decnet.txt> for more information. + +config DECNET_ROUTE_FWMARK + bool "DECnet: use FWMARK value as routing key (EXPERIMENTAL)" + depends on DECNET_ROUTER && NETFILTER + help + If you say Y here, you will be able to specify different routes for + packets with different FWMARK ("firewalling mark") values + (see ipchains(8), "-m" argument). + diff --git a/net/decnet/Makefile b/net/decnet/Makefile new file mode 100644 index 000000000000..e44003af71f6 --- /dev/null +++ b/net/decnet/Makefile @@ -0,0 +1,10 @@ + +obj-$(CONFIG_DECNET) += decnet.o + +decnet-y := af_decnet.o dn_nsp_in.o dn_nsp_out.o \ + dn_route.o dn_dev.o dn_neigh.o dn_timer.o +decnet-$(CONFIG_DECNET_ROUTER) += dn_fib.o dn_rules.o dn_table.o +decnet-y += sysctl_net_decnet.o + +obj-$(CONFIG_NETFILTER) += netfilter/ + diff --git a/net/decnet/README b/net/decnet/README new file mode 100644 index 000000000000..60e7ec88c81f --- /dev/null +++ b/net/decnet/README @@ -0,0 +1,8 @@ + Linux DECnet Project + ====================== + +The documentation for this kernel subsystem is available in the +Documentation/networking subdirectory of this distribution and also +on line at http://www.chygwyn.com/DECnet/ + +Steve Whitehouse <SteveW@ACM.org> diff --git a/net/decnet/TODO b/net/decnet/TODO new file mode 100644 index 000000000000..ebb5ac69d128 --- /dev/null +++ b/net/decnet/TODO @@ -0,0 +1,41 @@ +Steve's quick list of things that need finishing off: +[they are in no particular order and range from the trivial to the long winded] + + o Proper timeouts on each neighbour (in routing mode) rather than + just the 60 second On-Ethernet cache value. + + o Support for X.25 linklayer + + o Support for DDCMP link layer + + o The DDCMP device itself + + o PPP support (rfc1762) + + o Lots of testing with real applications + + o Verify errors etc. against POSIX 1003.1g (draft) + + o Using send/recvmsg() to get at connect/disconnect data (POSIX 1003.1g) + [maybe this should be done at socket level... the control data in the + send/recvmsg() calls should simply be a vector of set/getsockopt() + calls] + + o check MSG_CTRUNC is set where it should be. + + o Find all the commonality between DECnet and IPv4 routing code and extract + it into a small library of routines. [probably a project for 2.7.xx] + + o Add perfect socket hashing - an idea suggested by Paul Koning. Currently + we have a half-way house scheme which seems to work reasonably well, but + the full scheme is still worth implementing, its not not top of my list + right now. + + o Add session control message flow control + + o Add NSP message flow control + + o DECnet sendpages() function + + o AIO for DECnet + diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c new file mode 100644 index 000000000000..29bb3cd21965 --- /dev/null +++ b/net/decnet/af_decnet.c @@ -0,0 +1,2405 @@ + +/* + * DECnet An implementation of the DECnet protocol suite for the LINUX + * operating system. DECnet is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * DECnet Socket Layer Interface + * + * Authors: Eduardo Marcelo Serrat <emserrat@geocities.com> + * Patrick Caulfield <patrick@pandh.demon.co.uk> + * + * Changes: + * Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's + * version of the code. Original copyright preserved + * below. + * Steve Whitehouse: Some bug fixes, cleaning up some code to make it + * compatible with my routing layer. + * Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick + * Caulfield. + * Steve Whitehouse: Further bug fixes, checking module code still works + * with new routing layer. + * Steve Whitehouse: Additional set/get_sockopt() calls. + * Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new + * code. + * Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like + * way. Didn't manage it entirely, but its better. + * Steve Whitehouse: ditto for sendmsg(). + * Steve Whitehouse: A selection of bug fixes to various things. + * Steve Whitehouse: Added TIOCOUTQ ioctl. + * Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username. + * Steve Whitehouse: Fixes to connect() error returns. + * Patrick Caulfield: Fixes to delayed acceptance logic. + * David S. Miller: New socket locking + * Steve Whitehouse: Socket list hashing/locking + * Arnaldo C. Melo: use capable, not suser + * Steve Whitehouse: Removed unused code. Fix to use sk->allocation + * when required. + * Patrick Caulfield: /proc/net/decnet now has object name/number + * Steve Whitehouse: Fixed local port allocation, hashed sk list + * Matthew Wilcox: Fixes for dn_ioctl() + * Steve Whitehouse: New connect/accept logic to allow timeouts and + * prepare for sendpage etc. + */ + + +/****************************************************************************** + (c) 1995-1998 E.M. Serrat emserrat@geocities.com + + 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 + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +HISTORY: + +Version Kernel Date Author/Comments +------- ------ ---- --------------- +Version 0.0.1 2.0.30 01-dic-97 Eduardo Marcelo Serrat + (emserrat@geocities.com) + + First Development of DECnet Socket La- + yer for Linux. Only supports outgoing + connections. + +Version 0.0.2 2.1.105 20-jun-98 Patrick J. Caulfield + (patrick@pandh.demon.co.uk) + + Port to new kernel development version. + +Version 0.0.3 2.1.106 25-jun-98 Eduardo Marcelo Serrat + (emserrat@geocities.com) + _ + Added support for incoming connections + so we can start developing server apps + on Linux. + - + Module Support +Version 0.0.4 2.1.109 21-jul-98 Eduardo Marcelo Serrat + (emserrat@geocities.com) + _ + Added support for X11R6.4. Now we can + use DECnet transport for X on Linux!!! + - +Version 0.0.5 2.1.110 01-aug-98 Eduardo Marcelo Serrat + (emserrat@geocities.com) + Removed bugs on flow control + Removed bugs on incoming accessdata + order + - +Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat + dn_recvmsg fixes + + Patrick J. Caulfield + dn_bind fixes +*******************************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/netdevice.h> +#include <linux/inet.h> +#include <linux/route.h> +#include <linux/netfilter.h> +#include <linux/seq_file.h> +#include <net/sock.h> +#include <net/tcp.h> +#include <net/flow.h> +#include <asm/system.h> +#include <asm/ioctls.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <net/neighbour.h> +#include <net/dst.h> +#include <net/dn.h> +#include <net/dn_nsp.h> +#include <net/dn_dev.h> +#include <net/dn_route.h> +#include <net/dn_fib.h> +#include <net/dn_neigh.h> + +struct dn_sock { + struct sock sk; + struct dn_scp scp; +}; + +static void dn_keepalive(struct sock *sk); + +#define DN_SK_HASH_SHIFT 8 +#define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT) +#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1) + + +static struct proto_ops dn_proto_ops; +static DEFINE_RWLOCK(dn_hash_lock); +static struct hlist_head dn_sk_hash[DN_SK_HASH_SIZE]; +static struct hlist_head dn_wild_sk; + +static int __dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen, int flags); +static int __dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen, int flags); + +static struct hlist_head *dn_find_list(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); + + if (scp->addr.sdn_flags & SDF_WILD) + return hlist_empty(&dn_wild_sk) ? &dn_wild_sk : NULL; + + return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK]; +} + +/* + * Valid ports are those greater than zero and not already in use. + */ +static int check_port(unsigned short port) +{ + struct sock *sk; + struct hlist_node *node; + + if (port == 0) + return -1; + + sk_for_each(sk, node, &dn_sk_hash[port & DN_SK_HASH_MASK]) { + struct dn_scp *scp = DN_SK(sk); + if (scp->addrloc == port) + return -1; + } + return 0; +} + +static unsigned short port_alloc(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); +static unsigned short port = 0x2000; + unsigned short i_port = port; + + while(check_port(++port) != 0) { + if (port == i_port) + return 0; + } + + scp->addrloc = port; + + return 1; +} + +/* + * Since this is only ever called from user + * level, we don't need a write_lock() version + * of this. + */ +static int dn_hash_sock(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); + struct hlist_head *list; + int rv = -EUSERS; + + BUG_ON(sk_hashed(sk)); + + write_lock_bh(&dn_hash_lock); + + if (!scp->addrloc && !port_alloc(sk)) + goto out; + + rv = -EADDRINUSE; + if ((list = dn_find_list(sk)) == NULL) + goto out; + + sk_add_node(sk, list); + rv = 0; +out: + write_unlock_bh(&dn_hash_lock); + return rv; +} + +static void dn_unhash_sock(struct sock *sk) +{ + write_lock(&dn_hash_lock); + sk_del_node_init(sk); + write_unlock(&dn_hash_lock); +} + +static void dn_unhash_sock_bh(struct sock *sk) +{ + write_lock_bh(&dn_hash_lock); + sk_del_node_init(sk); + write_unlock_bh(&dn_hash_lock); +} + +static struct hlist_head *listen_hash(struct sockaddr_dn *addr) +{ + int i; + unsigned hash = addr->sdn_objnum; + + if (hash == 0) { + hash = addr->sdn_objnamel; + for(i = 0; i < dn_ntohs(addr->sdn_objnamel); i++) { + hash ^= addr->sdn_objname[i]; + hash ^= (hash << 3); + } + } + + return &dn_sk_hash[hash & DN_SK_HASH_MASK]; +} + +/* + * Called to transform a socket from bound (i.e. with a local address) + * into a listening socket (doesn't need a local port number) and rehashes + * based upon the object name/number. + */ +static void dn_rehash_sock(struct sock *sk) +{ + struct hlist_head *list; + struct dn_scp *scp = DN_SK(sk); + + if (scp->addr.sdn_flags & SDF_WILD) + return; + + write_lock_bh(&dn_hash_lock); + sk_del_node_init(sk); + DN_SK(sk)->addrloc = 0; + list = listen_hash(&DN_SK(sk)->addr); + sk_add_node(sk, list); + write_unlock_bh(&dn_hash_lock); +} + +int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type) +{ + int len = 2; + + *buf++ = type; + + switch(type) { + case 0: + *buf++ = sdn->sdn_objnum; + break; + case 1: + *buf++ = 0; + *buf++ = dn_ntohs(sdn->sdn_objnamel); + memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel)); + len = 3 + dn_ntohs(sdn->sdn_objnamel); + break; + case 2: + memset(buf, 0, 5); + buf += 5; + *buf++ = dn_ntohs(sdn->sdn_objnamel); + memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel)); + len = 7 + dn_ntohs(sdn->sdn_objnamel); + break; + } + + return len; +} + +/* + * On reception of usernames, we handle types 1 and 0 for destination + * addresses only. Types 2 and 4 are used for source addresses, but the + * UIC, GIC are ignored and they are both treated the same way. Type 3 + * is never used as I've no idea what its purpose might be or what its + * format is. + */ +int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt) +{ + unsigned char type; + int size = len; + int namel = 12; + + sdn->sdn_objnum = 0; + sdn->sdn_objnamel = dn_htons(0); + memset(sdn->sdn_objname, 0, DN_MAXOBJL); + + if (len < 2) + return -1; + + len -= 2; + *fmt = *data++; + type = *data++; + + switch(*fmt) { + case 0: + sdn->sdn_objnum = type; + return 2; + case 1: + namel = 16; + break; + case 2: + len -= 4; + data += 4; + break; + case 4: + len -= 8; + data += 8; + break; + default: + return -1; + } + + len -= 1; + + if (len < 0) + return -1; + + sdn->sdn_objnamel = dn_htons(*data++); + len -= dn_ntohs(sdn->sdn_objnamel); + + if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel)) + return -1; + + memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel)); + + return size - len; +} + +struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr) +{ + struct hlist_head *list = listen_hash(addr); + struct hlist_node *node; + struct sock *sk; + + read_lock(&dn_hash_lock); + sk_for_each(sk, node, list) { + struct dn_scp *scp = DN_SK(sk); + if (sk->sk_state != TCP_LISTEN) + continue; + if (scp->addr.sdn_objnum) { + if (scp->addr.sdn_objnum != addr->sdn_objnum) + continue; + } else { + if (addr->sdn_objnum) + continue; + if (scp->addr.sdn_objnamel != addr->sdn_objnamel) + continue; + if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0) + continue; + } + sock_hold(sk); + read_unlock(&dn_hash_lock); + return sk; + } + + sk = sk_head(&dn_wild_sk); + if (sk) { + if (sk->sk_state == TCP_LISTEN) + sock_hold(sk); + else + sk = NULL; + } + + read_unlock(&dn_hash_lock); + return sk; +} + +struct sock *dn_find_by_skb(struct sk_buff *skb) +{ + struct dn_skb_cb *cb = DN_SKB_CB(skb); + struct sock *sk; + struct hlist_node *node; + struct dn_scp *scp; + + read_lock(&dn_hash_lock); + sk_for_each(sk, node, &dn_sk_hash[cb->dst_port & DN_SK_HASH_MASK]) { + scp = DN_SK(sk); + if (cb->src != dn_saddr2dn(&scp->peer)) + continue; + if (cb->dst_port != scp->addrloc) + continue; + if (scp->addrrem && (cb->src_port != scp->addrrem)) + continue; + sock_hold(sk); + goto found; + } + sk = NULL; +found: + read_unlock(&dn_hash_lock); + return sk; +} + + + +static void dn_destruct(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); + + skb_queue_purge(&scp->data_xmit_queue); + skb_queue_purge(&scp->other_xmit_queue); + skb_queue_purge(&scp->other_receive_queue); + + dst_release(xchg(&sk->sk_dst_cache, NULL)); +} + +static struct proto dn_proto = { + .name = "DECNET", + .owner = THIS_MODULE, + .obj_size = sizeof(struct dn_sock), +}; + +static struct sock *dn_alloc_sock(struct socket *sock, int gfp) +{ + struct dn_scp *scp; + struct sock *sk = sk_alloc(PF_DECnet, gfp, &dn_proto, 1); + + if (!sk) + goto out; + + if (sock) + sock->ops = &dn_proto_ops; + sock_init_data(sock, sk); + + sk->sk_backlog_rcv = dn_nsp_backlog_rcv; + sk->sk_destruct = dn_destruct; + sk->sk_no_check = 1; + sk->sk_family = PF_DECnet; + sk->sk_protocol = 0; + sk->sk_allocation = gfp; + + /* Initialization of DECnet Session Control Port */ + scp = DN_SK(sk); + scp->state = DN_O; /* Open */ + scp->numdat = 1; /* Next data seg to tx */ + scp->numoth = 1; /* Next oth data to tx */ + scp->ackxmt_dat = 0; /* Last data seg ack'ed */ + scp->ackxmt_oth = 0; /* Last oth data ack'ed */ + scp->ackrcv_dat = 0; /* Highest data ack recv*/ + scp->ackrcv_oth = 0; /* Last oth data ack rec*/ + scp->flowrem_sw = DN_SEND; + scp->flowloc_sw = DN_SEND; + scp->flowrem_dat = 0; + scp->flowrem_oth = 1; + scp->flowloc_dat = 0; + scp->flowloc_oth = 1; + scp->services_rem = 0; + scp->services_loc = 1 | NSP_FC_NONE; + scp->info_rem = 0; + scp->info_loc = 0x03; /* NSP version 4.1 */ + scp->segsize_rem = 230 - DN_MAX_NSP_DATA_HEADER; /* Default: Updated by remote segsize */ + scp->nonagle = 0; + scp->multi_ireq = 1; + scp->accept_mode = ACC_IMMED; + scp->addr.sdn_family = AF_DECnet; + scp->peer.sdn_family = AF_DECnet; + scp->accessdata.acc_accl = 5; + memcpy(scp->accessdata.acc_acc, "LINUX", 5); + + scp->max_window = NSP_MAX_WINDOW; + scp->snd_window = NSP_MIN_WINDOW; + scp->nsp_srtt = NSP_INITIAL_SRTT; + scp->nsp_rttvar = NSP_INITIAL_RTTVAR; + scp->nsp_rxtshift = 0; + + skb_queue_head_init(&scp->data_xmit_queue); + skb_queue_head_init(&scp->other_xmit_queue); + skb_queue_head_init(&scp->other_receive_queue); + + scp->persist = 0; + scp->persist_fxn = NULL; + scp->keepalive = 10 * HZ; + scp->keepalive_fxn = dn_keepalive; + + init_timer(&scp->delack_timer); + scp->delack_pending = 0; + scp->delack_fxn = dn_nsp_delayed_ack; + + dn_start_slow_timer(sk); +out: + return sk; +} + +/* + * Keepalive timer. + * FIXME: Should respond to SO_KEEPALIVE etc. + */ +static void dn_keepalive(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); + + /* + * By checking the other_data transmit queue is empty + * we are double checking that we are not sending too + * many of these keepalive frames. + */ + if (skb_queue_len(&scp->other_xmit_queue) == 0) + dn_nsp_send_link(sk, DN_NOCHANGE, 0); +} + + +/* + * Timer for shutdown/destroyed sockets. + * When socket is dead & no packets have been sent for a + * certain amount of time, they are removed by this + * routine. Also takes care of sending out DI & DC + * frames at correct times. + */ +int dn_destroy_timer(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); + + scp->persist = dn_nsp_persist(sk); + + switch(scp->state) { + case DN_DI: + dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC); + if (scp->nsp_rxtshift >= decnet_di_count) + scp->state = DN_CN; + return 0; + + case DN_DR: + dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC); + if (scp->nsp_rxtshift >= decnet_dr_count) + scp->state = DN_DRC; + return 0; + + case DN_DN: + if (scp->nsp_rxtshift < decnet_dn_count) { + /* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */ + dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC); + return 0; + } + } + + scp->persist = (HZ * decnet_time_wait); + + if (sk->sk_socket) + return 0; + + if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) { + dn_unhash_sock(sk); + sock_put(sk); + return 1; + } + + return 0; +} + +static void dn_destroy_sock(struct sock *sk) +{ + struct dn_scp *scp = DN_SK(sk); + + scp->nsp_rxtshift = 0; /* reset back off */ + + if (sk->sk_socket) { + if (sk->sk_socket->state != SS_UNCONNECTED) + sk->sk_socket->state = SS_DISCONNECTING; + } + + sk->sk_state = TCP_CLOSE; + + switch(scp->state) { + case DN_DN: + dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, + sk->sk_allocation); + scp->persist_fxn = dn_destroy_timer; + scp->persist = dn_nsp_persist(sk); + break; + case DN_CR: + scp->state = DN_DR; + goto disc_reject; + case DN_RUN: + scp->state = DN_DI; + case DN_DI: + case DN_DR: +disc_reject: + dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->sk_allocation); + case DN_NC: + case DN_NR: + case DN_RJ: + case DN_DIC: + case DN_CN: + case DN_DRC: + case DN_CI: + case DN_CD: + scp->persist_fxn = dn_destroy_timer; + scp->persist = dn_nsp_persist(sk); + break; + default: + printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n"); + case DN_O: + dn_stop_slow_timer(sk); + + dn_unhash_sock_bh(sk); + sock_put(sk); + + break; + } +} + +char *dn_addr2asc(dn_address addr, char *buf) +{ + unsigned short node, area; + + node = addr & 0x03ff; + area = addr >> 10; + sprintf(buf, "%hd.%hd", area, node); + + return buf; +} + + + +static int dn_create(struct socket *sock, int protocol) +{ + struct sock *sk; + + switch(sock->type) { + case SOCK_SEQPACKET: + if (protocol != DNPROTO_NSP) + return -EPROTONOSUPPORT; + break; + case SOCK_STREAM: + break; + default: + return -ESOCKTNOSUPPORT; + } + + + if ((sk = dn_alloc_sock(sock, GFP_KERNEL)) == NULL) + return -ENOBUFS; + + sk->sk_protocol = protocol; + + return 0; +} + + +static int +dn_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (sk) { + sock_orphan(sk); + sock_hold(sk); + lock_sock(sk); + dn_destroy_sock(sk); + release_sock(sk); + sock_put(sk); + } + + return 0; +} + +static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sock *sk = sock->sk; + struct dn_scp *scp = DN_SK(sk); + struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr; + struct net_device *dev; + int rv; + + if (addr_len != sizeof(struct sockaddr_dn)) + return -EINVAL; + + if (saddr->sdn_family != AF_DECnet) + return -EINVAL; + + if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2)) + return -EINVAL; + + if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL) + return -EINVAL; + + if (saddr->sdn_flags & ~SDF_WILD) + return -EINVAL; + +#if 1 + if (!capable(CAP_NET_BIND_SERVICE) && (saddr->sdn_objnum || + (saddr->sdn_flags & SDF_WILD))) + return -EACCES; +#else + /* + * Maybe put the default actions in the default security ops for + * dn_prot_sock ? Would be nice if the capable call would go there + * too. + */ + if (security_dn_prot_sock(saddr) && + !capable(CAP_NET_BIND_SERVICE) || + saddr->sdn_objnum || (saddr->sdn_flags & SDF_WILD)) + return -EACCES; +#endif + + + if (!(saddr->sdn_flags & SDF_WILD)) { + if (dn_ntohs(saddr->sdn_nodeaddrl)) { + read_lock(&dev_base_lock); + for(dev = dev_base; dev; dev = dev->next) { + if (!dev->dn_ptr) + continue; + if (dn_dev_islocal(dev, dn_saddr2dn(saddr))) + break; + } + read_unlock(&dev_base_lock); + if (dev == NULL) + return -EADDRNOTAVAIL; + } + } + + rv = -EINVAL; + lock_sock(sk); + if (sock_flag(sk, SOCK_ZAPPED)) { + memcpy(&scp->addr, saddr, addr_len); + sock_reset_flag(sk, SOCK_ZAPPED); + + rv = dn_hash_sock(sk); + if (rv) + sock_set_flag(sk, SOCK_ZAPPED); + } + release_sock(sk); + + return rv; +} + + +static int dn_auto_bind(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct dn_scp *scp = DN_SK(sk); + int rv; + + sock_reset_flag(sk, SOCK_ZAPPED); + + scp->addr.sdn_flags = 0; + scp->addr.sdn_objnum = 0; + + /* + * This stuff is to keep compatibility with Eduardo's + * patch. I hope I can dispense with it shortly... + */ + if ((scp->accessdata.acc_accl != 0) && + (scp->accessdata.acc_accl <= 12)) { + + scp->addr.sdn_objnamel = dn_htons(scp->accessdata.acc_accl); + memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, dn_ntohs(scp->addr.sdn_objnamel)); + + scp->accessdata.acc_accl = 0; + memset(scp->accessdata.acc_acc, 0, 40); + } + /* End of compatibility stuff */ + + scp->addr.sdn_add.a_len = dn_htons(2); + rv = dn_dev_bind_default((dn_address *)scp->addr.sdn_add.a_addr); + if (rv == 0) { + rv = dn_hash_sock(sk); + if (rv) + sock_set_flag(sk, SOCK_ZAPPED); + } + + return rv; +} + +static int dn_confirm_accept(struct sock *sk, long *timeo, int allocation) +{ + struct dn_scp *scp = DN_SK(sk); + DEFINE_WAIT(wait); + int err; + + if (scp->state != DN_CR) + return -EINVAL; + + scp->state = DN_CC; + scp->segsize_loc = dst_metric(__sk_dst_get(sk), RTAX_ADVMSS); + dn_send_conn_conf(sk, allocation); + + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + for(;;) { + release_sock(sk); + if (scp->state == DN_CC) + *timeo = schedule_timeout(*timeo); + lock_sock(sk); + err = 0; + if (scp->state == DN_RUN) + break; + err = sock_error(sk); + if (err) + break; + err = sock_intr_errno(*timeo); + if (signal_pending(current)) + break; + err = -EAGAIN; + if (!*timeo) + break; + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + } + finish_wait(sk->sk_sleep, &wait); + if (err == 0) { + sk->sk_socket->state = SS_CONNECTED; + } else if (scp->state != DN_CC) { + sk->sk_socket->state = SS_UNCONNECTED; + } + return err; +} + +static int dn_wait_run(struct sock *sk, long *timeo) +{ + struct dn_scp *scp = DN_SK(sk); + DEFINE_WAIT(wait); + int err = 0; + + if (scp->state == DN_RUN) + goto out; + + if (!*timeo) + return -EALREADY; + + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + for(;;) { + release_sock(sk); + if (scp->state == DN_CI || scp->state == DN_CC) + *timeo = schedule_timeout(*timeo); + lock_sock(sk); + err = 0; + if (scp->state == DN_RUN) + break; + err = sock_error(sk); + if (err) + break; + err = sock_intr_errno(*timeo); + if (signal_pending(current)) + break; + err = -ETIMEDOUT; + if (!*timeo) + break; + prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + } + finish_wait(sk->sk_sleep, &wait); +out: + if (err == 0) { + sk->sk_socket->state = SS_CONNECTED; + } else if (scp->state != DN_CI && scp->state != DN_CC) { + sk->sk_socket->state = SS_UNCONNECTED; + } + return err; +} + +static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen, long *timeo, int flags) +{ + struct socket *sock = sk->sk_socket; + struct dn_scp *scp = DN_SK(sk); + int err = -EISCONN; + struct flowi fl; + + if (sock->state == SS_CONNECTED) + goto out; + + if (sock->state == SS_CONNECTING) { + err = 0; + if (scp->state == DN_RUN) { + sock->state = SS_CONNECTED; + goto out; + } + err = -ECONNREFUSED; + if (scp->state != DN_CI && scp->state != DN_CC) { + sock->state = SS_UNCONNECTED; + goto out; + } + return dn_wait_run(sk, timeo); + } + + err = -EINVAL; + if (scp->state != DN_O) + goto out; + + if (addr == NULL || addrlen != sizeof(struct sockaddr_dn)) + goto out; |