OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [decnet/] [af_decnet.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
 
 
/*
/*
 * DECnet       An implementation of the DECnet protocol suite for the LINUX
 * DECnet       An implementation of the DECnet protocol suite for the LINUX
 *              operating system.  DECnet is implemented using the  BSD Socket
 *              operating system.  DECnet is implemented using the  BSD Socket
 *              interface as the means of communication with the user level.
 *              interface as the means of communication with the user level.
 *
 *
 *              DECnet Socket Layer Interface
 *              DECnet Socket Layer Interface
 *
 *
 * Authors:     Eduardo Marcelo Serrat <emserrat@geocities.com>
 * Authors:     Eduardo Marcelo Serrat <emserrat@geocities.com>
 *              Patrick Caulfield <patrick@pandh.demon.co.uk>
 *              Patrick Caulfield <patrick@pandh.demon.co.uk>
 *
 *
 * Changes:
 * Changes:
 *        Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's
 *        Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's
 *                          version of the code. Original copyright preserved
 *                          version of the code. Original copyright preserved
 *                          below.
 *                          below.
 *        Steve Whitehouse: Some bug fixes, cleaning up some code to make it
 *        Steve Whitehouse: Some bug fixes, cleaning up some code to make it
 *                          compatible with my routing layer.
 *                          compatible with my routing layer.
 *        Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick
 *        Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick
 *                          Caulfield.
 *                          Caulfield.
 *        Steve Whitehouse: Further bug fixes, checking module code still works
 *        Steve Whitehouse: Further bug fixes, checking module code still works
 *                          with new routing layer.
 *                          with new routing layer.
 *        Steve Whitehouse: Additional set/get_sockopt() calls.
 *        Steve Whitehouse: Additional set/get_sockopt() calls.
 *        Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new
 *        Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new
 *                          code.
 *                          code.
 *        Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like
 *        Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like
 *                          way. Didn't manage it entirely, but its better.
 *                          way. Didn't manage it entirely, but its better.
 *        Steve Whitehouse: ditto for sendmsg().
 *        Steve Whitehouse: ditto for sendmsg().
 *        Steve Whitehouse: A selection of bug fixes to various things.
 *        Steve Whitehouse: A selection of bug fixes to various things.
 *        Steve Whitehouse: Added TIOCOUTQ ioctl.
 *        Steve Whitehouse: Added TIOCOUTQ ioctl.
 *        Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username.
 *        Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username.
 *        Steve Whitehouse: Fixes to connect() error returns.
 *        Steve Whitehouse: Fixes to connect() error returns.
 *       Patrick Caulfield: Fixes to delayed acceptance logic.
 *       Patrick Caulfield: Fixes to delayed acceptance logic.
 *         David S. Miller: New socket locking
 *         David S. Miller: New socket locking
 *        Steve Whitehouse: Socket list hashing/locking
 *        Steve Whitehouse: Socket list hashing/locking
 *         Arnaldo C. Melo: use capable, not suser
 *         Arnaldo C. Melo: use capable, not suser
 *        Steve Whitehouse: Removed unused code. Fix to use sk->allocation
 *        Steve Whitehouse: Removed unused code. Fix to use sk->allocation
 *                          when required.
 *                          when required.
 *       Patrick Caulfield: /proc/net/decnet now has object name/number
 *       Patrick Caulfield: /proc/net/decnet now has object name/number
 *        Steve Whitehouse: Fixed local port allocation, hashed sk list
 *        Steve Whitehouse: Fixed local port allocation, hashed sk list
 */
 */
 
 
 
 
/******************************************************************************
/******************************************************************************
    (c) 1995-1998 E.M. Serrat           emserrat@geocities.com
    (c) 1995-1998 E.M. Serrat           emserrat@geocities.com
 
 
    This program is free software; you can redistribute it and/or modify
    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
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    the Free Software Foundation; either version 2 of the License, or
    any later version.
    any later version.
 
 
    This program is distributed in the hope that it will be useful,
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    GNU General Public License for more details.
 
 
HISTORY:
HISTORY:
 
 
Version           Kernel     Date       Author/Comments
Version           Kernel     Date       Author/Comments
-------           ------     ----       ---------------
-------           ------     ----       ---------------
Version 0.0.1     2.0.30    01-dic-97   Eduardo Marcelo Serrat
Version 0.0.1     2.0.30    01-dic-97   Eduardo Marcelo Serrat
                                        (emserrat@geocities.com)
                                        (emserrat@geocities.com)
 
 
                                        First Development of DECnet Socket La-
                                        First Development of DECnet Socket La-
                                        yer for Linux. Only supports outgoing
                                        yer for Linux. Only supports outgoing
                                        connections.
                                        connections.
 
 
Version 0.0.2     2.1.105   20-jun-98   Patrick J. Caulfield
Version 0.0.2     2.1.105   20-jun-98   Patrick J. Caulfield
                                        (patrick@pandh.demon.co.uk)
                                        (patrick@pandh.demon.co.uk)
 
 
                                        Port to new kernel development version.
                                        Port to new kernel development version.
 
 
Version 0.0.3     2.1.106   25-jun-98   Eduardo Marcelo Serrat
Version 0.0.3     2.1.106   25-jun-98   Eduardo Marcelo Serrat
                                        (emserrat@geocities.com)
                                        (emserrat@geocities.com)
                                        _
                                        _
                                        Added support for incoming connections
                                        Added support for incoming connections
                                        so we can start developing server apps
                                        so we can start developing server apps
                                        on Linux.
                                        on Linux.
                                        -
                                        -
                                        Module Support
                                        Module Support
Version 0.0.4     2.1.109   21-jul-98   Eduardo Marcelo Serrat
Version 0.0.4     2.1.109   21-jul-98   Eduardo Marcelo Serrat
                                       (emserrat@geocities.com)
                                       (emserrat@geocities.com)
                                       _
                                       _
                                        Added support for X11R6.4. Now we can
                                        Added support for X11R6.4. Now we can
                                        use DECnet transport for X on Linux!!!
                                        use DECnet transport for X on Linux!!!
                                       -
                                       -
Version 0.0.5    2.1.110   01-aug-98   Eduardo Marcelo Serrat
Version 0.0.5    2.1.110   01-aug-98   Eduardo Marcelo Serrat
                                       (emserrat@geocities.com)
                                       (emserrat@geocities.com)
                                       Removed bugs on flow control
                                       Removed bugs on flow control
                                       Removed bugs on incoming accessdata
                                       Removed bugs on incoming accessdata
                                       order
                                       order
                                       -
                                       -
Version 0.0.6    2.1.110   07-aug-98   Eduardo Marcelo Serrat
Version 0.0.6    2.1.110   07-aug-98   Eduardo Marcelo Serrat
                                       dn_recvmsg fixes
                                       dn_recvmsg fixes
 
 
                                        Patrick J. Caulfield
                                        Patrick J. Caulfield
                                       dn_bind fixes
                                       dn_bind fixes
*******************************************************************************/
*******************************************************************************/
 
 
#include <linux/config.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <linux/inet.h>
#include <linux/inet.h>
#include <linux/route.h>
#include <linux/route.h>
#include <linux/netfilter.h>
#include <linux/netfilter.h>
#include <net/sock.h>
#include <net/sock.h>
#include <asm/segment.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/system.h>
#include <asm/ioctls.h>
#include <asm/ioctls.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/poll.h>
#include <net/neighbour.h>
#include <net/neighbour.h>
#include <net/dst.h>
#include <net/dst.h>
#include <net/dn.h>
#include <net/dn.h>
#include <net/dn_nsp.h>
#include <net/dn_nsp.h>
#include <net/dn_dev.h>
#include <net/dn_dev.h>
#include <net/dn_route.h>
#include <net/dn_route.h>
#include <net/dn_fib.h>
#include <net/dn_fib.h>
#include <net/dn_neigh.h>
#include <net/dn_neigh.h>
 
 
static void dn_keepalive(struct sock *sk);
static void dn_keepalive(struct sock *sk);
 
 
/*
/*
 * decnet_address is kept in network order, decnet_ether_address is kept
 * decnet_address is kept in network order, decnet_ether_address is kept
 * as a string of bytes.
 * as a string of bytes.
 */
 */
dn_address decnet_address = 0;
dn_address decnet_address = 0;
unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
 
 
#define DN_SK_HASH_SHIFT 8
#define DN_SK_HASH_SHIFT 8
#define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT)
#define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT)
#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)
#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)
 
 
static struct proto_ops dn_proto_ops;
static struct proto_ops dn_proto_ops;
rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
static struct sock *dn_sk_hash[DN_SK_HASH_SIZE];
static struct sock *dn_sk_hash[DN_SK_HASH_SIZE];
static struct sock *dn_wild_sk;
static struct sock *dn_wild_sk;
 
 
static int __dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
static int __dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
static int __dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);
static int __dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);
 
 
static struct sock **dn_find_list(struct sock *sk)
static struct sock **dn_find_list(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        if (scp->addr.sdn_flags & SDF_WILD)
        if (scp->addr.sdn_flags & SDF_WILD)
                return dn_wild_sk ? NULL : &dn_wild_sk;
                return dn_wild_sk ? NULL : &dn_wild_sk;
 
 
        return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK];
        return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK];
}
}
 
 
/*
/*
 * Valid ports are those greater than zero and not already in use.
 * Valid ports are those greater than zero and not already in use.
 */
 */
static int check_port(unsigned short port)
static int check_port(unsigned short port)
{
{
        struct sock *sk = dn_sk_hash[port & DN_SK_HASH_MASK];
        struct sock *sk = dn_sk_hash[port & DN_SK_HASH_MASK];
        if (port == 0)
        if (port == 0)
                return -1;
                return -1;
        while(sk) {
        while(sk) {
                struct dn_scp *scp = DN_SK(sk);
                struct dn_scp *scp = DN_SK(sk);
                if (scp->addrloc == port)
                if (scp->addrloc == port)
                        return -1;
                        return -1;
                sk = sk->next;
                sk = sk->next;
        }
        }
        return 0;
        return 0;
}
}
 
 
static unsigned short port_alloc(struct sock *sk)
static unsigned short port_alloc(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
static unsigned short port = 0x2000;
static unsigned short port = 0x2000;
        unsigned short i_port = port;
        unsigned short i_port = port;
 
 
        while(check_port(++port) != 0) {
        while(check_port(++port) != 0) {
                if (port == i_port)
                if (port == i_port)
                        return 0;
                        return 0;
        }
        }
 
 
        scp->addrloc = port;
        scp->addrloc = port;
 
 
        return 1;
        return 1;
}
}
 
 
/*
/*
 * Since this is only ever called from user
 * Since this is only ever called from user
 * level, we don't need a write_lock() version
 * level, we don't need a write_lock() version
 * of this.
 * of this.
 */
 */
static int dn_hash_sock(struct sock *sk)
static int dn_hash_sock(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        struct sock **skp;
        struct sock **skp;
        int rv = -EUSERS;
        int rv = -EUSERS;
 
 
        if (sk->next)
        if (sk->next)
                BUG();
                BUG();
        if (sk->pprev)
        if (sk->pprev)
                BUG();
                BUG();
 
 
        write_lock_bh(&dn_hash_lock);
        write_lock_bh(&dn_hash_lock);
 
 
        if (!scp->addrloc && !port_alloc(sk))
        if (!scp->addrloc && !port_alloc(sk))
                goto out;
                goto out;
 
 
        rv = -EADDRINUSE;
        rv = -EADDRINUSE;
        if ((skp = dn_find_list(sk)) == NULL)
        if ((skp = dn_find_list(sk)) == NULL)
                goto out;
                goto out;
 
 
        sk->next = *skp;
        sk->next = *skp;
        sk->pprev = skp;
        sk->pprev = skp;
        *skp = sk;
        *skp = sk;
        rv = 0;
        rv = 0;
out:
out:
        write_unlock_bh(&dn_hash_lock);
        write_unlock_bh(&dn_hash_lock);
        return rv;
        return rv;
}
}
 
 
static void dn_unhash_sock(struct sock *sk)
static void dn_unhash_sock(struct sock *sk)
{
{
        struct sock **skp = sk->pprev;
        struct sock **skp = sk->pprev;
 
 
        if (skp == NULL)
        if (skp == NULL)
                return;
                return;
 
 
        write_lock(&dn_hash_lock);
        write_lock(&dn_hash_lock);
        while(*skp != sk)
        while(*skp != sk)
                skp = &((*skp)->next);
                skp = &((*skp)->next);
        *skp = sk->next;
        *skp = sk->next;
        write_unlock(&dn_hash_lock);
        write_unlock(&dn_hash_lock);
 
 
        sk->next = NULL;
        sk->next = NULL;
        sk->pprev = NULL;
        sk->pprev = NULL;
}
}
 
 
static void dn_unhash_sock_bh(struct sock *sk)
static void dn_unhash_sock_bh(struct sock *sk)
{
{
        struct sock **skp = sk->pprev;
        struct sock **skp = sk->pprev;
 
 
        if (skp == NULL)
        if (skp == NULL)
                return;
                return;
 
 
        write_lock_bh(&dn_hash_lock);
        write_lock_bh(&dn_hash_lock);
        while(*skp != sk)
        while(*skp != sk)
                skp = &((*skp)->next);
                skp = &((*skp)->next);
        *skp = sk->next;
        *skp = sk->next;
        write_unlock_bh(&dn_hash_lock);
        write_unlock_bh(&dn_hash_lock);
 
 
        sk->next = NULL;
        sk->next = NULL;
        sk->pprev = NULL;
        sk->pprev = NULL;
}
}
 
 
struct sock **listen_hash(struct sockaddr_dn *addr)
struct sock **listen_hash(struct sockaddr_dn *addr)
{
{
        int i;
        int i;
        unsigned hash = addr->sdn_objnum;
        unsigned hash = addr->sdn_objnum;
 
 
        if (hash == 0) {
        if (hash == 0) {
                hash = addr->sdn_objnamel;
                hash = addr->sdn_objnamel;
                for(i = 0; i < addr->sdn_objnamel; i++) {
                for(i = 0; i < addr->sdn_objnamel; i++) {
                        hash ^= addr->sdn_objname[i];
                        hash ^= addr->sdn_objname[i];
                        hash ^= (hash << 3);
                        hash ^= (hash << 3);
                }
                }
        }
        }
 
 
        return &dn_sk_hash[hash & DN_SK_HASH_MASK];
        return &dn_sk_hash[hash & DN_SK_HASH_MASK];
}
}
 
 
/*
/*
 * Called to transform a socket from bound (i.e. with a local address)
 * 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
 * into a listening socket (doesn't need a local port number) and rehashes
 * based upon the object name/number.
 * based upon the object name/number.
 */
 */
static void dn_rehash_sock(struct sock *sk)
static void dn_rehash_sock(struct sock *sk)
{
{
        struct sock **skp = sk->pprev;
        struct sock **skp = sk->pprev;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        if (scp->addr.sdn_flags & SDF_WILD)
        if (scp->addr.sdn_flags & SDF_WILD)
                return;
                return;
 
 
        write_lock_bh(&dn_hash_lock);
        write_lock_bh(&dn_hash_lock);
        while(*skp != sk)
        while(*skp != sk)
                skp = &((*skp)->next);
                skp = &((*skp)->next);
        *skp = sk->next;
        *skp = sk->next;
 
 
        DN_SK(sk)->addrloc = 0;
        DN_SK(sk)->addrloc = 0;
        skp = listen_hash(&DN_SK(sk)->addr);
        skp = listen_hash(&DN_SK(sk)->addr);
 
 
        sk->next = *skp;
        sk->next = *skp;
        sk->pprev = skp;
        sk->pprev = skp;
        *skp = sk;
        *skp = sk;
        write_unlock_bh(&dn_hash_lock);
        write_unlock_bh(&dn_hash_lock);
}
}
 
 
int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
{
{
        int len = 2;
        int len = 2;
 
 
        *buf++ = type;
        *buf++ = type;
 
 
        switch(type) {
        switch(type) {
                case 0:
                case 0:
                        *buf++ = sdn->sdn_objnum;
                        *buf++ = sdn->sdn_objnum;
                        break;
                        break;
                case 1:
                case 1:
                        *buf++ = 0;
                        *buf++ = 0;
                        *buf++ = dn_ntohs(sdn->sdn_objnamel);
                        *buf++ = dn_ntohs(sdn->sdn_objnamel);
                        memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
                        memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
                        len = 3 + dn_ntohs(sdn->sdn_objnamel);
                        len = 3 + dn_ntohs(sdn->sdn_objnamel);
                        break;
                        break;
                case 2:
                case 2:
                        memset(buf, 0, 5);
                        memset(buf, 0, 5);
                        buf += 5;
                        buf += 5;
                        *buf++ = dn_ntohs(sdn->sdn_objnamel);
                        *buf++ = dn_ntohs(sdn->sdn_objnamel);
                        memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
                        memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
                        len = 7 + dn_ntohs(sdn->sdn_objnamel);
                        len = 7 + dn_ntohs(sdn->sdn_objnamel);
                        break;
                        break;
        }
        }
 
 
        return len;
        return len;
}
}
 
 
/*
/*
 * On reception of usernames, we handle types 1 and 0 for destination
 * 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
 * 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
 * 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
 * is never used as I've no idea what its purpose might be or what its
 * format is.
 * format is.
 */
 */
int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt)
int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt)
{
{
        unsigned char type;
        unsigned char type;
        int size = len;
        int size = len;
        int namel = 12;
        int namel = 12;
 
 
        sdn->sdn_objnum = 0;
        sdn->sdn_objnum = 0;
        sdn->sdn_objnamel = dn_htons(0);
        sdn->sdn_objnamel = dn_htons(0);
        memset(sdn->sdn_objname, 0, DN_MAXOBJL);
        memset(sdn->sdn_objname, 0, DN_MAXOBJL);
 
 
        if (len < 2)
        if (len < 2)
                return -1;
                return -1;
 
 
        len -= 2;
        len -= 2;
        *fmt = *data++;
        *fmt = *data++;
        type = *data++;
        type = *data++;
 
 
        switch(*fmt) {
        switch(*fmt) {
                case 0:
                case 0:
                        sdn->sdn_objnum = type;
                        sdn->sdn_objnum = type;
                        return 2;
                        return 2;
                case 1:
                case 1:
                        namel = 16;
                        namel = 16;
                        break;
                        break;
                case 2:
                case 2:
                        len  -= 4;
                        len  -= 4;
                        data += 4;
                        data += 4;
                        break;
                        break;
                case 4:
                case 4:
                        len  -= 8;
                        len  -= 8;
                        data += 8;
                        data += 8;
                        break;
                        break;
                default:
                default:
                        return -1;
                        return -1;
        }
        }
 
 
        len -= 1;
        len -= 1;
 
 
        if (len < 0)
        if (len < 0)
                return -1;
                return -1;
 
 
        sdn->sdn_objnamel = dn_htons(*data++);
        sdn->sdn_objnamel = dn_htons(*data++);
        len -= dn_ntohs(sdn->sdn_objnamel);
        len -= dn_ntohs(sdn->sdn_objnamel);
 
 
        if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel))
        if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel))
                return -1;
                return -1;
 
 
        memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel));
        memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel));
 
 
        return size - len;
        return size - len;
}
}
 
 
struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
{
{
        struct sock **skp = listen_hash(addr);
        struct sock **skp = listen_hash(addr);
        struct sock *sk;
        struct sock *sk;
 
 
        read_lock(&dn_hash_lock);
        read_lock(&dn_hash_lock);
        for(sk = *skp; sk != NULL; sk = sk->next) {
        for(sk = *skp; sk != NULL; sk = sk->next) {
                struct dn_scp *scp = DN_SK(sk);
                struct dn_scp *scp = DN_SK(sk);
                if (sk->state != TCP_LISTEN)
                if (sk->state != TCP_LISTEN)
                        continue;
                        continue;
                if (scp->addr.sdn_objnum) {
                if (scp->addr.sdn_objnum) {
                        if (scp->addr.sdn_objnum != addr->sdn_objnum)
                        if (scp->addr.sdn_objnum != addr->sdn_objnum)
                                continue;
                                continue;
                } else {
                } else {
                        if (addr->sdn_objnum)
                        if (addr->sdn_objnum)
                                continue;
                                continue;
                        if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
                        if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
                                continue;
                                continue;
                        if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0)
                        if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0)
                                continue;
                                continue;
                }
                }
                sock_hold(sk);
                sock_hold(sk);
                read_unlock(&dn_hash_lock);
                read_unlock(&dn_hash_lock);
                return sk;
                return sk;
        }
        }
 
 
        if (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN))
        if (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN))
                sock_hold((sk = dn_wild_sk));
                sock_hold((sk = dn_wild_sk));
 
 
        read_unlock(&dn_hash_lock);
        read_unlock(&dn_hash_lock);
        return sk;
        return sk;
}
}
 
 
struct sock *dn_find_by_skb(struct sk_buff *skb)
struct sock *dn_find_by_skb(struct sk_buff *skb)
{
{
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
        struct sock *sk;
        struct sock *sk;
        struct dn_scp *scp;
        struct dn_scp *scp;
 
 
        read_lock(&dn_hash_lock);
        read_lock(&dn_hash_lock);
        sk = dn_sk_hash[cb->dst_port & DN_SK_HASH_MASK];
        sk = dn_sk_hash[cb->dst_port & DN_SK_HASH_MASK];
        for (; sk != NULL; sk = sk->next) {
        for (; sk != NULL; sk = sk->next) {
                scp = DN_SK(sk);
                scp = DN_SK(sk);
                if (cb->src != dn_saddr2dn(&scp->peer))
                if (cb->src != dn_saddr2dn(&scp->peer))
                        continue;
                        continue;
                if (cb->dst_port != scp->addrloc)
                if (cb->dst_port != scp->addrloc)
                        continue;
                        continue;
                if (scp->addrrem && (cb->src_port != scp->addrrem))
                if (scp->addrrem && (cb->src_port != scp->addrrem))
                        continue;
                        continue;
                break;
                break;
        }
        }
 
 
        if (sk)
        if (sk)
                sock_hold(sk);
                sock_hold(sk);
 
 
        read_unlock(&dn_hash_lock);
        read_unlock(&dn_hash_lock);
 
 
        return sk;
        return sk;
}
}
 
 
 
 
 
 
static void dn_destruct(struct sock *sk)
static void dn_destruct(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        skb_queue_purge(&scp->data_xmit_queue);
        skb_queue_purge(&scp->data_xmit_queue);
        skb_queue_purge(&scp->other_xmit_queue);
        skb_queue_purge(&scp->other_xmit_queue);
        skb_queue_purge(&scp->other_receive_queue);
        skb_queue_purge(&scp->other_receive_queue);
 
 
        dst_release(xchg(&sk->dst_cache, NULL));
        dst_release(xchg(&sk->dst_cache, NULL));
 
 
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
struct sock *dn_alloc_sock(struct socket *sock, int gfp)
struct sock *dn_alloc_sock(struct socket *sock, int gfp)
{
{
        struct sock *sk;
        struct sock *sk;
        struct dn_scp *scp;
        struct dn_scp *scp;
 
 
        if  ((sk = sk_alloc(PF_DECnet, gfp, 1)) == NULL)
        if  ((sk = sk_alloc(PF_DECnet, gfp, 1)) == NULL)
                goto no_sock;
                goto no_sock;
 
 
        if (sock) {
        if (sock) {
                        sock->ops = &dn_proto_ops;
                        sock->ops = &dn_proto_ops;
        }
        }
        sock_init_data(sock,sk);
        sock_init_data(sock,sk);
        scp = DN_SK(sk);
        scp = DN_SK(sk);
 
 
        sk->backlog_rcv = dn_nsp_backlog_rcv;
        sk->backlog_rcv = dn_nsp_backlog_rcv;
        sk->destruct    = dn_destruct;
        sk->destruct    = dn_destruct;
        sk->no_check    = 1;
        sk->no_check    = 1;
        sk->family      = PF_DECnet;
        sk->family      = PF_DECnet;
        sk->protocol    = 0;
        sk->protocol    = 0;
        sk->allocation  = gfp;
        sk->allocation  = gfp;
 
 
        /* Initialization of DECnet Session Control Port                */
        /* Initialization of DECnet Session Control Port                */
        scp->state      = DN_O;         /* Open                 */
        scp->state      = DN_O;         /* Open                 */
        scp->numdat     = 1;            /* Next data seg to tx  */
        scp->numdat     = 1;            /* Next data seg to tx  */
        scp->numoth     = 1;            /* Next oth data to tx  */
        scp->numoth     = 1;            /* Next oth data to tx  */
        scp->ackxmt_dat = 0;             /* Last data seg ack'ed */
        scp->ackxmt_dat = 0;             /* Last data seg ack'ed */
        scp->ackxmt_oth = 0;             /* Last oth data ack'ed */
        scp->ackxmt_oth = 0;             /* Last oth data ack'ed */
        scp->ackrcv_dat = 0;             /* Highest data ack recv*/
        scp->ackrcv_dat = 0;             /* Highest data ack recv*/
        scp->ackrcv_oth = 0;             /* Last oth data ack rec*/
        scp->ackrcv_oth = 0;             /* Last oth data ack rec*/
        scp->flowrem_sw = DN_SEND;
        scp->flowrem_sw = DN_SEND;
        scp->flowloc_sw = DN_SEND;
        scp->flowloc_sw = DN_SEND;
        scp->flowrem_dat = 0;
        scp->flowrem_dat = 0;
        scp->flowrem_oth = 1;
        scp->flowrem_oth = 1;
        scp->flowloc_dat = 0;
        scp->flowloc_dat = 0;
        scp->flowloc_oth = 1;
        scp->flowloc_oth = 1;
        scp->services_rem = 0;
        scp->services_rem = 0;
        scp->services_loc = 1 | NSP_FC_NONE;
        scp->services_loc = 1 | NSP_FC_NONE;
        scp->info_rem = 0;
        scp->info_rem = 0;
        scp->info_loc = 0x03; /* NSP version 4.1 */
        scp->info_loc = 0x03; /* NSP version 4.1 */
        scp->segsize_rem = 230; /* Default: Updated by remote segsize */
        scp->segsize_rem = 230; /* Default: Updated by remote segsize */
        scp->segsize_loc = 1450; /* Best guess for ethernet */
        scp->segsize_loc = 1450; /* Best guess for ethernet */
        scp->nonagle = 0;
        scp->nonagle = 0;
        scp->multi_ireq = 1;
        scp->multi_ireq = 1;
        scp->accept_mode = ACC_IMMED;
        scp->accept_mode = ACC_IMMED;
        scp->addr.sdn_family    = AF_DECnet;
        scp->addr.sdn_family    = AF_DECnet;
        scp->peer.sdn_family    = AF_DECnet;
        scp->peer.sdn_family    = AF_DECnet;
        scp->accessdata.acc_accl = 5;
        scp->accessdata.acc_accl = 5;
        memcpy(scp->accessdata.acc_acc, "LINUX", 5);
        memcpy(scp->accessdata.acc_acc, "LINUX", 5);
 
 
        scp->max_window   = NSP_MAX_WINDOW;
        scp->max_window   = NSP_MAX_WINDOW;
        scp->snd_window   = NSP_MIN_WINDOW;
        scp->snd_window   = NSP_MIN_WINDOW;
        scp->nsp_srtt     = NSP_INITIAL_SRTT;
        scp->nsp_srtt     = NSP_INITIAL_SRTT;
        scp->nsp_rttvar   = NSP_INITIAL_RTTVAR;
        scp->nsp_rttvar   = NSP_INITIAL_RTTVAR;
        scp->nsp_rxtshift = 0;
        scp->nsp_rxtshift = 0;
 
 
        skb_queue_head_init(&scp->data_xmit_queue);
        skb_queue_head_init(&scp->data_xmit_queue);
        skb_queue_head_init(&scp->other_xmit_queue);
        skb_queue_head_init(&scp->other_xmit_queue);
        skb_queue_head_init(&scp->other_receive_queue);
        skb_queue_head_init(&scp->other_receive_queue);
 
 
        scp->persist = 0;
        scp->persist = 0;
        scp->persist_fxn = NULL;
        scp->persist_fxn = NULL;
        scp->keepalive = 10 * HZ;
        scp->keepalive = 10 * HZ;
        scp->keepalive_fxn = dn_keepalive;
        scp->keepalive_fxn = dn_keepalive;
 
 
        init_timer(&scp->delack_timer);
        init_timer(&scp->delack_timer);
        scp->delack_pending = 0;
        scp->delack_pending = 0;
        scp->delack_fxn = dn_nsp_delayed_ack;
        scp->delack_fxn = dn_nsp_delayed_ack;
 
 
        dn_start_slow_timer(sk);
        dn_start_slow_timer(sk);
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
 
 
        return sk;
        return sk;
no_sock:
no_sock:
        return NULL;
        return NULL;
}
}
 
 
/*
/*
 * Keepalive timer.
 * Keepalive timer.
 * FIXME: Should respond to SO_KEEPALIVE etc.
 * FIXME: Should respond to SO_KEEPALIVE etc.
 */
 */
static void dn_keepalive(struct sock *sk)
static void dn_keepalive(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        /*
        /*
         * By checking the other_data transmit queue is empty
         * By checking the other_data transmit queue is empty
         * we are double checking that we are not sending too
         * we are double checking that we are not sending too
         * many of these keepalive frames.
         * many of these keepalive frames.
         */
         */
        if (skb_queue_len(&scp->other_xmit_queue) == 0)
        if (skb_queue_len(&scp->other_xmit_queue) == 0)
                dn_nsp_send_link(sk, DN_NOCHANGE, 0);
                dn_nsp_send_link(sk, DN_NOCHANGE, 0);
}
}
 
 
 
 
/*
/*
 * Timer for shutdown/destroyed sockets.
 * Timer for shutdown/destroyed sockets.
 * When socket is dead & no packets have been sent for a
 * When socket is dead & no packets have been sent for a
 * certain amount of time, they are removed by this
 * certain amount of time, they are removed by this
 * routine. Also takes care of sending out DI & DC
 * routine. Also takes care of sending out DI & DC
 * frames at correct times.
 * frames at correct times.
 */
 */
int dn_destroy_timer(struct sock *sk)
int dn_destroy_timer(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        scp->persist = dn_nsp_persist(sk);
        scp->persist = dn_nsp_persist(sk);
 
 
        switch(scp->state) {
        switch(scp->state) {
                case DN_DI:
                case DN_DI:
                        dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
                        dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
                        if (scp->nsp_rxtshift >= decnet_di_count)
                        if (scp->nsp_rxtshift >= decnet_di_count)
                                scp->state = DN_CN;
                                scp->state = DN_CN;
                        return 0;
                        return 0;
 
 
                case DN_DR:
                case DN_DR:
                        dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
                        dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
                        if (scp->nsp_rxtshift >= decnet_dr_count)
                        if (scp->nsp_rxtshift >= decnet_dr_count)
                                scp->state = DN_DRC;
                                scp->state = DN_DRC;
                        return 0;
                        return 0;
 
 
                case DN_DN:
                case DN_DN:
                        if (scp->nsp_rxtshift < decnet_dn_count) {
                        if (scp->nsp_rxtshift < decnet_dn_count) {
                                /* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */
                                /* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */
                                dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
                                dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
                                return 0;
                                return 0;
                        }
                        }
        }
        }
 
 
        scp->persist = (HZ * decnet_time_wait);
        scp->persist = (HZ * decnet_time_wait);
 
 
        if (sk->socket)
        if (sk->socket)
                return 0;
                return 0;
 
 
        dn_stop_fast_timer(sk); /* unlikely, but possible that this is runninng */
        dn_stop_fast_timer(sk); /* unlikely, but possible that this is runninng */
        if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) {
        if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) {
                dn_unhash_sock(sk);
                dn_unhash_sock(sk);
                sock_put(sk);
                sock_put(sk);
                return 1;
                return 1;
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
static void dn_destroy_sock(struct sock *sk)
static void dn_destroy_sock(struct sock *sk)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        scp->nsp_rxtshift = 0; /* reset back off */
        scp->nsp_rxtshift = 0; /* reset back off */
 
 
        if (sk->socket) {
        if (sk->socket) {
                if (sk->socket->state != SS_UNCONNECTED)
                if (sk->socket->state != SS_UNCONNECTED)
                        sk->socket->state = SS_DISCONNECTING;
                        sk->socket->state = SS_DISCONNECTING;
        }
        }
 
 
        sk->state = TCP_CLOSE;
        sk->state = TCP_CLOSE;
 
 
        switch(scp->state) {
        switch(scp->state) {
                case DN_DN:
                case DN_DN:
                        dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, sk->allocation);
                        dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, sk->allocation);
                        scp->persist_fxn = dn_destroy_timer;
                        scp->persist_fxn = dn_destroy_timer;
                        scp->persist = dn_nsp_persist(sk);
                        scp->persist = dn_nsp_persist(sk);
                        break;
                        break;
                case DN_CR:
                case DN_CR:
                        scp->state = DN_DR;
                        scp->state = DN_DR;
                        goto disc_reject;
                        goto disc_reject;
                case DN_RUN:
                case DN_RUN:
                        scp->state = DN_DI;
                        scp->state = DN_DI;
                case DN_DI:
                case DN_DI:
                case DN_DR:
                case DN_DR:
disc_reject:
disc_reject:
                        dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->allocation);
                        dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->allocation);
                case DN_NC:
                case DN_NC:
                case DN_NR:
                case DN_NR:
                case DN_RJ:
                case DN_RJ:
                case DN_DIC:
                case DN_DIC:
                case DN_CN:
                case DN_CN:
                case DN_DRC:
                case DN_DRC:
                case DN_CI:
                case DN_CI:
                case DN_CD:
                case DN_CD:
                        scp->persist_fxn = dn_destroy_timer;
                        scp->persist_fxn = dn_destroy_timer;
                        scp->persist = dn_nsp_persist(sk);
                        scp->persist = dn_nsp_persist(sk);
                        break;
                        break;
                default:
                default:
                        printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
                        printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
                case DN_O:
                case DN_O:
                        dn_stop_fast_timer(sk);
                        dn_stop_fast_timer(sk);
                        dn_stop_slow_timer(sk);
                        dn_stop_slow_timer(sk);
 
 
                        dn_unhash_sock_bh(sk);
                        dn_unhash_sock_bh(sk);
                        sock_put(sk);
                        sock_put(sk);
 
 
                        break;
                        break;
        }
        }
}
}
 
 
char *dn_addr2asc(dn_address addr, char *buf)
char *dn_addr2asc(dn_address addr, char *buf)
{
{
        unsigned short node, area;
        unsigned short node, area;
 
 
        node = addr & 0x03ff;
        node = addr & 0x03ff;
        area = addr >> 10;
        area = addr >> 10;
        sprintf(buf, "%hd.%hd", area, node);
        sprintf(buf, "%hd.%hd", area, node);
 
 
        return buf;
        return buf;
}
}
 
 
 
 
static char *dn_state2asc(unsigned char state)
static char *dn_state2asc(unsigned char state)
{
{
        switch(state) {
        switch(state) {
                case DN_O:
                case DN_O:
                        return "OPEN";
                        return "OPEN";
                case DN_CR:
                case DN_CR:
                        return "  CR";
                        return "  CR";
                case DN_DR:
                case DN_DR:
                        return "  DR";
                        return "  DR";
                case DN_DRC:
                case DN_DRC:
                        return " DRC";
                        return " DRC";
                case DN_CC:
                case DN_CC:
                        return "  CC";
                        return "  CC";
                case DN_CI:
                case DN_CI:
                        return "  CI";
                        return "  CI";
                case DN_NR:
                case DN_NR:
                        return "  NR";
                        return "  NR";
                case DN_NC:
                case DN_NC:
                        return "  NC";
                        return "  NC";
                case DN_CD:
                case DN_CD:
                        return "  CD";
                        return "  CD";
                case DN_RJ:
                case DN_RJ:
                        return "  RJ";
                        return "  RJ";
                case DN_RUN:
                case DN_RUN:
                        return " RUN";
                        return " RUN";
                case DN_DI:
                case DN_DI:
                        return "  DI";
                        return "  DI";
                case DN_DIC:
                case DN_DIC:
                        return " DIC";
                        return " DIC";
                case DN_DN:
                case DN_DN:
                        return "  DN";
                        return "  DN";
                case DN_CL:
                case DN_CL:
                        return "  CL";
                        return "  CL";
                case DN_CN:
                case DN_CN:
                        return "  CN";
                        return "  CN";
        }
        }
 
 
        return "????";
        return "????";
}
}
 
 
static int dn_create(struct socket *sock, int protocol)
static int dn_create(struct socket *sock, int protocol)
{
{
        struct sock *sk;
        struct sock *sk;
 
 
        switch(sock->type) {
        switch(sock->type) {
                case SOCK_SEQPACKET:
                case SOCK_SEQPACKET:
                        if (protocol != DNPROTO_NSP)
                        if (protocol != DNPROTO_NSP)
                                return -EPROTONOSUPPORT;
                                return -EPROTONOSUPPORT;
                        break;
                        break;
                case SOCK_STREAM:
                case SOCK_STREAM:
                        break;
                        break;
                default:
                default:
                        return -ESOCKTNOSUPPORT;
                        return -ESOCKTNOSUPPORT;
        }
        }
 
 
 
 
        if ((sk = dn_alloc_sock(sock, GFP_KERNEL)) == NULL)
        if ((sk = dn_alloc_sock(sock, GFP_KERNEL)) == NULL)
                return -ENOBUFS;
                return -ENOBUFS;
 
 
        sk->protocol = protocol;
        sk->protocol = protocol;
 
 
        return 0;
        return 0;
}
}
 
 
 
 
static int
static int
dn_release(struct socket *sock)
dn_release(struct socket *sock)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
 
 
        if (sk) {
        if (sk) {
                sock_orphan(sk);
                sock_orphan(sk);
                sock_hold(sk);
                sock_hold(sk);
                lock_sock(sk);
                lock_sock(sk);
                dn_destroy_sock(sk);
                dn_destroy_sock(sk);
                release_sock(sk);
                release_sock(sk);
                sock_put(sk);
                sock_put(sk);
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
        struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
        struct net_device *dev;
        struct net_device *dev;
        int rv;
        int rv;
 
 
        if (sk->zapped == 0)
        if (sk->zapped == 0)
                return -EINVAL;
                return -EINVAL;
 
 
        if (addr_len != sizeof(struct sockaddr_dn))
        if (addr_len != sizeof(struct sockaddr_dn))
                return -EINVAL;
                return -EINVAL;
 
 
        if (saddr->sdn_family != AF_DECnet)
        if (saddr->sdn_family != AF_DECnet)
                return -EINVAL;
                return -EINVAL;
 
 
        if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2))
        if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2))
                return -EINVAL;
                return -EINVAL;
 
 
        if (saddr->sdn_objnum && !capable(CAP_NET_BIND_SERVICE))
        if (saddr->sdn_objnum && !capable(CAP_NET_BIND_SERVICE))
                return -EPERM;
                return -EPERM;
 
 
        if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL)
        if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL)
                return -EINVAL;
                return -EINVAL;
 
 
        if (saddr->sdn_flags & ~SDF_WILD)
        if (saddr->sdn_flags & ~SDF_WILD)
                return -EINVAL;
                return -EINVAL;
 
 
        if (saddr->sdn_flags & SDF_WILD) {
        if (saddr->sdn_flags & SDF_WILD) {
                if (!capable(CAP_NET_BIND_SERVICE))
                if (!capable(CAP_NET_BIND_SERVICE))
                        return -EPERM;
                        return -EPERM;
        } else {
        } else {
                if (dn_ntohs(saddr->sdn_nodeaddrl)) {
                if (dn_ntohs(saddr->sdn_nodeaddrl)) {
                        read_lock(&dev_base_lock);
                        read_lock(&dev_base_lock);
                        for(dev = dev_base; dev; dev = dev->next) {
                        for(dev = dev_base; dev; dev = dev->next) {
                                if (!dev->dn_ptr)
                                if (!dev->dn_ptr)
                                        continue;
                                        continue;
                                if (dn_dev_islocal(dev, dn_saddr2dn(saddr)))
                                if (dn_dev_islocal(dev, dn_saddr2dn(saddr)))
                                        break;
                                        break;
                        }
                        }
                        read_unlock(&dev_base_lock);
                        read_unlock(&dev_base_lock);
                        if (dev == NULL)
                        if (dev == NULL)
                                return -EADDRNOTAVAIL;
                                return -EADDRNOTAVAIL;
                }
                }
        }
        }
 
 
 
 
        memcpy(&scp->addr, saddr, addr_len);
        memcpy(&scp->addr, saddr, addr_len);
        sk->zapped = 0;
        sk->zapped = 0;
 
 
        if ((rv = dn_hash_sock(sk)) != 0)
        if ((rv = dn_hash_sock(sk)) != 0)
                sk->zapped = 1;
                sk->zapped = 1;
 
 
        return rv;
        return rv;
}
}
 
 
 
 
static int dn_auto_bind(struct socket *sock)
static int dn_auto_bind(struct socket *sock)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        sk->zapped = 0;
        sk->zapped = 0;
 
 
        scp->addr.sdn_flags  = 0;
        scp->addr.sdn_flags  = 0;
        scp->addr.sdn_objnum = 0;
        scp->addr.sdn_objnum = 0;
 
 
        /*
        /*
         * This stuff is to keep compatibility with Eduardo's
         * This stuff is to keep compatibility with Eduardo's
         * patch. I hope I can dispense with it shortly...
         * patch. I hope I can dispense with it shortly...
         */
         */
        if ((scp->accessdata.acc_accl != 0) &&
        if ((scp->accessdata.acc_accl != 0) &&
                (scp->accessdata.acc_accl <= 12)) {
                (scp->accessdata.acc_accl <= 12)) {
 
 
                scp->addr.sdn_objnamel = dn_htons(scp->accessdata.acc_accl);
                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));
                memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, dn_ntohs(scp->addr.sdn_objnamel));
 
 
                scp->accessdata.acc_accl = 0;
                scp->accessdata.acc_accl = 0;
                memset(scp->accessdata.acc_acc, 0, 40);
                memset(scp->accessdata.acc_acc, 0, 40);
        }
        }
 
 
        scp->addr.sdn_add.a_len = dn_htons(2);
        scp->addr.sdn_add.a_len = dn_htons(2);
        *(dn_address *)scp->addr.sdn_add.a_addr = decnet_address;
        *(dn_address *)scp->addr.sdn_add.a_addr = decnet_address;
 
 
        dn_hash_sock(sk);
        dn_hash_sock(sk);
 
 
        return 0;
        return 0;
}
}
 
 
 
 
static int dn_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
static int dn_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{
{
        struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
        struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        int err = -EISCONN;
        int err = -EISCONN;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (sock->state == SS_CONNECTED)
        if (sock->state == SS_CONNECTED)
                goto out;
                goto out;
 
 
        if (sock->state == SS_CONNECTING) {
        if (sock->state == SS_CONNECTING) {
                err = 0;
                err = 0;
                if (sk->state == TCP_ESTABLISHED)
                if (sk->state == TCP_ESTABLISHED)
                        goto out;
                        goto out;
 
 
                err = -ECONNREFUSED;
                err = -ECONNREFUSED;
                if (sk->state == TCP_CLOSE)
                if (sk->state == TCP_CLOSE)
                        goto out;
                        goto out;
        }
        }
 
 
        err = -EINVAL;
        err = -EINVAL;
        if (DN_SK(sk)->state != DN_O)
        if (DN_SK(sk)->state != DN_O)
                goto out;
                goto out;
 
 
        if (addr_len != sizeof(struct sockaddr_dn))
        if (addr_len != sizeof(struct sockaddr_dn))
                goto out;
                goto out;
 
 
        if (addr->sdn_family != AF_DECnet)
        if (addr->sdn_family != AF_DECnet)
                goto out;
                goto out;
 
 
        if (addr->sdn_flags & SDF_WILD)
        if (addr->sdn_flags & SDF_WILD)
                goto out;
                goto out;
 
 
        err = -EADDRNOTAVAIL;
        err = -EADDRNOTAVAIL;
        if (sk->zapped && (err = dn_auto_bind(sock)))
        if (sk->zapped && (err = dn_auto_bind(sock)))
                goto out;
                goto out;
 
 
        memcpy(&scp->peer, addr, addr_len);
        memcpy(&scp->peer, addr, addr_len);
 
 
        err = -EHOSTUNREACH;
        err = -EHOSTUNREACH;
        if (dn_route_output(&sk->dst_cache, dn_saddr2dn(&scp->peer), dn_saddr2dn(&scp->addr), 0) < 0)
        if (dn_route_output(&sk->dst_cache, dn_saddr2dn(&scp->peer), dn_saddr2dn(&scp->addr), 0) < 0)
                goto out;
                goto out;
 
 
        sk->state   = TCP_SYN_SENT;
        sk->state   = TCP_SYN_SENT;
        sock->state = SS_CONNECTING;
        sock->state = SS_CONNECTING;
        DN_SK(sk)->state = DN_CI;
        DN_SK(sk)->state = DN_CI;
 
 
        dn_nsp_send_conninit(sk, NSP_CI);
        dn_nsp_send_conninit(sk, NSP_CI);
 
 
        err = -EINPROGRESS;
        err = -EINPROGRESS;
        if ((sk->state == TCP_SYN_SENT) && (flags & O_NONBLOCK))
        if ((sk->state == TCP_SYN_SENT) && (flags & O_NONBLOCK))
                goto out;
                goto out;
 
 
        while(sk->state == TCP_SYN_SENT) {
        while(sk->state == TCP_SYN_SENT) {
 
 
                err = -ERESTARTSYS;
                err = -ERESTARTSYS;
                if (signal_pending(current))
                if (signal_pending(current))
                        goto out;
                        goto out;
 
 
                if ((err = sock_error(sk)) != 0) {
                if ((err = sock_error(sk)) != 0) {
                        sock->state = SS_UNCONNECTED;
                        sock->state = SS_UNCONNECTED;
                        goto out;
                        goto out;
                }
                }
 
 
                SOCK_SLEEP_PRE(sk);
                SOCK_SLEEP_PRE(sk);
 
 
                if (sk->state == TCP_SYN_SENT)
                if (sk->state == TCP_SYN_SENT)
                        schedule();
                        schedule();
 
 
                SOCK_SLEEP_POST(sk);
                SOCK_SLEEP_POST(sk);
        }
        }
 
 
        if (sk->state != TCP_ESTABLISHED) {
        if (sk->state != TCP_ESTABLISHED) {
                sock->state = SS_UNCONNECTED;
                sock->state = SS_UNCONNECTED;
                err = sock_error(sk);
                err = sock_error(sk);
                goto out;
                goto out;
        }
        }
 
 
        err = 0;
        err = 0;
        sock->state = SS_CONNECTED;
        sock->state = SS_CONNECTED;
out:
out:
        release_sock(sk);
        release_sock(sk);
 
 
        return err;
        return err;
}
}
 
 
static void dn_access_copy(struct sk_buff *skb, struct accessdata_dn *acc)
static void dn_access_copy(struct sk_buff *skb, struct accessdata_dn *acc)
{
{
        unsigned char *ptr = skb->data;
        unsigned char *ptr = skb->data;
 
 
        acc->acc_userl = *ptr++;
        acc->acc_userl = *ptr++;
        memcpy(&acc->acc_user, ptr, acc->acc_userl);
        memcpy(&acc->acc_user, ptr, acc->acc_userl);
        ptr += acc->acc_userl;
        ptr += acc->acc_userl;
 
 
        acc->acc_passl = *ptr++;
        acc->acc_passl = *ptr++;
        memcpy(&acc->acc_pass, ptr, acc->acc_passl);
        memcpy(&acc->acc_pass, ptr, acc->acc_passl);
        ptr += acc->acc_passl;
        ptr += acc->acc_passl;
 
 
        acc->acc_accl = *ptr++;
        acc->acc_accl = *ptr++;
        memcpy(&acc->acc_acc, ptr, acc->acc_accl);
        memcpy(&acc->acc_acc, ptr, acc->acc_accl);
 
 
        skb_pull(skb, acc->acc_accl + acc->acc_passl + acc->acc_userl + 3);
        skb_pull(skb, acc->acc_accl + acc->acc_passl + acc->acc_userl + 3);
 
 
}
}
 
 
static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
{
{
        unsigned char *ptr = skb->data;
        unsigned char *ptr = skb->data;
 
 
        opt->opt_optl   = *ptr++;
        opt->opt_optl   = *ptr++;
        opt->opt_status = 0;
        opt->opt_status = 0;
        memcpy(opt->opt_data, ptr, opt->opt_optl);
        memcpy(opt->opt_data, ptr, opt->opt_optl);
        skb_pull(skb, opt->opt_optl + 1);
        skb_pull(skb, opt->opt_optl + 1);
 
 
}
}
 
 
 
 
/*
/*
 * This is here for use in the sockopt() call as well as
 * This is here for use in the sockopt() call as well as
 * in accept(). Must be called with a locked socket.
 * in accept(). Must be called with a locked socket.
 */
 */
static int dn_wait_accept(struct socket *sock, int flags)
static int dn_wait_accept(struct socket *sock, int flags)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
 
 
        while(sk->state == TCP_LISTEN) {
        while(sk->state == TCP_LISTEN) {
                if (flags & O_NONBLOCK) {
                if (flags & O_NONBLOCK) {
                        return -EAGAIN;
                        return -EAGAIN;
                }
                }
 
 
                SOCK_SLEEP_PRE(sk)
                SOCK_SLEEP_PRE(sk)
 
 
                if (sk->state == TCP_LISTEN)
                if (sk->state == TCP_LISTEN)
                        schedule();
                        schedule();
 
 
                SOCK_SLEEP_POST(sk)
                SOCK_SLEEP_POST(sk)
 
 
                if (signal_pending(current))
                if (signal_pending(current))
                        return -ERESTARTSYS; /* But of course you don't! */
                        return -ERESTARTSYS; /* But of course you don't! */
        }
        }
 
 
        if ((DN_SK(sk)->state != DN_RUN) && (DN_SK(sk)->state != DN_DRC)) {
        if ((DN_SK(sk)->state != DN_RUN) && (DN_SK(sk)->state != DN_DRC)) {
                sock->state = SS_UNCONNECTED;
                sock->state = SS_UNCONNECTED;
                return sock_error(sk);
                return sock_error(sk);
        }
        }
 
 
        sock->state = SS_CONNECTED;
        sock->state = SS_CONNECTED;
 
 
        return 0;
        return 0;
}
}
 
 
 
 
static int dn_accept(struct socket *sock, struct socket *newsock, int flags)
static int dn_accept(struct socket *sock, struct socket *newsock, int flags)
{
{
        struct sock *sk = sock->sk, *newsk;
        struct sock *sk = sock->sk, *newsk;
        struct sk_buff *skb = NULL;
        struct sk_buff *skb = NULL;
        struct dn_skb_cb *cb;
        struct dn_skb_cb *cb;
        unsigned char menuver;
        unsigned char menuver;
        int err = 0;
        int err = 0;
        unsigned char type;
        unsigned char type;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (sk->state != TCP_LISTEN) {
        if (sk->state != TCP_LISTEN) {
                release_sock(sk);
                release_sock(sk);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        if (DN_SK(sk)->state != DN_O) {
        if (DN_SK(sk)->state != DN_O) {
                release_sock(sk);
                release_sock(sk);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        do
        do
        {
        {
                if ((skb = skb_dequeue(&sk->receive_queue)) == NULL)
                if ((skb = skb_dequeue(&sk->receive_queue)) == NULL)
                {
                {
                        if (flags & O_NONBLOCK)
                        if (flags & O_NONBLOCK)
                        {
                        {
                                release_sock(sk);
                                release_sock(sk);
                                return -EAGAIN;
                                return -EAGAIN;
                        }
                        }
 
 
                        SOCK_SLEEP_PRE(sk);
                        SOCK_SLEEP_PRE(sk);
 
 
                        if (!skb_peek(&sk->receive_queue))
                        if (!skb_peek(&sk->receive_queue))
                                schedule();
                                schedule();
 
 
                        SOCK_SLEEP_POST(sk);
                        SOCK_SLEEP_POST(sk);
 
 
                        if (signal_pending(current))
                        if (signal_pending(current))
                        {
                        {
                                release_sock(sk);
                                release_sock(sk);
                                return -ERESTARTSYS;
                                return -ERESTARTSYS;
                        }
                        }
                }
                }
        } while (skb == NULL);
        } while (skb == NULL);
 
 
        cb = DN_SKB_CB(skb);
        cb = DN_SKB_CB(skb);
 
 
        if ((newsk = dn_alloc_sock(newsock, sk->allocation)) == NULL) {
        if ((newsk = dn_alloc_sock(newsock, sk->allocation)) == NULL) {
                release_sock(sk);
                release_sock(sk);
                kfree_skb(skb);
                kfree_skb(skb);
                return -ENOBUFS;
                return -ENOBUFS;
        }
        }
        sk->ack_backlog--;
        sk->ack_backlog--;
        release_sock(sk);
        release_sock(sk);
 
 
        dst_release(xchg(&newsk->dst_cache, skb->dst));
        dst_release(xchg(&newsk->dst_cache, skb->dst));
        skb->dst = NULL;
        skb->dst = NULL;
 
 
        DN_SK(newsk)->state        = DN_CR;
        DN_SK(newsk)->state        = DN_CR;
        DN_SK(newsk)->addrrem      = cb->src_port;
        DN_SK(newsk)->addrrem      = cb->src_port;
        DN_SK(newsk)->services_rem = cb->services;
        DN_SK(newsk)->services_rem = cb->services;
        DN_SK(newsk)->info_rem     = cb->info;
        DN_SK(newsk)->info_rem     = cb->info;
        DN_SK(newsk)->segsize_rem  = cb->segsize;
        DN_SK(newsk)->segsize_rem  = cb->segsize;
        DN_SK(newsk)->accept_mode  = DN_SK(sk)->accept_mode;
        DN_SK(newsk)->accept_mode  = DN_SK(sk)->accept_mode;
 
 
        if (DN_SK(newsk)->segsize_rem < 230)
        if (DN_SK(newsk)->segsize_rem < 230)
                DN_SK(newsk)->segsize_rem = 230;
                DN_SK(newsk)->segsize_rem = 230;
 
 
        if ((DN_SK(newsk)->services_rem & NSP_FC_MASK) == NSP_FC_NONE)
        if ((DN_SK(newsk)->services_rem & NSP_FC_MASK) == NSP_FC_NONE)
                DN_SK(newsk)->max_window = decnet_no_fc_max_cwnd;
                DN_SK(newsk)->max_window = decnet_no_fc_max_cwnd;
 
 
        newsk->state  = TCP_LISTEN;
        newsk->state  = TCP_LISTEN;
        newsk->zapped = 0;
        newsk->zapped = 0;
 
 
        memcpy(&(DN_SK(newsk)->addr), &(DN_SK(sk)->addr), sizeof(struct sockaddr_dn));
        memcpy(&(DN_SK(newsk)->addr), &(DN_SK(sk)->addr), sizeof(struct sockaddr_dn));
 
 
        /*
        /*
         * If we are listening on a wild socket, we don't want
         * If we are listening on a wild socket, we don't want
         * the newly created socket on the wrong hash queue.
         * the newly created socket on the wrong hash queue.
         */
         */
        DN_SK(newsk)->addr.sdn_flags &= ~SDF_WILD;
        DN_SK(newsk)->addr.sdn_flags &= ~SDF_WILD;
 
 
        skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->addr), &type));
        skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->addr), &type));
        skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->peer), &type));
        skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->peer), &type));
        *(dn_address *)(DN_SK(newsk)->peer.sdn_add.a_addr) = cb->src;
        *(dn_address *)(DN_SK(newsk)->peer.sdn_add.a_addr) = cb->src;
        *(dn_address *)(DN_SK(newsk)->addr.sdn_add.a_addr) = cb->dst;
        *(dn_address *)(DN_SK(newsk)->addr.sdn_add.a_addr) = cb->dst;
 
 
        menuver = *skb->data;
        menuver = *skb->data;
        skb_pull(skb, 1);
        skb_pull(skb, 1);
 
 
        if (menuver & DN_MENUVER_ACC)
        if (menuver & DN_MENUVER_ACC)
                dn_access_copy(skb, &(DN_SK(newsk)->accessdata));
                dn_access_copy(skb, &(DN_SK(newsk)->accessdata));
 
 
        if (menuver & DN_MENUVER_USR)
        if (menuver & DN_MENUVER_USR)
                dn_user_copy(skb, &(DN_SK(newsk)->conndata_in));
                dn_user_copy(skb, &(DN_SK(newsk)->conndata_in));
 
 
        if (menuver & DN_MENUVER_PRX)
        if (menuver & DN_MENUVER_PRX)
                DN_SK(newsk)->peer.sdn_flags |= SDF_PROXY;
                DN_SK(newsk)->peer.sdn_flags |= SDF_PROXY;
 
 
        if (menuver & DN_MENUVER_UIC)
        if (menuver & DN_MENUVER_UIC)
                DN_SK(newsk)->peer.sdn_flags |= SDF_UICPROXY;
                DN_SK(newsk)->peer.sdn_flags |= SDF_UICPROXY;
 
 
        kfree_skb(skb);
        kfree_skb(skb);
 
 
        memcpy(&(DN_SK(newsk)->conndata_out), &(DN_SK(sk)->conndata_out),
        memcpy(&(DN_SK(newsk)->conndata_out), &(DN_SK(sk)->conndata_out),
                sizeof(struct optdata_dn));
                sizeof(struct optdata_dn));
        memcpy(&(DN_SK(newsk)->discdata_out), &(DN_SK(sk)->discdata_out),
        memcpy(&(DN_SK(newsk)->discdata_out), &(DN_SK(sk)->discdata_out),
                sizeof(struct optdata_dn));
                sizeof(struct optdata_dn));
 
 
        lock_sock(newsk);
        lock_sock(newsk);
        /*
        /*
         * FIXME: This can fail if we've run out of local ports....
         * FIXME: This can fail if we've run out of local ports....
         */
         */
        dn_hash_sock(newsk);
        dn_hash_sock(newsk);
 
 
        dn_send_conn_ack(newsk);
        dn_send_conn_ack(newsk);
 
 
        /*
        /*
         * Here we use sk->allocation since although the conn conf is
         * Here we use sk->allocation since although the conn conf is
         * for the newsk, the context is the old socket.
         * for the newsk, the context is the old socket.
         */
         */
        if (DN_SK(newsk)->accept_mode == ACC_IMMED) {
        if (DN_SK(newsk)->accept_mode == ACC_IMMED) {
                DN_SK(newsk)->state = DN_CC;
                DN_SK(newsk)->state = DN_CC;
                dn_send_conn_conf(newsk, sk->allocation);
                dn_send_conn_conf(newsk, sk->allocation);
                err = dn_wait_accept(newsock, flags);
                err = dn_wait_accept(newsock, flags);
        }
        }
 
 
        release_sock(newsk);
        release_sock(newsk);
        return err;
        return err;
}
}
 
 
 
 
static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int *uaddr_len,int peer)
static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int *uaddr_len,int peer)
{
{
        struct sockaddr_dn *sa = (struct sockaddr_dn *)uaddr;
        struct sockaddr_dn *sa = (struct sockaddr_dn *)uaddr;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
 
 
        *uaddr_len = sizeof(struct sockaddr_dn);
        *uaddr_len = sizeof(struct sockaddr_dn);
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (peer) {
        if (peer) {
                if ((sock->state != SS_CONNECTED &&
                if ((sock->state != SS_CONNECTED &&
                     sock->state != SS_CONNECTING) &&
                     sock->state != SS_CONNECTING) &&
                    scp->accept_mode == ACC_IMMED)
                    scp->accept_mode == ACC_IMMED)
                        return -ENOTCONN;
                        return -ENOTCONN;
 
 
                memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
                memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
        } else {
        } else {
                memcpy(sa, &scp->addr, sizeof(struct sockaddr_dn));
                memcpy(sa, &scp->addr, sizeof(struct sockaddr_dn));
        }
        }
 
 
        release_sock(sk);
        release_sock(sk);
 
 
        return 0;
        return 0;
}
}
 
 
 
 
static unsigned int dn_poll(struct file *file, struct socket *sock, poll_table  *wait)
static unsigned int dn_poll(struct file *file, struct socket *sock, poll_table  *wait)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        int mask = datagram_poll(file, sock, wait);
        int mask = datagram_poll(file, sock, wait);
 
 
        if (skb_queue_len(&scp->other_receive_queue))
        if (skb_queue_len(&scp->other_receive_queue))
                mask |= POLLRDBAND;
                mask |= POLLRDBAND;
 
 
        return mask;
        return mask;
}
}
 
 
static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        int err = -EOPNOTSUPP;
        int err = -EOPNOTSUPP;
        long amount = 0;
        long amount = 0;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int val;
        int val;
 
 
        switch(cmd)
        switch(cmd)
        {
        {
        case SIOCGIFADDR:
        case SIOCGIFADDR:
        case SIOCSIFADDR:
        case SIOCSIFADDR:
                return dn_dev_ioctl(cmd, (void *)arg);
                return dn_dev_ioctl(cmd, (void *)arg);
 
 
        case SIOCATMARK:
        case SIOCATMARK:
                lock_sock(sk);
                lock_sock(sk);
                val = (skb_queue_len(&scp->other_receive_queue) != 0);
                val = (skb_queue_len(&scp->other_receive_queue) != 0);
                if (scp->state != DN_RUN)
                if (scp->state != DN_RUN)
                        val = -ENOTCONN;
                        val = -ENOTCONN;
                release_sock(sk);
                release_sock(sk);
                return val;
                return val;
 
 
#ifdef CONFIG_DECNET_ROUTER
#ifdef CONFIG_DECNET_ROUTER
        case SIOCADDRT:
        case SIOCADDRT:
        case SIOCDELRT:
        case SIOCDELRT:
                return dn_fib_ioctl(sock, cmd, arg);
                return dn_fib_ioctl(sock, cmd, arg);
#endif /* CONFIG_DECNET_ROUTER */
#endif /* CONFIG_DECNET_ROUTER */
 
 
        case OSIOCSNETADDR:
        case OSIOCSNETADDR:
                if (!capable(CAP_NET_ADMIN)) {
                if (!capable(CAP_NET_ADMIN)) {
                        err = -EPERM;
                        err = -EPERM;
                        break;
                        break;
                }
                }
 
 
                dn_dev_devices_off();
                dn_dev_devices_off();
 
 
                decnet_address = (unsigned short)arg;
                decnet_address = (unsigned short)arg;
                dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
                dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
 
 
                dn_dev_devices_on();
                dn_dev_devices_on();
                err = 0;
                err = 0;
                break;
                break;
 
 
        case OSIOCGNETADDR:
        case OSIOCGNETADDR:
                err = put_user(decnet_address, (unsigned short *)arg);
                err = put_user(decnet_address, (unsigned short *)arg);
                break;
                break;
        case SIOCGIFCONF:
        case SIOCGIFCONF:
        case SIOCGIFFLAGS:
        case SIOCGIFFLAGS:
        case SIOCGIFBRDADDR:
        case SIOCGIFBRDADDR:
                return dev_ioctl(cmd,(void *)arg);
                return dev_ioctl(cmd,(void *)arg);
 
 
        case TIOCOUTQ:
        case TIOCOUTQ:
                amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
                amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
                if (amount < 0)
                if (amount < 0)
                        amount = 0;
                        amount = 0;
                err = put_user(amount, (int *)arg);
                err = put_user(amount, (int *)arg);
                break;
                break;
 
 
        case TIOCINQ:
        case TIOCINQ:
                lock_sock(sk);
                lock_sock(sk);
                if ((skb = skb_peek(&scp->other_receive_queue)) != NULL) {
                if ((skb = skb_peek(&scp->other_receive_queue)) != NULL) {
                        amount = skb->len;
                        amount = skb->len;
                } else {
                } else {
                        struct sk_buff *skb = sk->receive_queue.next;
                        struct sk_buff *skb = sk->receive_queue.next;
                        for(;;) {
                        for(;;) {
                                if (skb == (struct sk_buff *)&sk->receive_queue)
                                if (skb == (struct sk_buff *)&sk->receive_queue)
                                        break;
                                        break;
                                amount += skb->len;
                                amount += skb->len;
                                skb = skb->next;
                                skb = skb->next;
                        }
                        }
                }
                }
                release_sock(sk);
                release_sock(sk);
                err = put_user(amount, (int *)arg);
                err = put_user(amount, (int *)arg);
                break;
                break;
        }
        }
 
 
        return err;
        return err;
}
}
 
 
static int dn_listen(struct socket *sock, int backlog)
static int dn_listen(struct socket *sock, int backlog)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int err = -EINVAL;
        int err = -EINVAL;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (sk->zapped)
        if (sk->zapped)
                goto out;
                goto out;
 
 
        if ((DN_SK(sk)->state != DN_O) || (sk->state == TCP_LISTEN))
        if ((DN_SK(sk)->state != DN_O) || (sk->state == TCP_LISTEN))
                goto out;
                goto out;
 
 
        sk->max_ack_backlog = backlog;
        sk->max_ack_backlog = backlog;
        sk->ack_backlog     = 0;
        sk->ack_backlog     = 0;
        sk->state           = TCP_LISTEN;
        sk->state           = TCP_LISTEN;
        err                 = 0;
        err                 = 0;
        dn_rehash_sock(sk);
        dn_rehash_sock(sk);
 
 
out:
out:
        release_sock(sk);
        release_sock(sk);
 
 
        return err;
        return err;
}
}
 
 
 
 
static int dn_shutdown(struct socket *sock, int how)
static int dn_shutdown(struct socket *sock, int how)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        int err = -ENOTCONN;
        int err = -ENOTCONN;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (sock->state == SS_UNCONNECTED)
        if (sock->state == SS_UNCONNECTED)
                goto out;
                goto out;
 
 
        err = 0;
        err = 0;
        if (sock->state == SS_DISCONNECTING)
        if (sock->state == SS_DISCONNECTING)
                goto out;
                goto out;
 
 
        err = -EINVAL;
        err = -EINVAL;
        if (scp->state == DN_O)
        if (scp->state == DN_O)
                goto out;
                goto out;
 
 
        if (how != SHUTDOWN_MASK)
        if (how != SHUTDOWN_MASK)
                goto out;
                goto out;
 
 
 
 
        sk->shutdown = how;
        sk->shutdown = how;
        dn_destroy_sock(sk);
        dn_destroy_sock(sk);
        err = 0;
        err = 0;
 
 
out:
out:
        release_sock(sk);
        release_sock(sk);
 
 
        return err;
        return err;
}
}
 
 
static int dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
static int dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int err;
        int err;
 
 
        lock_sock(sk);
        lock_sock(sk);
        err = __dn_setsockopt(sock, level, optname, optval, optlen, 0);
        err = __dn_setsockopt(sock, level, optname, optval, optlen, 0);
        release_sock(sk);
        release_sock(sk);
 
 
        return err;
        return err;
}
}
 
 
static int __dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags)
static int __dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags)
{
{
        struct  sock *sk = sock->sk;
        struct  sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        union {
        union {
                struct optdata_dn opt;
                struct optdata_dn opt;
                struct accessdata_dn acc;
                struct accessdata_dn acc;
                int mode;
                int mode;
                unsigned long win;
                unsigned long win;
                int val;
                int val;
                unsigned char services;
                unsigned char services;
                unsigned char info;
                unsigned char info;
        } u;
        } u;
        int err;
        int err;
 
 
        if (optlen && !optval)
        if (optlen && !optval)
                return -EINVAL;
                return -EINVAL;
 
 
        if (optlen > sizeof(u))
        if (optlen > sizeof(u))
                return -EINVAL;
                return -EINVAL;
 
 
        if (copy_from_user(&u, optval, optlen))
        if (copy_from_user(&u, optval, optlen))
                return -EFAULT;
                return -EFAULT;
 
 
        switch(optname) {
        switch(optname) {
                case DSO_CONDATA:
                case DSO_CONDATA:
                        if (sock->state == SS_CONNECTED)
                        if (sock->state == SS_CONNECTED)
                                return -EISCONN;
                                return -EISCONN;
                        if ((scp->state != DN_O) && (scp->state != DN_CR))
                        if ((scp->state != DN_O) && (scp->state != DN_CR))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if (optlen != sizeof(struct optdata_dn))
                        if (optlen != sizeof(struct optdata_dn))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if (u.opt.opt_optl > 16)
                        if (u.opt.opt_optl > 16)
                                return -EINVAL;
                                return -EINVAL;
 
 
                        memcpy(&scp->conndata_out, &u.opt, optlen);
                        memcpy(&scp->conndata_out, &u.opt, optlen);
                        break;
                        break;
 
 
                case DSO_DISDATA:
                case DSO_DISDATA:
                        if (sock->state != SS_CONNECTED && scp->accept_mode == ACC_IMMED)
                        if (sock->state != SS_CONNECTED && scp->accept_mode == ACC_IMMED)
                                return -ENOTCONN;
                                return -ENOTCONN;
 
 
                        if (optlen != sizeof(struct optdata_dn))
                        if (optlen != sizeof(struct optdata_dn))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if (u.opt.opt_optl > 16)
                        if (u.opt.opt_optl > 16)
                                return -EINVAL;
                                return -EINVAL;
 
 
                        memcpy(&scp->discdata_out, &u.opt, optlen);
                        memcpy(&scp->discdata_out, &u.opt, optlen);
                        break;
                        break;
 
 
                case DSO_CONACCESS:
                case DSO_CONACCESS:
                        if (sock->state == SS_CONNECTED)
                        if (sock->state == SS_CONNECTED)
                                return -EISCONN;
                                return -EISCONN;
                        if (scp->state != DN_O)
                        if (scp->state != DN_O)
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if (optlen != sizeof(struct accessdata_dn))
                        if (optlen != sizeof(struct accessdata_dn))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if ((u.acc.acc_accl > DN_MAXACCL) ||
                        if ((u.acc.acc_accl > DN_MAXACCL) ||
                                        (u.acc.acc_passl > DN_MAXACCL) ||
                                        (u.acc.acc_passl > DN_MAXACCL) ||
                                        (u.acc.acc_userl > DN_MAXACCL))
                                        (u.acc.acc_userl > DN_MAXACCL))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        memcpy(&scp->accessdata, &u.acc, optlen);
                        memcpy(&scp->accessdata, &u.acc, optlen);
                        break;
                        break;
 
 
                case DSO_ACCEPTMODE:
                case DSO_ACCEPTMODE:
                        if (sock->state == SS_CONNECTED)
                        if (sock->state == SS_CONNECTED)
                                return -EISCONN;
                                return -EISCONN;
                        if (scp->state != DN_O)
                        if (scp->state != DN_O)
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if (optlen != sizeof(int))
                        if (optlen != sizeof(int))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        if ((u.mode != ACC_IMMED) && (u.mode != ACC_DEFER))
                        if ((u.mode != ACC_IMMED) && (u.mode != ACC_DEFER))
                                return -EINVAL;
                                return -EINVAL;
 
 
                        scp->accept_mode = (unsigned char)u.mode;
                        scp->accept_mode = (unsigned char)u.mode;
                        break;
                        break;
 
 
                case DSO_CONACCEPT:
                case DSO_CONACCEPT:
 
 
                        if (scp->state != DN_CR)
                        if (scp->state != DN_CR)
                                return -EINVAL;
                                return -EINVAL;
 
 
                        scp->state = DN_CC;
                        scp->state = DN_CC;
                        dn_send_conn_conf(sk, sk->allocation);
                        dn_send_conn_conf(sk, sk->allocation);
                        err = dn_wait_accept(sock, sock->file->f_flags);
                        err = dn_wait_accept(sock, sock->file->f_flags);
                        return err;
                        return err;
 
 
                case DSO_CONREJECT:
                case DSO_CONREJECT:
 
 
                        if (scp->state != DN_CR)
                        if (scp->state != DN_CR)
                                return -EINVAL;
                                return -EINVAL;
 
 
                        scp->state = DN_DR;
                        scp->state = DN_DR;
                        sk->shutdown = SHUTDOWN_MASK;
                        sk->shutdown = SHUTDOWN_MASK;
                        dn_nsp_send_disc(sk, 0x38, 0, sk->allocation);
                        dn_nsp_send_disc(sk, 0x38, 0, sk->allocation);
                        break;
                        break;
 
 
                default:
                default:
#ifdef CONFIG_NETFILTER
#ifdef CONFIG_NETFILTER
                return nf_setsockopt(sk, PF_DECnet, optname, optval, optlen);
                return nf_setsockopt(sk, PF_DECnet, optname, optval, optlen);
#endif
#endif
                case DSO_LINKINFO:
                case DSO_LINKINFO:
                case DSO_STREAM:
                case DSO_STREAM:
                case DSO_SEQPACKET:
                case DSO_SEQPACKET:
                        return -ENOPROTOOPT;
                        return -ENOPROTOOPT;
 
 
                case DSO_MAXWINDOW:
                case DSO_MAXWINDOW:
                        if (optlen != sizeof(unsigned long))
                        if (optlen != sizeof(unsigned long))
                                return -EINVAL;
                                return -EINVAL;
                        if (u.win > NSP_MAX_WINDOW)
                        if (u.win > NSP_MAX_WINDOW)
                                u.win = NSP_MAX_WINDOW;
                                u.win = NSP_MAX_WINDOW;
                        if (u.win == 0)
                        if (u.win == 0)
                                return -EINVAL;
                                return -EINVAL;
                        scp->max_window = u.win;
                        scp->max_window = u.win;
                        if (scp->snd_window > u.win)
                        if (scp->snd_window > u.win)
                                scp->snd_window = u.win;
                                scp->snd_window = u.win;
                        break;
                        break;
 
 
                case DSO_NODELAY:
                case DSO_NODELAY:
                        if (optlen != sizeof(int))
                        if (optlen != sizeof(int))
                                return -EINVAL;
                                return -EINVAL;
                        if (scp->nonagle == 2)
                        if (scp->nonagle == 2)
                                return -EINVAL;
                                return -EINVAL;
                        scp->nonagle = (u.val == 0) ? 0 : 1;
                        scp->nonagle = (u.val == 0) ? 0 : 1;
                        /* if (scp->nonagle == 1) { Push pending frames } */
                        /* if (scp->nonagle == 1) { Push pending frames } */
                        break;
                        break;
 
 
                case DSO_CORK:
                case DSO_CORK:
                        if (optlen != sizeof(int))
                        if (optlen != sizeof(int))
                                return -EINVAL;
                                return -EINVAL;
                        if (scp->nonagle == 1)
                        if (scp->nonagle == 1)
                                return -EINVAL;
                                return -EINVAL;
                        scp->nonagle = (u.val == 0) ? 0 : 2;
                        scp->nonagle = (u.val == 0) ? 0 : 2;
                        /* if (scp->nonagle == 0) { Push pending frames } */
                        /* if (scp->nonagle == 0) { Push pending frames } */
                        break;
                        break;
 
 
                case DSO_SERVICES:
                case DSO_SERVICES:
                        if (optlen != sizeof(unsigned char))
                        if (optlen != sizeof(unsigned char))
                                return -EINVAL;
                                return -EINVAL;
                        if ((u.services & ~NSP_FC_MASK) != 0x01)
                        if ((u.services & ~NSP_FC_MASK) != 0x01)
                                return -EINVAL;
                                return -EINVAL;
                        if ((u.services & NSP_FC_MASK) == NSP_FC_MASK)
                        if ((u.services & NSP_FC_MASK) == NSP_FC_MASK)
                                return -EINVAL;
                                return -EINVAL;
                        scp->services_loc = u.services;
                        scp->services_loc = u.services;
                        break;
                        break;
 
 
                case DSO_INFO:
                case DSO_INFO:
                        if (optlen != sizeof(unsigned char))
                        if (optlen != sizeof(unsigned char))
                                return -EINVAL;
                                return -EINVAL;
                        if (u.info & 0xfc)
                        if (u.info & 0xfc)
                                return -EINVAL;
                                return -EINVAL;
                        scp->info_loc = u.info;
                        scp->info_loc = u.info;
                        break;
                        break;
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
static int dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
static int dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int err;
        int err;
 
 
        lock_sock(sk);
        lock_sock(sk);
        err = __dn_getsockopt(sock, level, optname, optval, optlen, 0);
        err = __dn_getsockopt(sock, level, optname, optval, optlen, 0);
        release_sock(sk);
        release_sock(sk);
 
 
        return err;
        return err;
}
}
 
 
static int __dn_getsockopt(struct socket *sock, int level,int optname, char *optval,int *optlen, int flags)
static int __dn_getsockopt(struct socket *sock, int level,int optname, char *optval,int *optlen, int flags)
{
{
        struct  sock *sk = sock->sk;
        struct  sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        struct linkinfo_dn link;
        struct linkinfo_dn link;
        unsigned int r_len;
        unsigned int r_len;
        void *r_data = NULL;
        void *r_data = NULL;
        unsigned int val;
        unsigned int val;
 
 
        if(get_user(r_len , optlen))
        if(get_user(r_len , optlen))
                return -EFAULT;
                return -EFAULT;
 
 
        switch(optname) {
        switch(optname) {
                case DSO_CONDATA:
                case DSO_CONDATA:
                        if (r_len > sizeof(struct optdata_dn))
                        if (r_len > sizeof(struct optdata_dn))
                                r_len = sizeof(struct optdata_dn);
                                r_len = sizeof(struct optdata_dn);
                        r_data = &scp->conndata_in;
                        r_data = &scp->conndata_in;
                        break;
                        break;
 
 
                case DSO_DISDATA:
                case DSO_DISDATA:
                        if (r_len > sizeof(struct optdata_dn))
                        if (r_len > sizeof(struct optdata_dn))
                                r_len = sizeof(struct optdata_dn);
                                r_len = sizeof(struct optdata_dn);
                        r_data = &scp->discdata_in;
                        r_data = &scp->discdata_in;
                        break;
                        break;
 
 
                case DSO_CONACCESS:
                case DSO_CONACCESS:
                        if (r_len > sizeof(struct accessdata_dn))
                        if (r_len > sizeof(struct accessdata_dn))
                                r_len = sizeof(struct accessdata_dn);
                                r_len = sizeof(struct accessdata_dn);
                        r_data = &scp->accessdata;
                        r_data = &scp->accessdata;
                        break;
                        break;
 
 
                case DSO_ACCEPTMODE:
                case DSO_ACCEPTMODE:
                        if (r_len > sizeof(unsigned char))
                        if (r_len > sizeof(unsigned char))
                                r_len = sizeof(unsigned char);
                                r_len = sizeof(unsigned char);
                        r_data = &scp->accept_mode;
                        r_data = &scp->accept_mode;
                        break;
                        break;
 
 
                case DSO_LINKINFO:
                case DSO_LINKINFO:
                        if (r_len > sizeof(struct linkinfo_dn))
                        if (r_len > sizeof(struct linkinfo_dn))
                                r_len = sizeof(struct linkinfo_dn);
                                r_len = sizeof(struct linkinfo_dn);
 
 
                        switch(sock->state) {
                        switch(sock->state) {
                                case SS_CONNECTING:
                                case SS_CONNECTING:
                                        link.idn_linkstate = LL_CONNECTING;
                                        link.idn_linkstate = LL_CONNECTING;
                                        break;
                                        break;
                                case SS_DISCONNECTING:
                                case SS_DISCONNECTING:
                                        link.idn_linkstate = LL_DISCONNECTING;
                                        link.idn_linkstate = LL_DISCONNECTING;
                                        break;
                                        break;
                                case SS_CONNECTED:
                                case SS_CONNECTED:
                                        link.idn_linkstate = LL_RUNNING;
                                        link.idn_linkstate = LL_RUNNING;
                                        break;
                                        break;
                                default:
                                default:
                                        link.idn_linkstate = LL_INACTIVE;
                                        link.idn_linkstate = LL_INACTIVE;
                        }
                        }
 
 
                        link.idn_segsize = scp->segsize_rem;
                        link.idn_segsize = scp->segsize_rem;
                        r_data = &link;
                        r_data = &link;
                        break;
                        break;
 
 
                default:
                default:
#ifdef CONFIG_NETFILTER
#ifdef CONFIG_NETFILTER
                {
                {
                        int val, len;
                        int val, len;
 
 
                        if(get_user(len, optlen))
                        if(get_user(len, optlen))
                                return -EFAULT;
                                return -EFAULT;
 
 
                        val = nf_getsockopt(sk, PF_DECnet, optname,
                        val = nf_getsockopt(sk, PF_DECnet, optname,
                                                        optval, &len);
                                                        optval, &len);
                        if (val >= 0)
                        if (val >= 0)
                                val = put_user(len, optlen);
                                val = put_user(len, optlen);
                        return val;
                        return val;
                }
                }
#endif
#endif
                case DSO_STREAM:
                case DSO_STREAM:
                case DSO_SEQPACKET:
                case DSO_SEQPACKET:
                case DSO_CONACCEPT:
                case DSO_CONACCEPT:
                case DSO_CONREJECT:
                case DSO_CONREJECT:
                        return -ENOPROTOOPT;
                        return -ENOPROTOOPT;
 
 
                case DSO_MAXWINDOW:
                case DSO_MAXWINDOW:
                        if (r_len > sizeof(unsigned long))
                        if (r_len > sizeof(unsigned long))
                                r_len = sizeof(unsigned long);
                                r_len = sizeof(unsigned long);
                        r_data = &scp->max_window;
                        r_data = &scp->max_window;
                        break;
                        break;
 
 
                case DSO_NODELAY:
                case DSO_NODELAY:
                        if (r_len > sizeof(int))
                        if (r_len > sizeof(int))
                                r_len = sizeof(int);
                                r_len = sizeof(int);
                        val = (scp->nonagle == 1);
                        val = (scp->nonagle == 1);
                        r_data = &val;
                        r_data = &val;
                        break;
                        break;
 
 
                case DSO_CORK:
                case DSO_CORK:
                        if (r_len > sizeof(int))
                        if (r_len > sizeof(int))
                                r_len = sizeof(int);
                                r_len = sizeof(int);
                        val = (scp->nonagle == 2);
                        val = (scp->nonagle == 2);
                        r_data = &val;
                        r_data = &val;
                        break;
                        break;
 
 
                case DSO_SERVICES:
                case DSO_SERVICES:
                        if (r_len > sizeof(unsigned char))
                        if (r_len > sizeof(unsigned char))
                                r_len = sizeof(unsigned char);
                                r_len = sizeof(unsigned char);
                        r_data = &scp->services_rem;
                        r_data = &scp->services_rem;
                        break;
                        break;
 
 
                case DSO_INFO:
                case DSO_INFO:
                        if (r_len > sizeof(unsigned char))
                        if (r_len > sizeof(unsigned char))
                                r_len = sizeof(unsigned char);
                                r_len = sizeof(unsigned char);
                        r_data = &scp->info_rem;
                        r_data = &scp->info_rem;
                        break;
                        break;
        }
        }
 
 
        if (r_data) {
        if (r_data) {
                if (copy_to_user(optval, r_data, r_len))
                if (copy_to_user(optval, r_data, r_len))
                        return -EFAULT;
                        return -EFAULT;
                if (put_user(r_len, optlen))
                if (put_user(r_len, optlen))
                        return -EFAULT;
                        return -EFAULT;
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 * Used by send/recvmsg to wait until the socket is connected
 * Used by send/recvmsg to wait until the socket is connected
 * before passing data.
 * before passing data.
 */
 */
static int dn_wait_run(struct sock *sk, int flags)
static int dn_wait_run(struct sock *sk, int flags)
{
{
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        int err = 0;
        int err = 0;
 
 
        switch(scp->state) {
        switch(scp->state) {
                case DN_RUN:
                case DN_RUN:
                        return 0;
                        return 0;
 
 
                case DN_CR:
                case DN_CR:
                        scp->state = DN_CC;
                        scp->state = DN_CC;
                        dn_send_conn_conf(sk, sk->allocation);
                        dn_send_conn_conf(sk, sk->allocation);
                        return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0);
                        return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0);
                case DN_CI:
                case DN_CI:
                case DN_CC:
                case DN_CC:
                        break;
                        break;
                default:
                default:
                        return -ENOTCONN;
                        return -ENOTCONN;
        }
        }
 
 
        if (flags & MSG_DONTWAIT)
        if (flags & MSG_DONTWAIT)
                return -EWOULDBLOCK;
                return -EWOULDBLOCK;
 
 
        do {
        do {
                if ((err = sock_error(sk)) != 0)
                if ((err = sock_error(sk)) != 0)
                        break;
                        break;
 
 
                if (signal_pending(current)) {
                if (signal_pending(current)) {
                        err = -ERESTARTSYS;
                        err = -ERESTARTSYS;
                        break;
                        break;
                }
                }
 
 
                SOCK_SLEEP_PRE(sk)
                SOCK_SLEEP_PRE(sk)
 
 
                if (scp->state != DN_RUN)
                if (scp->state != DN_RUN)
                        schedule();
                        schedule();
 
 
                SOCK_SLEEP_POST(sk)
                SOCK_SLEEP_POST(sk)
 
 
        } while(scp->state != DN_RUN);
        } while(scp->state != DN_RUN);
 
 
        return 0;
        return 0;
}
}
 
 
 
 
static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target)
static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target)
{
{
        struct sk_buff *skb = q->next;
        struct sk_buff *skb = q->next;
        int len = 0;
        int len = 0;
 
 
        if (flags & MSG_OOB)
        if (flags & MSG_OOB)
                return skb_queue_len(q) ? 1 : 0;
                return skb_queue_len(q) ? 1 : 0;
 
 
        while(skb != (struct sk_buff *)q) {
        while(skb != (struct sk_buff *)q) {
                struct dn_skb_cb *cb = DN_SKB_CB(skb);
                struct dn_skb_cb *cb = DN_SKB_CB(skb);
                len += skb->len;
                len += skb->len;
 
 
                if (cb->nsp_flags & 0x40) {
                if (cb->nsp_flags & 0x40) {
                        /* SOCK_SEQPACKET reads to EOM */
                        /* SOCK_SEQPACKET reads to EOM */
                        if (sk->type == SOCK_SEQPACKET)
                        if (sk->type == SOCK_SEQPACKET)
                                return 1;
                                return 1;
                        /* so does SOCK_STREAM unless WAITALL is specified */
                        /* so does SOCK_STREAM unless WAITALL is specified */
                        if (!(flags & MSG_WAITALL))
                        if (!(flags & MSG_WAITALL))
                                return 1;
                                return 1;
                }
                }
 
 
                /* minimum data length for read exceeded */
                /* minimum data length for read exceeded */
                if (len >= target)
                if (len >= target)
                        return 1;
                        return 1;
 
 
                skb = skb->next;
                skb = skb->next;
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
 
 
static int dn_recvmsg(struct socket *sock, struct msghdr *msg, int size,
static int dn_recvmsg(struct socket *sock, struct msghdr *msg, int size,
        int flags, struct scm_cookie *scm)
        int flags, struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        struct sk_buff_head *queue = &sk->receive_queue;
        struct sk_buff_head *queue = &sk->receive_queue;
        int target = size > 1 ? 1 : 0;
        int target = size > 1 ? 1 : 0;
        int copied = 0;
        int copied = 0;
        int rv = 0;
        int rv = 0;
        struct sk_buff *skb, *nskb;
        struct sk_buff *skb, *nskb;
        struct dn_skb_cb *cb = NULL;
        struct dn_skb_cb *cb = NULL;
        unsigned char eor = 0;
        unsigned char eor = 0;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (sk->zapped) {
        if (sk->zapped) {
                rv = -EADDRNOTAVAIL;
                rv = -EADDRNOTAVAIL;
                goto out;
                goto out;
        }
        }
 
 
        if ((rv = dn_wait_run(sk, flags)) != 0)
        if ((rv = dn_wait_run(sk, flags)) != 0)
                goto out;
                goto out;
 
 
        if (sk->shutdown & RCV_SHUTDOWN) {
        if (sk->shutdown & RCV_SHUTDOWN) {
                send_sig(SIGPIPE, current, 0);
                send_sig(SIGPIPE, current, 0);
                rv = -EPIPE;
                rv = -EPIPE;
                goto out;
                goto out;
        }
        }
 
 
        if (flags & ~(MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT)) {
        if (flags & ~(MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT)) {
                rv = -EOPNOTSUPP;
                rv = -EOPNOTSUPP;
                goto out;
                goto out;
        }
        }
 
 
        if (flags & MSG_OOB)
        if (flags & MSG_OOB)
                queue = &scp->other_receive_queue;
                queue = &scp->other_receive_queue;
 
 
        if (flags & MSG_WAITALL)
        if (flags & MSG_WAITALL)
                target = size;
                target = size;
 
 
 
 
        /*
        /*
         * See if there is data ready to read, sleep if there isn't
         * See if there is data ready to read, sleep if there isn't
         */
         */
        for(;;) {
        for(;;) {
                if (sk->err)
                if (sk->err)
                        goto out;
                        goto out;
 
 
                if (skb_queue_len(&scp->other_receive_queue)) {
                if (skb_queue_len(&scp->other_receive_queue)) {
                        if (!(flags & MSG_OOB)) {
                        if (!(flags & MSG_OOB)) {
                                msg->msg_flags |= MSG_OOB;
                                msg->msg_flags |= MSG_OOB;
                                if (!scp->other_report) {
                                if (!scp->other_report) {
                                        scp->other_report = 1;
                                        scp->other_report = 1;
                                        goto out;
                                        goto out;
                                }
                                }
                        }
                        }
                }
                }
 
 
                if (scp->state != DN_RUN)
                if (scp->state != DN_RUN)
                        goto out;
                        goto out;
 
 
                if (signal_pending(current)) {
                if (signal_pending(current)) {
                        rv = -ERESTARTSYS;
                        rv = -ERESTARTSYS;
                        goto out;
                        goto out;
                }
                }
 
 
                if (dn_data_ready(sk, queue, flags, target))
                if (dn_data_ready(sk, queue, flags, target))
                        break;
                        break;
 
 
                if (flags & MSG_DONTWAIT) {
                if (flags & MSG_DONTWAIT) {
                        rv = -EWOULDBLOCK;
                        rv = -EWOULDBLOCK;
                        goto out;
                        goto out;
                }
                }
 
 
                set_bit(SOCK_ASYNC_WAITDATA, &sock->flags);
                set_bit(SOCK_ASYNC_WAITDATA, &sock->flags);
                SOCK_SLEEP_PRE(sk)
                SOCK_SLEEP_PRE(sk)
 
 
                if (!dn_data_ready(sk, queue, flags, target))
                if (!dn_data_ready(sk, queue, flags, target))
                        schedule();
                        schedule();
 
 
                SOCK_SLEEP_POST(sk)
                SOCK_SLEEP_POST(sk)
                clear_bit(SOCK_ASYNC_WAITDATA, &sock->flags);
                clear_bit(SOCK_ASYNC_WAITDATA, &sock->flags);
        }
        }
 
 
        for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) {
        for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) {
                int chunk = skb->len;
                int chunk = skb->len;
                cb = DN_SKB_CB(skb);
                cb = DN_SKB_CB(skb);
 
 
                if ((chunk + copied) > size)
                if ((chunk + copied) > size)
                        chunk = size - copied;
                        chunk = size - copied;
 
 
                if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
                if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
                        rv = -EFAULT;
                        rv = -EFAULT;
                        break;
                        break;
                }
                }
                copied += chunk;
                copied += chunk;
 
 
                if (!(flags & MSG_PEEK))
                if (!(flags & MSG_PEEK))
                        skb_pull(skb, chunk);
                        skb_pull(skb, chunk);
 
 
                eor = cb->nsp_flags & 0x40;
                eor = cb->nsp_flags & 0x40;
                nskb = skb->next;
                nskb = skb->next;
 
 
                if (skb->len == 0) {
                if (skb->len == 0) {
                        skb_unlink(skb);
                        skb_unlink(skb);
                        kfree_skb(skb);
                        kfree_skb(skb);
                        /*
                        /*
                         * N.B. Don't refer to skb or cb after this point
                         * N.B. Don't refer to skb or cb after this point
                         * in loop.
                         * in loop.
                         */
                         */
                        if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
                        if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
                                scp->flowloc_sw = DN_SEND;
                                scp->flowloc_sw = DN_SEND;
                                dn_nsp_send_link(sk, DN_SEND, 0);
                                dn_nsp_send_link(sk, DN_SEND, 0);
                        }
                        }
                }
                }
 
 
                if (eor) {
                if (eor) {
                        if (sk->type == SOCK_SEQPACKET)
                        if (sk->type == SOCK_SEQPACKET)
                                break;
                                break;
                        if (!(flags & MSG_WAITALL))
                        if (!(flags & MSG_WAITALL))
                                break;
                                break;
                }
                }
 
 
                if (flags & MSG_OOB)
                if (flags & MSG_OOB)
                        break;
                        break;
 
 
                if (copied >= target)
                if (copied >= target)
                        break;
                        break;
        }
        }
 
 
        rv = copied;
        rv = copied;
 
 
 
 
        if (eor && (sk->type == SOCK_SEQPACKET))
        if (eor && (sk->type == SOCK_SEQPACKET))
                msg->msg_flags |= MSG_EOR;
                msg->msg_flags |= MSG_EOR;
 
 
out:
out:
        if (rv == 0)
        if (rv == 0)
                rv = (flags & MSG_PEEK) ? -sk->err : sock_error(sk);
                rv = (flags & MSG_PEEK) ? -sk->err : sock_error(sk);
 
 
        if ((rv >= 0) && msg->msg_name) {
        if ((rv >= 0) && msg->msg_name) {
                memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn));
                memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn));
                msg->msg_namelen = sizeof(struct sockaddr_dn);
                msg->msg_namelen = sizeof(struct sockaddr_dn);
        }
        }
 
 
        release_sock(sk);
        release_sock(sk);
 
 
        return rv;
        return rv;
}
}
 
 
 
 
static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *queue, int flags)
static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *queue, int flags)
{
{
        unsigned char fctype = scp->services_rem & NSP_FC_MASK;
        unsigned char fctype = scp->services_rem & NSP_FC_MASK;
        if (skb_queue_len(queue) >= scp->snd_window)
        if (skb_queue_len(queue) >= scp->snd_window)
                return 1;
                return 1;
        if (fctype != NSP_FC_NONE) {
        if (fctype != NSP_FC_NONE) {
                if (flags & MSG_OOB) {
                if (flags & MSG_OOB) {
                        if (scp->flowrem_oth == 0)
                        if (scp->flowrem_oth == 0)
                                return 1;
                                return 1;
                } else {
                } else {
                        if (scp->flowrem_dat == 0)
                        if (scp->flowrem_dat == 0)
                                return 1;
                                return 1;
                }
                }
        }
        }
        return 0;
        return 0;
}
}
 
 
static int dn_sendmsg(struct socket *sock, struct msghdr *msg, int size,
static int dn_sendmsg(struct socket *sock, struct msghdr *msg, int size,
           struct scm_cookie *scm)
           struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct dn_scp *scp = DN_SK(sk);
        struct dn_scp *scp = DN_SK(sk);
        int mss;
        int mss;
        struct sk_buff_head *queue = &scp->data_xmit_queue;
        struct sk_buff_head *queue = &scp->data_xmit_queue;
        int flags = msg->msg_flags;
        int flags = msg->msg_flags;
        int err = 0;
        int err = 0;
        int sent = 0;
        int sent = 0;
        int addr_len = msg->msg_namelen;
        int addr_len = msg->msg_namelen;
        struct sockaddr_dn *addr = (struct sockaddr_dn *)msg->msg_name;
        struct sockaddr_dn *addr = (struct sockaddr_dn *)msg->msg_name;
        struct sk_buff *skb = NULL;
        struct sk_buff *skb = NULL;
        struct dn_skb_cb *cb;
        struct dn_skb_cb *cb;
        unsigned char msgflg;
        unsigned char msgflg;
        unsigned char *ptr;
        unsigned char *ptr;
        unsigned short ack;
        unsigned short ack;
        int len;
        int len;
        unsigned char fctype;
        unsigned char fctype;
 
 
        if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT|MSG_EOR))
        if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT|MSG_EOR))
                return -EOPNOTSUPP;
                return -EOPNOTSUPP;
 
 
        if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
        if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
                return -EINVAL;
                return -EINVAL;
 
 
        if (sk->zapped && dn_auto_bind(sock))  {
        if (sk->zapped && dn_auto_bind(sock))  {
                err = -EADDRNOTAVAIL;
                err = -EADDRNOTAVAIL;
                goto out;
                goto out;
        }
        }
 
 
        if (scp->state == DN_O) {
        if (scp->state == DN_O) {
                if (!addr_len || !addr) {
                if (!addr_len || !addr) {
                        err = -ENOTCONN;
                        err = -ENOTCONN;
                        goto out;
                        goto out;
                }
                }
 
 
                if ((err = dn_connect(sock, (struct sockaddr *)addr, addr_len, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0)) < 0)
                if ((err = dn_connect(sock, (struct sockaddr *)addr, addr_len, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0)) < 0)
                        goto out;
                        goto out;
        }
        }
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if ((err = dn_wait_run(sk, flags)) < 0)
        if ((err = dn_wait_run(sk, flags)) < 0)
                goto out;
                goto out;
 
 
        if (sk->shutdown & SEND_SHUTDOWN) {
        if (sk->shutdown & SEND_SHUTDOWN) {
                send_sig(SIGPIPE, current, 0);
                send_sig(SIGPIPE, current, 0);
                err = -EPIPE;
                err = -EPIPE;
                goto out;
                goto out;
        }
        }
 
 
        if ((flags & MSG_TRYHARD) && sk->dst_cache)
        if ((flags & MSG_TRYHARD) && sk->dst_cache)
                dst_negative_advice(&sk->dst_cache);
                dst_negative_advice(&sk->dst_cache);
 
 
        mss = scp->segsize_rem;
        mss = scp->segsize_rem;
        fctype = scp->services_rem & NSP_FC_MASK;
        fctype = scp->services_rem & NSP_FC_MASK;
 
 
        if (sk->dst_cache && sk->dst_cache->neighbour) {
        if (sk->dst_cache && sk->dst_cache->neighbour) {
                struct dn_neigh *dn = (struct dn_neigh *)sk->dst_cache->neighbour;
                struct dn_neigh *dn = (struct dn_neigh *)sk->dst_cache->neighbour;
                if (dn->blksize < (mss + 11))
                if (dn->blksize < (mss + 11))
                        mss = dn->blksize - 11;
                        mss = dn->blksize - 11;
        }
        }
 
 
        /*
        /*
         * The only difference between SEQPACKET & STREAM sockets under DECnet
         * The only difference between SEQPACKET & STREAM sockets under DECnet
         * is that SEQPACKET sockets set the MSG_EOR flag for the last
         * is that SEQPACKET sockets set the MSG_EOR flag for the last
         * session control message segment.
         * session control message segment.
         */
         */
 
 
        if (flags & MSG_OOB) {
        if (flags & MSG_OOB) {
                mss = 16;
                mss = 16;
                queue = &scp->other_xmit_queue;
                queue = &scp->other_xmit_queue;
                if (size > mss) {
                if (size > mss) {
                        err = -EMSGSIZE;
                        err = -EMSGSIZE;
                        goto out;
                        goto out;
                }
                }
        }
        }
 
 
        scp->persist_fxn = dn_nsp_xmit_timeout;
        scp->persist_fxn = dn_nsp_xmit_timeout;
 
 
        while(sent < size) {
        while(sent < size) {
                err = sock_error(sk);
                err = sock_error(sk);
                if (err)
                if (err)
                        goto out;
                        goto out;
 
 
                if (signal_pending(current)) {
                if (signal_pending(current)) {
                        err = -ERESTARTSYS;
                        err = -ERESTARTSYS;
                        goto out;
                        goto out;
                }
                }
 
 
                /*
                /*
                 * Calculate size that we wish to send.
                 * Calculate size that we wish to send.
                 */
                 */
                len = size - sent;
                len = size - sent;
 
 
                if (len > mss)
                if (len > mss)
                        len = mss;
                        len = mss;
 
 
                /*
                /*
                 * Wait for queue size to go down below the window
                 * Wait for queue size to go down below the window
                 * size.
                 * size.
                 */
                 */
                if (dn_queue_too_long(scp, queue, flags)) {
                if (dn_queue_too_long(scp, queue, flags)) {
                        if (flags & MSG_DONTWAIT) {
                        if (flags & MSG_DONTWAIT) {
                                err = -EWOULDBLOCK;
                                err = -EWOULDBLOCK;
                                goto out;
                                goto out;
                        }
                        }
 
 
                        SOCK_SLEEP_PRE(sk)
                        SOCK_SLEEP_PRE(sk)
 
 
                        if (dn_queue_too_long(scp, queue, flags))
                        if (dn_queue_too_long(scp, queue, flags))
                                schedule();
                                schedule();
 
 
                        SOCK_SLEEP_POST(sk)
                        SOCK_SLEEP_POST(sk)
 
 
                        continue;
                        continue;
                }
                }
 
 
                /*
                /*
                 * Get a suitably sized skb.
                 * Get a suitably sized skb.
                 */
                 */
                skb = dn_alloc_send_skb(sk, &len, flags & MSG_DONTWAIT, &err);
                skb = dn_alloc_send_skb(sk, &len, flags & MSG_DONTWAIT, &err);
 
 
                if (err)
                if (err)
                        break;
                        break;
 
 
                if (!skb)
                if (!skb)
                        continue;
                        continue;
 
 
                cb = DN_SKB_CB(skb);
                cb = DN_SKB_CB(skb);
 
 
                ptr = skb_put(skb, 9);
                ptr = skb_put(skb, 9);
 
 
                if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
                if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
                        err = -EFAULT;
                        err = -EFAULT;
                        goto out;
                        goto out;
                }
                }
 
 
                if (flags & MSG_OOB) {
                if (flags & MSG_OOB) {
                        cb->segnum = scp->numoth;
                        cb->segnum = scp->numoth;
                        seq_add(&scp->numoth, 1);
                        seq_add(&scp->numoth, 1);
                        msgflg = 0x30;
                        msgflg = 0x30;
                        ack = (scp->numoth_rcv & 0x0FFF) | 0x8000;
                        ack = (scp->numoth_rcv & 0x0FFF) | 0x8000;
                        scp->ackxmt_oth = scp->numoth_rcv;
                        scp->ackxmt_oth = scp->numoth_rcv;
                        if (fctype != NSP_FC_NONE)
                        if (fctype != NSP_FC_NONE)
                                scp->flowrem_oth--;
                                scp->flowrem_oth--;
                } else {
                } else {
                        cb->segnum = scp->numdat;
                        cb->segnum = scp->numdat;
                        seq_add(&scp->numdat, 1);
                        seq_add(&scp->numdat, 1);
                        msgflg = 0x00;
                        msgflg = 0x00;
                        if (sock->type == SOCK_STREAM)
                        if (sock->type == SOCK_STREAM)
                                msgflg = 0x60;
                                msgflg = 0x60;
                        if (scp->seg_total == 0)
                        if (scp->seg_total == 0)
                                msgflg |= 0x20;
                                msgflg |= 0x20;
 
 
                        scp->seg_total += len;
                        scp->seg_total += len;
 
 
                        if (((sent + len) == size) && (flags & MSG_EOR)) {
                        if (((sent + len) == size) && (flags & MSG_EOR)) {
                                msgflg |= 0x40;
                                msgflg |= 0x40;
                                scp->seg_total = 0;
                                scp->seg_total = 0;
                                if (fctype == NSP_FC_SCMC)
                                if (fctype == NSP_FC_SCMC)
                                        scp->flowrem_dat--;
                                        scp->flowrem_dat--;
                        }
                        }
                        ack = (scp->numdat_rcv & 0x0FFF) | 0x8000;
                        ack = (scp->numdat_rcv & 0x0FFF) | 0x8000;
                        scp->ackxmt_dat = scp->numdat_rcv;
                        scp->ackxmt_dat = scp->numdat_rcv;
                        if (fctype == NSP_FC_SRC)
                        if (fctype == NSP_FC_SRC)
                                scp->flowrem_dat--;
                                scp->flowrem_dat--;
                }
                }
 
 
                *ptr++ = msgflg;
                *ptr++ = msgflg;
                *(__u16 *)ptr = scp->addrrem;
                *(__u16 *)ptr = scp->addrrem;
                ptr += 2;
                ptr += 2;
                *(__u16 *)ptr = scp->addrloc;
                *(__u16 *)ptr = scp->addrloc;
                ptr += 2;
                ptr += 2;
                *(__u16 *)ptr = dn_htons(ack);
                *(__u16 *)ptr = dn_htons(ack);
                ptr += 2;
                ptr += 2;
                *(__u16 *)ptr = dn_htons(cb->segnum);
                *(__u16 *)ptr = dn_htons(cb->segnum);
 
 
                sent += len;
                sent += len;
                dn_nsp_queue_xmit(sk, skb, sk->allocation, flags & MSG_OOB);
                dn_nsp_queue_xmit(sk, skb, sk->allocation, flags & MSG_OOB);
                skb = NULL;
                skb = NULL;
 
 
                scp->persist = dn_nsp_persist(sk);
                scp->persist = dn_nsp_persist(sk);
 
 
        }
        }
out:
out:
 
 
        if (skb)
        if (skb)
                kfree_skb(skb);
                kfree_skb(skb);
 
 
        release_sock(sk);
        release_sock(sk);
 
 
        return sent ? sent : err;
        return sent ? sent : err;
}
}
 
 
static int dn_device_event(struct notifier_block *this, unsigned long event,
static int dn_device_event(struct notifier_block *this, unsigned long event,
                        void *ptr)
                        void *ptr)
{
{
        struct net_device *dev = (struct net_device *)ptr;
        struct net_device *dev = (struct net_device *)ptr;
 
 
        switch(event) {
        switch(event) {
                case NETDEV_UP:
                case NETDEV_UP:
                        dn_dev_up(dev);
                        dn_dev_up(dev);
                        break;
                        break;
                case NETDEV_DOWN:
                case NETDEV_DOWN:
                        dn_dev_down(dev);
                        dn_dev_down(dev);
                        break;
                        break;
                default:
                default:
                        break;
                        break;
        }
        }
 
 
        return NOTIFY_DONE;
        return NOTIFY_DONE;
}
}
 
 
static struct notifier_block dn_dev_notifier = {
static struct notifier_block dn_dev_notifier = {
        notifier_call:  dn_device_event,
        notifier_call:  dn_device_event,
};
};
 
 
extern int dn_route_rcv(struct sk_buff *, struct net_device *, struct packet_type *);
extern int dn_route_rcv(struct sk_buff *, struct net_device *, struct packet_type *);
 
 
static struct packet_type dn_dix_packet_type = {
static struct packet_type dn_dix_packet_type = {
        type:           __constant_htons(ETH_P_DNA_RT),
        type:           __constant_htons(ETH_P_DNA_RT),
        dev:            NULL,           /* All devices */
        dev:            NULL,           /* All devices */
        func:           dn_route_rcv,
        func:           dn_route_rcv,
        data:           (void*)1,
        data:           (void*)1,
};
};
 
 
#define IS_NOT_PRINTABLE(x) ((x) < 32 || (x) > 126)
#define IS_NOT_PRINTABLE(x) ((x) < 32 || (x) > 126)
 
 
static void dn_printable_object(struct sockaddr_dn *dn, unsigned char *buf)
static void dn_printable_object(struct sockaddr_dn *dn, unsigned char *buf)
{
{
        int i;
        int i;
 
 
        switch (dn_ntohs(dn->sdn_objnamel)) {
        switch (dn_ntohs(dn->sdn_objnamel)) {
                case 0:
                case 0:
                        sprintf(buf, "%d", dn->sdn_objnum);
                        sprintf(buf, "%d", dn->sdn_objnum);
                        break;
                        break;
                default:
                default:
                        for (i = 0; i < dn_ntohs(dn->sdn_objnamel); i++) {
                        for (i = 0; i < dn_ntohs(dn->sdn_objnamel); i++) {
                                buf[i] = dn->sdn_objname[i];
                                buf[i] = dn->sdn_objname[i];
                                if (IS_NOT_PRINTABLE(buf[i]))
                                if (IS_NOT_PRINTABLE(buf[i]))
                                        buf[i] = '.';
                                        buf[i] = '.';
                        }
                        }
                        buf[i] = 0;
                        buf[i] = 0;
        }
        }
}
}
 
 
static int dn_get_info(char *buffer, char **start, off_t offset, int length)
static int dn_get_info(char *buffer, char **start, off_t offset, int length)
{
{
        struct sock *sk;
        struct sock *sk;
        struct dn_scp *scp;
        struct dn_scp *scp;
        int len = 0;
        int len = 0;
        off_t pos = 0;
        off_t pos = 0;
        off_t begin = 0;
        off_t begin = 0;
        char buf1[DN_ASCBUF_LEN];
        char buf1[DN_ASCBUF_LEN];
        char buf2[DN_ASCBUF_LEN];
        char buf2[DN_ASCBUF_LEN];
        char local_object[DN_MAXOBJL+3];
        char local_object[DN_MAXOBJL+3];
        char remote_object[DN_MAXOBJL+3];
        char remote_object[DN_MAXOBJL+3];
        int i;
        int i;
 
 
        len += sprintf(buffer + len, "Local                                              Remote\n");
        len += sprintf(buffer + len, "Local                                              Remote\n");
 
 
        read_lock(&dn_hash_lock);
        read_lock(&dn_hash_lock);
        for(i = 0; i < DN_SK_HASH_SIZE; i++) {
        for(i = 0; i < DN_SK_HASH_SIZE; i++) {
                for(sk = dn_sk_hash[i]; sk != NULL; sk = sk->next) {
                for(sk = dn_sk_hash[i]; sk != NULL; sk = sk->next) {
                        scp = DN_SK(sk);
                        scp = DN_SK(sk);
 
 
                        dn_printable_object(&scp->addr, local_object);
                        dn_printable_object(&scp->addr, local_object);
                        dn_printable_object(&scp->peer, remote_object);
                        dn_printable_object(&scp->peer, remote_object);
 
 
                        len += sprintf(buffer + len,
                        len += sprintf(buffer + len,
                                        "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n",
                                        "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n",
                                        dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1),
                                        dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1),
                                        scp->addrloc,
                                        scp->addrloc,
                                        scp->numdat,
                                        scp->numdat,
                                        scp->numoth,
                                        scp->numoth,
                                        scp->ackxmt_dat,
                                        scp->ackxmt_dat,
                                        scp->ackxmt_oth,
                                        scp->ackxmt_oth,
                                        scp->flowloc_sw,
                                        scp->flowloc_sw,
                                        local_object,
                                        local_object,
                                        dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2),
                                        dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2),
                                        scp->addrrem,
                                        scp->addrrem,
                                        scp->numdat_rcv,
                                        scp->numdat_rcv,
                                        scp->numoth_rcv,
                                        scp->numoth_rcv,
                                        scp->ackrcv_dat,
                                        scp->ackrcv_dat,
                                        scp->ackrcv_oth,
                                        scp->ackrcv_oth,
                                        scp->flowrem_sw,
                                        scp->flowrem_sw,
                                        remote_object,
                                        remote_object,
                                        dn_state2asc(scp->state),
                                        dn_state2asc(scp->state),
                                        ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
                                        ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
 
 
                        pos = begin + len;
                        pos = begin + len;
                        if (pos < offset) {
                        if (pos < offset) {
                                len = 0;
                                len = 0;
                                begin = pos;
                                begin = pos;
                        }
                        }
                        if (pos > (offset + length))
                        if (pos > (offset + length))
                                break;
                                break;
                }
                }
        }
        }
        read_unlock(&dn_hash_lock);
        read_unlock(&dn_hash_lock);
 
 
        *start = buffer + (offset - begin);
        *start = buffer + (offset - begin);
        len -= (offset - begin);
        len -= (offset - begin);
 
 
        if (len > length)
        if (len > length)
                len = length;
                len = length;
 
 
        return len;
        return len;
}
}
 
 
 
 
static struct net_proto_family  dn_family_ops = {
static struct net_proto_family  dn_family_ops = {
        family:         AF_DECnet,
        family:         AF_DECnet,
        create:         dn_create,
        create:         dn_create,
};
};
 
 
static struct proto_ops dn_proto_ops = {
static struct proto_ops dn_proto_ops = {
        family:         AF_DECnet,
        family:         AF_DECnet,
 
 
        release:        dn_release,
        release:        dn_release,
        bind:           dn_bind,
        bind:           dn_bind,
        connect:        dn_connect,
        connect:        dn_connect,
        socketpair:     sock_no_socketpair,
        socketpair:     sock_no_socketpair,
        accept:         dn_accept,
        accept:         dn_accept,
        getname:        dn_getname,
        getname:        dn_getname,
        poll:           dn_poll,
        poll:           dn_poll,
        ioctl:          dn_ioctl,
        ioctl:          dn_ioctl,
        listen:         dn_listen,
        listen:         dn_listen,
        shutdown:       dn_shutdown,
        shutdown:       dn_shutdown,
        setsockopt:     dn_setsockopt,
        setsockopt:     dn_setsockopt,
        getsockopt:     dn_getsockopt,
        getsockopt:     dn_getsockopt,
        sendmsg:        dn_sendmsg,
        sendmsg:        dn_sendmsg,
        recvmsg:        dn_recvmsg,
        recvmsg:        dn_recvmsg,
        mmap:           sock_no_mmap,
        mmap:           sock_no_mmap,
        sendpage:       sock_no_sendpage,
        sendpage:       sock_no_sendpage,
};
};
 
 
#ifdef CONFIG_SYSCTL
#ifdef CONFIG_SYSCTL
void dn_register_sysctl(void);
void dn_register_sysctl(void);
void dn_unregister_sysctl(void);
void dn_unregister_sysctl(void);
#endif
#endif
 
 
 
 
#ifdef MODULE
#ifdef MODULE
EXPORT_NO_SYMBOLS;
EXPORT_NO_SYMBOLS;
MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
MODULE_AUTHOR("Linux DECnet Project Team");
MODULE_AUTHOR("Linux DECnet Project Team");
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
 
 
static int addr[2] = {0, 0};
static int addr[2] = {0, 0};
 
 
MODULE_PARM(addr, "2i");
MODULE_PARM(addr, "2i");
MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
#endif
#endif
 
 
static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.20-pre1s (C) 1995-2002 Linux DECnet Project Team\n";
static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.20-pre1s (C) 1995-2002 Linux DECnet Project Team\n";
 
 
static int __init decnet_init(void)
static int __init decnet_init(void)
{
{
#ifdef MODULE
#ifdef MODULE
        if (addr[0] > 63 || addr[0] < 0) {
        if (addr[0] > 63 || addr[0] < 0) {
                printk(KERN_ERR "DECnet: Area must be between 0 and 63");
                printk(KERN_ERR "DECnet: Area must be between 0 and 63");
                return 1;
                return 1;
        }
        }
 
 
        if (addr[1] > 1023 || addr[1] < 0) {
        if (addr[1] > 1023 || addr[1] < 0) {
                printk(KERN_ERR "DECnet: Node must be between 0 and 1023");
                printk(KERN_ERR "DECnet: Node must be between 0 and 1023");
                return 1;
                return 1;
        }
        }
 
 
        decnet_address = dn_htons((addr[0] << 10) | addr[1]);
        decnet_address = dn_htons((addr[0] << 10) | addr[1]);
        dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
        dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
#endif
#endif
 
 
        printk(banner);
        printk(banner);
 
 
        sock_register(&dn_family_ops);
        sock_register(&dn_family_ops);
        dev_add_pack(&dn_dix_packet_type);
        dev_add_pack(&dn_dix_packet_type);
        register_netdevice_notifier(&dn_dev_notifier);
        register_netdevice_notifier(&dn_dev_notifier);
 
 
        proc_net_create("decnet", 0, dn_get_info);
        proc_net_create("decnet", 0, dn_get_info);
 
 
        dn_neigh_init();
        dn_neigh_init();
        dn_dev_init();
        dn_dev_init();
        dn_route_init();
        dn_route_init();
 
 
#ifdef CONFIG_DECNET_ROUTER
#ifdef CONFIG_DECNET_ROUTER
        dn_fib_init();
        dn_fib_init();
#endif /* CONFIG_DECNET_ROUTER */
#endif /* CONFIG_DECNET_ROUTER */
 
 
#ifdef CONFIG_SYSCTL
#ifdef CONFIG_SYSCTL
        dn_register_sysctl();
        dn_register_sysctl();
#endif /* CONFIG_SYSCTL */
#endif /* CONFIG_SYSCTL */
 
 
        /*
        /*
         * Prevent DECnet module unloading until its fixed properly.
         * Prevent DECnet module unloading until its fixed properly.
         * Requires an audit of the code to check for memory leaks and
         * Requires an audit of the code to check for memory leaks and
         * initialisation problems etc.
         * initialisation problems etc.
         */
         */
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
 
 
        return 0;
        return 0;
 
 
}
}
 
 
#ifndef MODULE
#ifndef MODULE
static int __init decnet_setup(char *str)
static int __init decnet_setup(char *str)
{
{
        unsigned short area = simple_strtoul(str, &str, 0);
        unsigned short area = simple_strtoul(str, &str, 0);
        unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
        unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
 
 
        decnet_address = dn_htons(area << 10 | node);
        decnet_address = dn_htons(area << 10 | node);
        dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
        dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
 
 
        return 1;
        return 1;
}
}
 
 
__setup("decnet=", decnet_setup);
__setup("decnet=", decnet_setup);
#endif
#endif
 
 
static void __exit decnet_exit(void)
static void __exit decnet_exit(void)
{
{
        sock_unregister(AF_DECnet);
        sock_unregister(AF_DECnet);
        dev_remove_pack(&dn_dix_packet_type);
        dev_remove_pack(&dn_dix_packet_type);
 
 
#ifdef CONFIG_SYSCTL
#ifdef CONFIG_SYSCTL
        dn_unregister_sysctl();
        dn_unregister_sysctl();
#endif /* CONFIG_SYSCTL */
#endif /* CONFIG_SYSCTL */
 
 
        unregister_netdevice_notifier(&dn_dev_notifier);
        unregister_netdevice_notifier(&dn_dev_notifier);
 
 
        dn_route_cleanup();
        dn_route_cleanup();
        dn_dev_cleanup();
        dn_dev_cleanup();
        dn_neigh_cleanup();
        dn_neigh_cleanup();
 
 
#ifdef CONFIG_DECNET_ROUTER
#ifdef CONFIG_DECNET_ROUTER
        dn_fib_cleanup();
        dn_fib_cleanup();
#endif /* CONFIG_DECNET_ROUTER */
#endif /* CONFIG_DECNET_ROUTER */
 
 
        proc_net_remove("decnet");
        proc_net_remove("decnet");
}
}
 
 
module_init(decnet_init);
module_init(decnet_init);
module_exit(decnet_exit);
module_exit(decnet_exit);
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.