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

Subversion Repositories or1k

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

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

Rev 1275 Rev 1765
/*
/*
 *      This module implements the (SPP-derived) Sequenced Packet eXchange
 *      This module implements the (SPP-derived) Sequenced Packet eXchange
 *      (SPX) protocol for Linux 2.1.X as specified in
 *      (SPX) protocol for Linux 2.1.X as specified in
 *              NetWare SPX Services Specification, Semantics and API
 *              NetWare SPX Services Specification, Semantics and API
 *               Revision:       1.00
 *               Revision:       1.00
 *               Revision Date:  February 9, 1993
 *               Revision Date:  February 9, 1993
 *
 *
 *      Developers:
 *      Developers:
 *      Jay Schulist    <jschlst@samba.org>
 *      Jay Schulist    <jschlst@samba.org>
 *      Jim Freeman     <jfree@caldera.com>
 *      Jim Freeman     <jfree@caldera.com>
 *
 *
 *      Changes:
 *      Changes:
 *      Alan Cox        :       Fixed an skb_unshare check for NULL
 *      Alan Cox        :       Fixed an skb_unshare check for NULL
 *                              that crashed it under load. Renamed and
 *                              that crashed it under load. Renamed and
 *                              made static the ipx ops. Removed the hack
 *                              made static the ipx ops. Removed the hack
 *                              ipx methods interface. Dropped AF_SPX - its
 *                              ipx methods interface. Dropped AF_SPX - its
 *                              the wrong abstraction.
 *                              the wrong abstraction.
 *      Eduardo Trapani :       Added a check for the return value of
 *      Eduardo Trapani :       Added a check for the return value of
 *                              ipx_if_offset that crashed sock_alloc_send_skb.
 *                              ipx_if_offset that crashed sock_alloc_send_skb.
 *                              Added spx_datagram_poll() so that select()
 *                              Added spx_datagram_poll() so that select()
 *                              works now on SPX sockets.  Added updating
 *                              works now on SPX sockets.  Added updating
 *                              of the alloc count to follow rmt_seq.
 *                              of the alloc count to follow rmt_seq.
 *
 *
 *      This program is free software; you can redistribute it and/or
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *      2 of the License, or (at your option) any later version.
 *
 *
 *      None of the authors or maintainers or their employers admit
 *      None of the authors or maintainers or their employers admit
 *      liability nor provide warranty for any of this software.
 *      liability nor provide warranty for any of this software.
 *      This material is provided "as is" and at no charge.
 *      This material is provided "as is" and at no charge.
 */
 */
 
 
#include <linux/module.h>
#include <linux/module.h>
#include <net/ipx.h>
#include <net/ipx.h>
#include <net/spx.h>
#include <net/spx.h>
#include <net/sock.h>
#include <net/sock.h>
#include <asm/byteorder.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <linux/uio.h>
#include <linux/uio.h>
#include <linux/unistd.h>
#include <linux/unistd.h>
#include <linux/poll.h>
#include <linux/poll.h>
 
 
static struct proto_ops *ipx_operations;
static struct proto_ops *ipx_operations;
static struct proto_ops spx_ops;
static struct proto_ops spx_ops;
static __u16  connids;
static __u16  connids;
 
 
/* Functions needed for SPX connection start up */
/* Functions needed for SPX connection start up */
static int spx_transmit(struct sock *sk,struct sk_buff *skb,int type,int len);
static int spx_transmit(struct sock *sk,struct sk_buff *skb,int type,int len);
static void spx_retransmit(unsigned long data);
static void spx_retransmit(unsigned long data);
static void spx_watchdog(unsigned long data);
static void spx_watchdog(unsigned long data);
void spx_rcv(struct sock *sk, int bytes);
void spx_rcv(struct sock *sk, int bytes);
 
 
extern void ipx_remove_socket(struct sock *sk);
extern void ipx_remove_socket(struct sock *sk);
 
 
/* Datagram poll:       the same code as datagram_poll() in net/core
/* Datagram poll:       the same code as datagram_poll() in net/core
                        but the right spx buffers are looked at and
                        but the right spx buffers are looked at and
                        there is no question on the type of the socket
                        there is no question on the type of the socket
                        */
                        */
static unsigned int spx_datagram_poll(struct file * file, struct socket *sock, poll_table *wait)
static unsigned int spx_datagram_poll(struct file * file, struct socket *sock, poll_table *wait)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        unsigned int mask;
        unsigned int mask;
 
 
        poll_wait(file, sk->sleep, wait);
        poll_wait(file, sk->sleep, wait);
        mask = 0;
        mask = 0;
 
 
        /* exceptional events? */
        /* exceptional events? */
        if (sk->err || !skb_queue_empty(&sk->error_queue))
        if (sk->err || !skb_queue_empty(&sk->error_queue))
                mask |= POLLERR;
                mask |= POLLERR;
        if (sk->shutdown & RCV_SHUTDOWN)
        if (sk->shutdown & RCV_SHUTDOWN)
                mask |= POLLHUP;
                mask |= POLLHUP;
 
 
        /* readable? */
        /* readable? */
        if (!skb_queue_empty(&pdata->rcv_queue))
        if (!skb_queue_empty(&pdata->rcv_queue))
                mask |= POLLIN | POLLRDNORM;
                mask |= POLLIN | POLLRDNORM;
 
 
        /* Need to check for termination and startup */
        /* Need to check for termination and startup */
        if (sk->state==TCP_CLOSE)
        if (sk->state==TCP_CLOSE)
                mask |= POLLHUP;
                mask |= POLLHUP;
        /* connection hasn't started yet? */
        /* connection hasn't started yet? */
        if (sk->state == TCP_SYN_SENT)
        if (sk->state == TCP_SYN_SENT)
                return mask;
                return mask;
 
 
        /* writable? */
        /* writable? */
        if (sock_writeable(sk))
        if (sock_writeable(sk))
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
        else
        else
                set_bit(SOCK_ASYNC_NOSPACE,&sk->socket->flags);
                set_bit(SOCK_ASYNC_NOSPACE,&sk->socket->flags);
 
 
        return mask;
        return mask;
}
}
 
 
/* Create the SPX specific data */
/* Create the SPX specific data */
static int spx_sock_init(struct sock *sk)
static int spx_sock_init(struct sock *sk)
{
{
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
 
 
        pdata->state            = SPX_CLOSED;
        pdata->state            = SPX_CLOSED;
        pdata->sequence         = 0;
        pdata->sequence         = 0;
        pdata->acknowledge      = 0;
        pdata->acknowledge      = 0;
        pdata->source_connid    = htons(connids);
        pdata->source_connid    = htons(connids);
        pdata->rmt_seq          = 0;
        pdata->rmt_seq          = 0;
        connids++;
        connids++;
 
 
        pdata->owner            = (void *)sk;
        pdata->owner            = (void *)sk;
        pdata->sndbuf           = sk->sndbuf;
        pdata->sndbuf           = sk->sndbuf;
 
 
        pdata->watchdog.function = spx_watchdog;
        pdata->watchdog.function = spx_watchdog;
        pdata->watchdog.data    = (unsigned long)sk;
        pdata->watchdog.data    = (unsigned long)sk;
        pdata->wd_interval      = VERIFY_TIMEOUT;
        pdata->wd_interval      = VERIFY_TIMEOUT;
        pdata->retransmit.function = spx_retransmit;
        pdata->retransmit.function = spx_retransmit;
        pdata->retransmit.data  = (unsigned long)sk;
        pdata->retransmit.data  = (unsigned long)sk;
        pdata->retransmits      = 0;
        pdata->retransmits      = 0;
        pdata->retries          = 0;
        pdata->retries          = 0;
        pdata->max_retries      = RETRY_COUNT;
        pdata->max_retries      = RETRY_COUNT;
 
 
        skb_queue_head_init(&pdata->rcv_queue);
        skb_queue_head_init(&pdata->rcv_queue);
        skb_queue_head_init(&pdata->transmit_queue);
        skb_queue_head_init(&pdata->transmit_queue);
        skb_queue_head_init(&pdata->retransmit_queue);
        skb_queue_head_init(&pdata->retransmit_queue);
 
 
        return (0);
        return (0);
}
}
 
 
static int spx_create(struct socket *sock, int protocol)
static int spx_create(struct socket *sock, int protocol)
{
{
        struct sock *sk;
        struct sock *sk;
 
 
        /*
        /*
         *      Called on connection receive so cannot be GFP_KERNEL
         *      Called on connection receive so cannot be GFP_KERNEL
         */
         */
 
 
        sk = sk_alloc(PF_IPX, GFP_ATOMIC, 1);
        sk = sk_alloc(PF_IPX, GFP_ATOMIC, 1);
        if(sk == NULL)
        if(sk == NULL)
                return (-ENOMEM);
                return (-ENOMEM);
 
 
        switch(sock->type)
        switch(sock->type)
        {
        {
                case SOCK_SEQPACKET:
                case SOCK_SEQPACKET:
                        sock->ops = &spx_ops;
                        sock->ops = &spx_ops;
                        break;
                        break;
                default:
                default:
                        sk_free(sk);
                        sk_free(sk);
                        return (-ESOCKTNOSUPPORT);
                        return (-ESOCKTNOSUPPORT);
        }
        }
 
 
        sock_init_data(sock, sk);
        sock_init_data(sock, sk);
        spx_sock_init(sk);
        spx_sock_init(sk);
        sk->data_ready  = spx_rcv;
        sk->data_ready  = spx_rcv;
        sk->destruct    = NULL;
        sk->destruct    = NULL;
        sk->no_check    = 1;
        sk->no_check    = 1;
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
 
 
        return (0);
        return (0);
}
}
 
 
 
 
void spx_close_socket(struct sock *sk)
void spx_close_socket(struct sock *sk)
{
{
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
 
 
        pdata->state    = SPX_CLOSED;
        pdata->state    = SPX_CLOSED;
        sk->state       = TCP_CLOSE;
        sk->state       = TCP_CLOSE;
        del_timer(&pdata->retransmit);
        del_timer(&pdata->retransmit);
        del_timer(&pdata->watchdog);
        del_timer(&pdata->watchdog);
}
}
 
 
void spx_destroy_socket(struct sock *sk)
void spx_destroy_socket(struct sock *sk)
{
{
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct sk_buff *skb;
        struct sk_buff *skb;
 
 
        ipx_remove_socket(sk);
        ipx_remove_socket(sk);
        while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
        while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
                kfree_skb(skb);
                kfree_skb(skb);
        while((skb = skb_dequeue(&pdata->transmit_queue)) != NULL)
        while((skb = skb_dequeue(&pdata->transmit_queue)) != NULL)
                kfree_skb(skb);
                kfree_skb(skb);
        while((skb = skb_dequeue(&pdata->retransmit_queue)) != NULL)
        while((skb = skb_dequeue(&pdata->retransmit_queue)) != NULL)
                kfree_skb(skb);
                kfree_skb(skb);
        while((skb = skb_dequeue(&pdata->rcv_queue)) != NULL)
        while((skb = skb_dequeue(&pdata->rcv_queue)) != NULL)
                kfree_skb(skb);
                kfree_skb(skb);
 
 
        sk_free(sk);
        sk_free(sk);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
/* Release an SPX socket */
/* Release an SPX socket */
static int spx_release(struct socket *sock)
static int spx_release(struct socket *sock)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
 
 
        if(sk == NULL)
        if(sk == NULL)
                return (0);
                return (0);
        if(!sk->dead)
        if(!sk->dead)
                sk->state_change(sk);
                sk->state_change(sk);
        sk->dead = 1;
        sk->dead = 1;
 
 
        if(pdata->state != SPX_CLOSED)
        if(pdata->state != SPX_CLOSED)
        {
        {
                spx_transmit(sk, NULL, DISCON, 0);
                spx_transmit(sk, NULL, DISCON, 0);
                spx_close_socket(sk);
                spx_close_socket(sk);
        }
        }
 
 
        sock->sk        = NULL;
        sock->sk        = NULL;
        sk->socket      = NULL;
        sk->socket      = NULL;
        spx_destroy_socket(sk);
        spx_destroy_socket(sk);
 
 
        return (0);
        return (0);
}
}
 
 
/* Move a socket into listening state. */
/* Move a socket into listening state. */
static int spx_listen(struct socket *sock, int backlog)
static int spx_listen(struct socket *sock, int backlog)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
 
 
        if(sock->state != SS_UNCONNECTED)
        if(sock->state != SS_UNCONNECTED)
                return (-EINVAL);
                return (-EINVAL);
        if(sock->type != SOCK_SEQPACKET)
        if(sock->type != SOCK_SEQPACKET)
                return (-EOPNOTSUPP);
                return (-EOPNOTSUPP);
        if(sk->zapped != 0)
        if(sk->zapped != 0)
                return (-EAGAIN);
                return (-EAGAIN);
 
 
        sk->max_ack_backlog = backlog;
        sk->max_ack_backlog = backlog;
        if(sk->state != TCP_LISTEN)
        if(sk->state != TCP_LISTEN)
        {
        {
                sk->ack_backlog = 0;
                sk->ack_backlog = 0;
                sk->state = TCP_LISTEN;
                sk->state = TCP_LISTEN;
        }
        }
        sk->socket->flags |= __SO_ACCEPTCON;
        sk->socket->flags |= __SO_ACCEPTCON;
 
 
        return (0);
        return (0);
}
}
 
 
/* Accept a pending SPX connection */
/* Accept a pending SPX connection */
static int spx_accept(struct socket *sock, struct socket *newsock, int flags)
static int spx_accept(struct socket *sock, struct socket *newsock, int flags)
{
{
        struct sock *sk;
        struct sock *sk;
        struct sock *newsk;
        struct sock *newsk;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int err;
        int err;
 
 
        if(sock->sk == NULL)
        if(sock->sk == NULL)
                return (-EINVAL);
                return (-EINVAL);
        sk = sock->sk;
        sk = sock->sk;
 
 
        if((sock->state != SS_UNCONNECTED) || !(sock->flags & __SO_ACCEPTCON))
        if((sock->state != SS_UNCONNECTED) || !(sock->flags & __SO_ACCEPTCON))
                return (-EINVAL);
                return (-EINVAL);
        if(sock->type != SOCK_SEQPACKET)
        if(sock->type != SOCK_SEQPACKET)
                return (-EOPNOTSUPP);
                return (-EOPNOTSUPP);
        if(sk->state != TCP_LISTEN)
        if(sk->state != TCP_LISTEN)
                return (-EINVAL);
                return (-EINVAL);
 
 
        cli();
        cli();
        do {
        do {
                skb = skb_dequeue(&sk->receive_queue);
                skb = skb_dequeue(&sk->receive_queue);
                if(skb == NULL)
                if(skb == NULL)
                {
                {
                        if(flags & O_NONBLOCK)
                        if(flags & O_NONBLOCK)
                        {
                        {
                                sti();
                                sti();
                                return (-EWOULDBLOCK);
                                return (-EWOULDBLOCK);
                        }
                        }
                        interruptible_sleep_on(sk->sleep);
                        interruptible_sleep_on(sk->sleep);
                        if(signal_pending(current))
                        if(signal_pending(current))
                        {
                        {
                                sti();
                                sti();
                                return (-ERESTARTSYS);
                                return (-ERESTARTSYS);
                        }
                        }
                }
                }
        } while (skb == NULL);
        } while (skb == NULL);
 
 
        newsk           = skb->sk;
        newsk           = skb->sk;
        newsk->pair     = NULL;
        newsk->pair     = NULL;
        sti();
        sti();
 
 
        err = spx_transmit(newsk, skb, CONACK, 0);   /* Connection ACK */
        err = spx_transmit(newsk, skb, CONACK, 0);   /* Connection ACK */
        if(err)
        if(err)
                return (err);
                return (err);
 
 
        /* Now attach up the new socket */
        /* Now attach up the new socket */
        sock->sk        = NULL;
        sock->sk        = NULL;
        sk->ack_backlog--;
        sk->ack_backlog--;
        newsock->sk     = newsk;
        newsock->sk     = newsk;
        newsk->state    = TCP_ESTABLISHED;
        newsk->state    = TCP_ESTABLISHED;
        newsk->protinfo.af_ipx.dest_addr = newsk->tp_pinfo.af_spx.dest_addr;
        newsk->protinfo.af_ipx.dest_addr = newsk->tp_pinfo.af_spx.dest_addr;
 
 
        return (0);
        return (0);
}
}
 
 
/* Build a connection to an SPX socket */
/* Build a connection to an SPX socket */
static int spx_connect(struct socket *sock, struct sockaddr *uaddr,
static int spx_connect(struct socket *sock, struct sockaddr *uaddr,
                int addr_len, int flags)
                int addr_len, int flags)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct sockaddr_ipx src;
        struct sockaddr_ipx src;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int size, err;
        int size, err;
 
 
        size = sizeof(src);
        size = sizeof(src);
        err  = ipx_operations->getname(sock, (struct sockaddr *)&src, &size, 0);
        err  = ipx_operations->getname(sock, (struct sockaddr *)&src, &size, 0);
        if(err)
        if(err)
                return (err);
                return (err);
 
 
        pdata->source_addr.net  = src.sipx_network;
        pdata->source_addr.net  = src.sipx_network;
        memcpy(pdata->source_addr.node, src.sipx_node, IPX_NODE_LEN);
        memcpy(pdata->source_addr.node, src.sipx_node, IPX_NODE_LEN);
        pdata->source_addr.sock = (unsigned short)src.sipx_port;
        pdata->source_addr.sock = (unsigned short)src.sipx_port;
 
 
        err = ipx_operations->connect(sock, uaddr, addr_len, flags);
        err = ipx_operations->connect(sock, uaddr, addr_len, flags);
        if(err)
        if(err)
                return (err);
                return (err);
 
 
        pdata->dest_addr = sk->protinfo.af_ipx.dest_addr;
        pdata->dest_addr = sk->protinfo.af_ipx.dest_addr;
        pdata->state     = SPX_CONNECTING;
        pdata->state     = SPX_CONNECTING;
        sock->state      = SS_CONNECTING;
        sock->state      = SS_CONNECTING;
        sk->state        = TCP_SYN_SENT;
        sk->state        = TCP_SYN_SENT;
 
 
        /* Send Connection request */
        /* Send Connection request */
        err = spx_transmit(sk, NULL, CONREQ, 0);
        err = spx_transmit(sk, NULL, CONREQ, 0);
        if(err)
        if(err)
                return (err);
                return (err);
 
 
        cli();
        cli();
        do {
        do {
                skb = skb_dequeue(&sk->receive_queue);
                skb = skb_dequeue(&sk->receive_queue);
                if(skb == NULL)
                if(skb == NULL)
                {
                {
                        if(flags & O_NONBLOCK)
                        if(flags & O_NONBLOCK)
                        {
                        {
                                sti();
                                sti();
                                return (-EWOULDBLOCK);
                                return (-EWOULDBLOCK);
                        }
                        }
                        interruptible_sleep_on(sk->sleep);
                        interruptible_sleep_on(sk->sleep);
                        if(signal_pending(current))
                        if(signal_pending(current))
                        {
                        {
                                sti();
                                sti();
                                return (-ERESTARTSYS);
                                return (-ERESTARTSYS);
                        }
                        }
                }
                }
        } while (skb == NULL);
        } while (skb == NULL);
 
 
        if(pdata->state == SPX_CLOSED)
        if(pdata->state == SPX_CLOSED)
        {
        {
                sti();
                sti();
                del_timer(&pdata->watchdog);
                del_timer(&pdata->watchdog);
                return (-ETIMEDOUT);
                return (-ETIMEDOUT);
        }
        }
 
 
        sock->state     = SS_CONNECTED;
        sock->state     = SS_CONNECTED;
        sk->state       = TCP_ESTABLISHED;
        sk->state       = TCP_ESTABLISHED;
        kfree_skb(skb);
        kfree_skb(skb);
        sti();
        sti();
 
 
        return (0);
        return (0);
}
}
 
 
/*
/*
 * Calculate the timeout for a packet. Thankfully SPX has a large
 * Calculate the timeout for a packet. Thankfully SPX has a large
 * fudge factor (3/4 secs) and does not pay much attention to RTT.
 * fudge factor (3/4 secs) and does not pay much attention to RTT.
 * As we simply have a default retry time of 1*HZ and a max retry
 * As we simply have a default retry time of 1*HZ and a max retry
 * time of 5*HZ. Between those values we increase the timeout based
 * time of 5*HZ. Between those values we increase the timeout based
 * on the number of retransmit tries.
 * on the number of retransmit tries.
 *
 *
 * FixMe: This is quite fake, but will work for now. (JS)
 * FixMe: This is quite fake, but will work for now. (JS)
 */
 */
static inline unsigned long spx_calc_rtt(int tries)
static inline unsigned long spx_calc_rtt(int tries)
{
{
        if(tries < 1)
        if(tries < 1)
                return (RETRY_TIME);
                return (RETRY_TIME);
        if(tries > 5)
        if(tries > 5)
                return (MAX_RETRY_DELAY);
                return (MAX_RETRY_DELAY);
        return (tries * HZ);
        return (tries * HZ);
}
}
 
 
static int spx_route_skb(struct spx_opt *pdata, struct sk_buff *skb, int type)
static int spx_route_skb(struct spx_opt *pdata, struct sk_buff *skb, int type)
{
{
        struct sk_buff *skb2;
        struct sk_buff *skb2;
        int err = 0;
        int err = 0;
 
 
        skb = skb_unshare(skb, GFP_ATOMIC);
        skb = skb_unshare(skb, GFP_ATOMIC);
        if(skb == NULL)
        if(skb == NULL)
                return (-ENOBUFS);
                return (-ENOBUFS);
 
 
        switch(type)
        switch(type)
        {
        {
                case (CONREQ):
                case (CONREQ):
                case (DATA):
                case (DATA):
                        if(!skb_queue_empty(&pdata->retransmit_queue))
                        if(!skb_queue_empty(&pdata->retransmit_queue))
                        {
                        {
                                skb_queue_tail(&pdata->transmit_queue, skb);
                                skb_queue_tail(&pdata->transmit_queue, skb);
                                return 0;
                                return 0;
                        }
                        }
 
 
                case (TQUEUE):
                case (TQUEUE):
                        pdata->retransmit.expires = jiffies + spx_calc_rtt(0);
                        pdata->retransmit.expires = jiffies + spx_calc_rtt(0);
                        add_timer(&pdata->retransmit);
                        add_timer(&pdata->retransmit);
 
 
                        skb2 = skb_clone(skb, GFP_NOIO);
                        skb2 = skb_clone(skb, GFP_NOIO);
                        if(skb2 == NULL)
                        if(skb2 == NULL)
                                return -ENOBUFS;
                                return -ENOBUFS;
                        skb_queue_tail(&pdata->retransmit_queue, skb2);
                        skb_queue_tail(&pdata->retransmit_queue, skb2);
 
 
                case (ACK):
                case (ACK):
                case (CONACK):
                case (CONACK):
                case (WDREQ):
                case (WDREQ):
                case (WDACK):
                case (WDACK):
                case (DISCON):
                case (DISCON):
                case (DISACK):
                case (DISACK):
                case (RETRAN):
                case (RETRAN):
                default:
                default:
                        /* Send data */
                        /* Send data */
                        err = ipxrtr_route_skb(skb);
                        err = ipxrtr_route_skb(skb);
                        if(err)
                        if(err)
                                kfree_skb(skb);
                                kfree_skb(skb);
        }
        }
 
 
        return (err);
        return (err);
}
}
 
 
/* SPX packet transmit engine */
/* SPX packet transmit engine */
static int spx_transmit(struct sock *sk, struct sk_buff *skb, int type, int len)
static int spx_transmit(struct sock *sk, struct sk_buff *skb, int type, int len)
{
{
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct ipxspxhdr *ipxh;
        struct ipxspxhdr *ipxh;
        unsigned long flags;
        unsigned long flags;
        int err;
        int err;
 
 
        if(skb == NULL)
        if(skb == NULL)
        {
        {
                int offset  = ipx_if_offset(pdata->dest_addr.net);
                int offset  = ipx_if_offset(pdata->dest_addr.net);
                int size    = offset + sizeof(struct ipxspxhdr);
                int size    = offset + sizeof(struct ipxspxhdr);
 
 
                if (offset < 0) /* ENETUNREACH */
                if (offset < 0) /* ENETUNREACH */
                        return(-ENETUNREACH);
                        return(-ENETUNREACH);
 
 
                save_flags(flags);
                save_flags(flags);
                cli();
                cli();
                skb = sock_alloc_send_skb(sk, size, 0, &err);
                skb = sock_alloc_send_skb(sk, size, 0, &err);
                if(skb == NULL) {
                if(skb == NULL) {
                        restore_flags(flags);
                        restore_flags(flags);
                        return (-ENOMEM);
                        return (-ENOMEM);
                }
                }
                skb_reserve(skb, offset);
                skb_reserve(skb, offset);
                skb->h.raw = skb->nh.raw = skb_put(skb,sizeof(struct ipxspxhdr));
                skb->h.raw = skb->nh.raw = skb_put(skb,sizeof(struct ipxspxhdr));
                restore_flags(flags);
                restore_flags(flags);
        }
        }
 
 
        /* IPX header */
        /* IPX header */
        ipxh = (struct ipxspxhdr *)skb->nh.raw;
        ipxh = (struct ipxspxhdr *)skb->nh.raw;
        ipxh->ipx.ipx_checksum  = 0xFFFF;
        ipxh->ipx.ipx_checksum  = 0xFFFF;
        ipxh->ipx.ipx_pktsize   = htons(SPX_SYS_PKT_LEN);
        ipxh->ipx.ipx_pktsize   = htons(SPX_SYS_PKT_LEN);
        ipxh->ipx.ipx_tctrl     = 0;
        ipxh->ipx.ipx_tctrl     = 0;
        ipxh->ipx.ipx_type      = IPX_TYPE_SPX;
        ipxh->ipx.ipx_type      = IPX_TYPE_SPX;
        ipxh->ipx.ipx_dest      = pdata->dest_addr;
        ipxh->ipx.ipx_dest      = pdata->dest_addr;
        ipxh->ipx.ipx_source    = pdata->source_addr;
        ipxh->ipx.ipx_source    = pdata->source_addr;
 
 
        /* SPX header */
        /* SPX header */
        ipxh->spx.dtype         = 0;
        ipxh->spx.dtype         = 0;
        ipxh->spx.sequence      = htons(pdata->sequence);
        ipxh->spx.sequence      = htons(pdata->sequence);
        ipxh->spx.ackseq        = htons(pdata->rmt_seq);
        ipxh->spx.ackseq        = htons(pdata->rmt_seq);
        ipxh->spx.sconn         = pdata->source_connid;
        ipxh->spx.sconn         = pdata->source_connid;
        ipxh->spx.dconn         = pdata->dest_connid;
        ipxh->spx.dconn         = pdata->dest_connid;
        ipxh->spx.allocseq      = htons(pdata->alloc);
        ipxh->spx.allocseq      = htons(pdata->alloc);
 
 
        /* Reset/Set WD timer */
        /* Reset/Set WD timer */
        mod_timer(&pdata->watchdog, jiffies+VERIFY_TIMEOUT);
        mod_timer(&pdata->watchdog, jiffies+VERIFY_TIMEOUT);
 
 
        switch(type)
        switch(type)
        {
        {
                case (DATA):    /* Data */
                case (DATA):    /* Data */
                        ipxh->ipx.ipx_pktsize   = htons(SPX_SYS_PKT_LEN + len);
                        ipxh->ipx.ipx_pktsize   = htons(SPX_SYS_PKT_LEN + len);
                        ipxh->spx.cctl          = (CCTL_ACK | CCTL_EOM);
                        ipxh->spx.cctl          = (CCTL_ACK | CCTL_EOM);
                        pdata->sequence++;
                        pdata->sequence++;
                        break;
                        break;
 
 
                case (ACK):     /* ACK */
                case (ACK):     /* ACK */
                        pdata->rmt_seq++;
                        pdata->rmt_seq++;
                case (WDACK):   /* WD ACK */
                case (WDACK):   /* WD ACK */
                case (CONACK):  /* Connection ACK */
                case (CONACK):  /* Connection ACK */
                        ipxh->spx.cctl          = CCTL_SYS;
                        ipxh->spx.cctl          = CCTL_SYS;
                        ipxh->spx.ackseq        = htons(pdata->rmt_seq);
                        ipxh->spx.ackseq        = htons(pdata->rmt_seq);
                        break;
                        break;
 
 
                case (CONREQ):  /* Connection Request */
                case (CONREQ):  /* Connection Request */
                        del_timer(&pdata->watchdog);
                        del_timer(&pdata->watchdog);
                case (WDREQ):   /* WD Request */
                case (WDREQ):   /* WD Request */
                        pdata->source_connid    = htons(connids++);
                        pdata->source_connid    = htons(connids++);
                        pdata->dest_connid      = 0xFFFF;
                        pdata->dest_connid      = 0xFFFF;
                        pdata->alloc            = 3 + pdata->rmt_seq;
                        pdata->alloc            = 3 + pdata->rmt_seq;
                        ipxh->spx.cctl          = (CCTL_ACK | CCTL_SYS);
                        ipxh->spx.cctl          = (CCTL_ACK | CCTL_SYS);
                        ipxh->spx.sconn         = pdata->source_connid;
                        ipxh->spx.sconn         = pdata->source_connid;
                        ipxh->spx.dconn         = pdata->dest_connid;
                        ipxh->spx.dconn         = pdata->dest_connid;
                        ipxh->spx.allocseq      = htons(pdata->alloc);
                        ipxh->spx.allocseq      = htons(pdata->alloc);
                        break;
                        break;
 
 
                case (DISCON):  /* Informed Disconnect */
                case (DISCON):  /* Informed Disconnect */
                        ipxh->spx.cctl          = CCTL_ACK;
                        ipxh->spx.cctl          = CCTL_ACK;
                        ipxh->spx.dtype         = SPX_DTYPE_ECONN;
                        ipxh->spx.dtype         = SPX_DTYPE_ECONN;
                        break;
                        break;
 
 
                case (DISACK):  /* Informed Disconnect ACK */
                case (DISACK):  /* Informed Disconnect ACK */
                        ipxh->spx.cctl          = 0;
                        ipxh->spx.cctl          = 0;
                        ipxh->spx.dtype         = SPX_DTYPE_ECACK;
                        ipxh->spx.dtype         = SPX_DTYPE_ECACK;
                        ipxh->spx.sequence      = 0;
                        ipxh->spx.sequence      = 0;
                        ipxh->spx.ackseq        = htons(pdata->rmt_seq++);
                        ipxh->spx.ackseq        = htons(pdata->rmt_seq++);
                        break;
                        break;
 
 
                default:
                default:
                        return (-EOPNOTSUPP);
                        return (-EOPNOTSUPP);
        }
        }
 
 
        /* Send data */
        /* Send data */
        return (spx_route_skb(pdata, skb, type));
        return (spx_route_skb(pdata, skb, type));
}
}
 
 
/* Check the state of the connection and send a WD request if needed. */
/* Check the state of the connection and send a WD request if needed. */
static void spx_watchdog(unsigned long data)
static void spx_watchdog(unsigned long data)
{
{
        struct sock *sk = (struct sock*)data;
        struct sock *sk = (struct sock*)data;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
 
 
        del_timer(&pdata->watchdog);
        del_timer(&pdata->watchdog);
        if(pdata->state == SPX_CLOSED)
        if(pdata->state == SPX_CLOSED)
                return;
                return;
        if(pdata->retries > pdata->max_retries)
        if(pdata->retries > pdata->max_retries)
        {
        {
                spx_close_socket(sk);   /* Unilateral Abort */
                spx_close_socket(sk);   /* Unilateral Abort */
                return;
                return;
        }
        }
 
 
        /* Send WD request */
        /* Send WD request */
        spx_transmit(sk, NULL, WDREQ, 0);
        spx_transmit(sk, NULL, WDREQ, 0);
        pdata->retries++;
        pdata->retries++;
 
 
        return;
        return;
}
}
 
 
static void spx_retransmit(unsigned long data)
static void spx_retransmit(unsigned long data)
{
{
        struct sock *sk = (struct sock*)data;
        struct sock *sk = (struct sock*)data;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct sk_buff *skb;
        struct sk_buff *skb;
        unsigned long flags;
        unsigned long flags;
        int err;
        int err;
 
 
        del_timer(&pdata->retransmit);
        del_timer(&pdata->retransmit);
        if(pdata->state == SPX_CLOSED)
        if(pdata->state == SPX_CLOSED)
                return;
                return;
        if(pdata->retransmits > RETRY_COUNT)
        if(pdata->retransmits > RETRY_COUNT)
        {
        {
                spx_close_socket(sk);   /* Unilateral Abort */
                spx_close_socket(sk);   /* Unilateral Abort */
                return;
                return;
        }
        }
 
 
        /* Need to leave skb on the queue, aye the fear */
        /* Need to leave skb on the queue, aye the fear */
        save_flags(flags);
        save_flags(flags);
        cli();
        cli();
        skb = skb_peek(&pdata->retransmit_queue);
        skb = skb_peek(&pdata->retransmit_queue);
        if(skb_cloned(skb))
        if(skb_cloned(skb))
                skb = skb_copy(skb, GFP_ATOMIC);
                skb = skb_copy(skb, GFP_ATOMIC);
        else
        else
                skb = skb_clone(skb, GFP_ATOMIC);
                skb = skb_clone(skb, GFP_ATOMIC);
        restore_flags(flags);
        restore_flags(flags);
 
 
        pdata->retransmit.expires = jiffies + spx_calc_rtt(pdata->retransmits);
        pdata->retransmit.expires = jiffies + spx_calc_rtt(pdata->retransmits);
        add_timer(&pdata->retransmit);
        add_timer(&pdata->retransmit);
 
 
        err = spx_route_skb(pdata, skb, RETRAN);
        err = spx_route_skb(pdata, skb, RETRAN);
        pdata->retransmits++;
        pdata->retransmits++;
 
 
        return;
        return;
}
}
 
 
/* Check packet for retransmission, ConReqAck aware */
/* Check packet for retransmission, ConReqAck aware */
static int spx_retransmit_chk(struct spx_opt *pdata, int ackseq, int type)
static int spx_retransmit_chk(struct spx_opt *pdata, int ackseq, int type)
{
{
        struct ipxspxhdr *ipxh;
        struct ipxspxhdr *ipxh;
        struct sk_buff *skb;
        struct sk_buff *skb;
 
 
        skb = skb_dequeue(&pdata->retransmit_queue);
        skb = skb_dequeue(&pdata->retransmit_queue);
        if(!skb)
        if(!skb)
                return (-ENOENT);
                return (-ENOENT);
 
 
        /* Check Data/ACK seq */
        /* Check Data/ACK seq */
        switch(type)
        switch(type)
        {
        {
                case ACK:       /* Check Sequence, Should == 1 */
                case ACK:       /* Check Sequence, Should == 1 */
                        ipxh = (struct ipxspxhdr *)skb->nh.raw;
                        ipxh = (struct ipxspxhdr *)skb->nh.raw;
                        if(!(ntohs(ipxh->spx.sequence) - htons(ackseq)))
                        if(!(ntohs(ipxh->spx.sequence) - htons(ackseq)))
                                break;
                                break;
 
 
                case CONACK:
                case CONACK:
                        del_timer(&pdata->retransmit);
                        del_timer(&pdata->retransmit);
                        pdata->retransmits = 0;
                        pdata->retransmits = 0;
                        kfree_skb(skb);
                        kfree_skb(skb);
                        if(skb_queue_empty(&pdata->retransmit_queue))
                        if(skb_queue_empty(&pdata->retransmit_queue))
                        {
                        {
                                skb = skb_dequeue(&pdata->transmit_queue);
                                skb = skb_dequeue(&pdata->transmit_queue);
                                if(skb != NULL)
                                if(skb != NULL)
                                        spx_route_skb(pdata, skb, TQUEUE);
                                        spx_route_skb(pdata, skb, TQUEUE);
                        }
                        }
                        return (0);
                        return (0);
        }
        }
 
 
        skb_queue_head(&pdata->retransmit_queue, skb);
        skb_queue_head(&pdata->retransmit_queue, skb);
        return (-1);
        return (-1);
}
}
 
 
/* SPX packet receive engine */
/* SPX packet receive engine */
void spx_rcv(struct sock *sk, int bytes)
void spx_rcv(struct sock *sk, int bytes)
{
{
        struct sk_buff *skb;
        struct sk_buff *skb;
        struct ipxspxhdr *ipxh;
        struct ipxspxhdr *ipxh;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
 
 
        skb = skb_dequeue(&sk->receive_queue);
        skb = skb_dequeue(&sk->receive_queue);
        if(skb == NULL)
        if(skb == NULL)
                return;
                return;
        ipxh = (struct ipxspxhdr *)skb->nh.raw;
        ipxh = (struct ipxspxhdr *)skb->nh.raw;
 
 
        /* Can't receive on a closed connection */
        /* Can't receive on a closed connection */
        if((pdata->state == SPX_CLOSED) && (ipxh->spx.sequence != 0))
        if((pdata->state == SPX_CLOSED) && (ipxh->spx.sequence != 0))
                goto toss_skb;
                goto toss_skb;
        if(ntohs(ipxh->ipx.ipx_pktsize) < SPX_SYS_PKT_LEN)
        if(ntohs(ipxh->ipx.ipx_pktsize) < SPX_SYS_PKT_LEN)
                goto toss_skb;
                goto toss_skb;
        if(ipxh->ipx.ipx_type != IPX_TYPE_SPX)
        if(ipxh->ipx.ipx_type != IPX_TYPE_SPX)
                goto toss_skb;
                goto toss_skb;
        if(ntohs(ipxh->spx.ackseq) > pdata->sequence)
        if(ntohs(ipxh->spx.ackseq) > pdata->sequence)
                goto toss_skb;
                goto toss_skb;
 
 
        /* Reset WD timer on any received packet */
        /* Reset WD timer on any received packet */
        del_timer(&pdata->watchdog);
        del_timer(&pdata->watchdog);
        pdata->retries = 0;
        pdata->retries = 0;
        pdata->watchdog.expires = jiffies + ABORT_TIMEOUT;
        pdata->watchdog.expires = jiffies + ABORT_TIMEOUT;
        add_timer(&pdata->watchdog);
        add_timer(&pdata->watchdog);
 
 
        switch(ipxh->spx.cctl)
        switch(ipxh->spx.cctl)
        {
        {
                case (CCTL_SYS | CCTL_ACK):
                case (CCTL_SYS | CCTL_ACK):
                        if((ipxh->spx.sequence == 0)     /* ConReq */
                        if((ipxh->spx.sequence == 0)     /* ConReq */
                                && (ipxh->spx.ackseq == 0)
                                && (ipxh->spx.ackseq == 0)
                                && (ipxh->spx.dconn == 0xFFFF))
                                && (ipxh->spx.dconn == 0xFFFF))
                        {
                        {
                                pdata->state            = SPX_CONNECTED;
                                pdata->state            = SPX_CONNECTED;
                                pdata->dest_addr        = ipxh->ipx.ipx_source;
                                pdata->dest_addr        = ipxh->ipx.ipx_source;
                                pdata->source_addr      = ipxh->ipx.ipx_dest;
                                pdata->source_addr      = ipxh->ipx.ipx_dest;
                                pdata->dest_connid      = ipxh->spx.sconn;
                                pdata->dest_connid      = ipxh->spx.sconn;
                                pdata->alloc = 3 + ntohs(ipxh->spx.sequence);
                                pdata->alloc = 3 + ntohs(ipxh->spx.sequence);
 
 
                                skb_queue_tail(&sk->receive_queue, skb);
                                skb_queue_tail(&sk->receive_queue, skb);
                                wake_up_interruptible(sk->sleep);
                                wake_up_interruptible(sk->sleep);
                        }
                        }
                        else    /* WD Request */
                        else    /* WD Request */
                                spx_transmit(sk, skb, WDACK, 0);
                                spx_transmit(sk, skb, WDACK, 0);
                        goto finish;
                        goto finish;
 
 
                case CCTL_SYS:  /* ACK */
                case CCTL_SYS:  /* ACK */
                        if((ipxh->spx.dtype == 0)       /* ConReq ACK */
                        if((ipxh->spx.dtype == 0)       /* ConReq ACK */
                                && (ipxh->spx.sconn != 0xFFFF)
                                && (ipxh->spx.sconn != 0xFFFF)
                                && (ipxh->spx.dconn != 0xFFFF)
                                && (ipxh->spx.dconn != 0xFFFF)
                                && (ipxh->spx.sequence == 0)
                                && (ipxh->spx.sequence == 0)
                                && (ipxh->spx.ackseq == 0)
                                && (ipxh->spx.ackseq == 0)
                                && (pdata->state != SPX_CONNECTED))
                                && (pdata->state != SPX_CONNECTED))
                        {
                        {
                                pdata->state = SPX_CONNECTED;
                                pdata->state = SPX_CONNECTED;
                                pdata->dest_connid = ipxh->spx.sconn;
                                pdata->dest_connid = ipxh->spx.sconn;
 
 
                                if(spx_retransmit_chk(pdata, 0, CONACK) < 0)
                                if(spx_retransmit_chk(pdata, 0, CONACK) < 0)
                                        goto toss_skb;
                                        goto toss_skb;
 
 
                                skb_queue_tail(&sk->receive_queue, skb);
                                skb_queue_tail(&sk->receive_queue, skb);
                                wake_up_interruptible(sk->sleep);
                                wake_up_interruptible(sk->sleep);
                                goto finish;
                                goto finish;
                        }
                        }
 
 
                        spx_retransmit_chk(pdata, ipxh->spx.ackseq, ACK);
                        spx_retransmit_chk(pdata, ipxh->spx.ackseq, ACK);
                        goto toss_skb;
                        goto toss_skb;
 
 
                case (CCTL_ACK):
                case (CCTL_ACK):
                        /* Informed Disconnect */
                        /* Informed Disconnect */
                        if(ipxh->spx.dtype == SPX_DTYPE_ECONN)
                        if(ipxh->spx.dtype == SPX_DTYPE_ECONN)
                        {
                        {
 
 
                                spx_transmit(sk, skb, DISACK, 0);
                                spx_transmit(sk, skb, DISACK, 0);
                                spx_close_socket(sk);
                                spx_close_socket(sk);
                                goto finish;
                                goto finish;
                        }
                        }
                        /* Fall through */
                        /* Fall through */
 
 
                default:
                default:
                        if(ntohs(ipxh->spx.sequence) == pdata->rmt_seq)
                        if(ntohs(ipxh->spx.sequence) == pdata->rmt_seq)
                        {
                        {
                                pdata->rmt_seq = ntohs(ipxh->spx.sequence);
                                pdata->rmt_seq = ntohs(ipxh->spx.sequence);
                                pdata->rmt_ack = ntohs(ipxh->spx.ackseq);
                                pdata->rmt_ack = ntohs(ipxh->spx.ackseq);
                                pdata->alloc   = pdata->rmt_seq + 3;
                                pdata->alloc   = pdata->rmt_seq + 3;
                                if(pdata->rmt_ack > 0 || pdata->rmt_ack == 0)
                                if(pdata->rmt_ack > 0 || pdata->rmt_ack == 0)
                                        spx_retransmit_chk(pdata,pdata->rmt_ack, ACK);
                                        spx_retransmit_chk(pdata,pdata->rmt_ack, ACK);
 
 
                                skb_queue_tail(&pdata->rcv_queue, skb);
                                skb_queue_tail(&pdata->rcv_queue, skb);
                                wake_up_interruptible(sk->sleep);
                                wake_up_interruptible(sk->sleep);
                                if(ipxh->spx.cctl&CCTL_ACK)
                                if(ipxh->spx.cctl&CCTL_ACK)
                                        spx_transmit(sk, NULL, ACK, 0);
                                        spx_transmit(sk, NULL, ACK, 0);
                                goto finish;
                                goto finish;
                        }
                        }
 
 
                        if(ipxh->spx.dtype == SPX_DTYPE_ECACK)
                        if(ipxh->spx.dtype == SPX_DTYPE_ECACK)
                        {
                        {
                                if(pdata->state != SPX_CLOSED)
                                if(pdata->state != SPX_CLOSED)
                                        spx_close_socket(sk);
                                        spx_close_socket(sk);
                                goto toss_skb;
                                goto toss_skb;
                        }
                        }
        }
        }
 
 
toss_skb:       /* Catch All */
toss_skb:       /* Catch All */
        kfree_skb(skb);
        kfree_skb(skb);
finish:
finish:
        return;
        return;
}
}
 
 
/* Get message/packet data from user-land */
/* Get message/packet data from user-land */
static int spx_sendmsg(struct socket *sock, struct msghdr *msg, int len,
static int spx_sendmsg(struct socket *sock, struct msghdr *msg, int len,
                        struct scm_cookie *scm)
                        struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int flags = msg->msg_flags;
        int flags = msg->msg_flags;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int err, offset, size;
        int err, offset, size;
 
 
        if(len > 534)
        if(len > 534)
                return (-EMSGSIZE);
                return (-EMSGSIZE);
        if(sk->zapped)
        if(sk->zapped)
                return (-ENOTCONN); /* Socket not bound */
                return (-ENOTCONN); /* Socket not bound */
        if(flags&~MSG_DONTWAIT)
        if(flags&~MSG_DONTWAIT)
                return (-EINVAL);
                return (-EINVAL);
 
 
        offset  = ipx_if_offset(sk->tp_pinfo.af_spx.dest_addr.net);
        offset  = ipx_if_offset(sk->tp_pinfo.af_spx.dest_addr.net);
        size    = offset + sizeof(struct ipxspxhdr) + len;
        size    = offset + sizeof(struct ipxspxhdr) + len;
 
 
        cli();
        cli();
        skb     = sock_alloc_send_skb(sk, size, flags&MSG_DONTWAIT, &err);
        skb     = sock_alloc_send_skb(sk, size, flags&MSG_DONTWAIT, &err);
        sti();
        sti();
        if(skb == NULL)
        if(skb == NULL)
                return (err);
                return (err);
 
 
        skb->sk = sk;
        skb->sk = sk;
        skb_reserve(skb, offset);
        skb_reserve(skb, offset);
        skb->h.raw = skb->nh.raw = skb_put(skb, sizeof(struct ipxspxhdr));
        skb->h.raw = skb->nh.raw = skb_put(skb, sizeof(struct ipxspxhdr));
 
 
        err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
        err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
        if(err)
        if(err)
        {
        {
                kfree_skb(skb);
                kfree_skb(skb);
                return (-EFAULT);
                return (-EFAULT);
        }
        }
 
 
        err = spx_transmit(sk, skb, DATA, len);
        err = spx_transmit(sk, skb, DATA, len);
        if(err)
        if(err)
                return (-EAGAIN);
                return (-EAGAIN);
 
 
        return (len);
        return (len);
}
}
 
 
/* Send message/packet data to user-land */
/* Send message/packet data to user-land */
static int spx_recvmsg(struct socket *sock, struct msghdr *msg, int size,
static int spx_recvmsg(struct socket *sock, struct msghdr *msg, int size,
                        int flags, struct scm_cookie *scm)
                        int flags, struct scm_cookie *scm)
{
{
        struct sk_buff *skb;
        struct sk_buff *skb;
        struct ipxspxhdr *ispxh;
        struct ipxspxhdr *ispxh;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
        struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)msg->msg_name;
        struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)msg->msg_name;
        int copied, err;
        int copied, err;
 
 
        if(sk->zapped)
        if(sk->zapped)
                return (-ENOTCONN); /* Socket not bound */
                return (-ENOTCONN); /* Socket not bound */
 
 
        lock_sock(sk);
        lock_sock(sk);
restart:
restart:
        while(skb_queue_empty(&pdata->rcv_queue))      /* No data */
        while(skb_queue_empty(&pdata->rcv_queue))      /* No data */
        {
        {
                /* Socket errors? */
                /* Socket errors? */
                err = sock_error(sk);
                err = sock_error(sk);
                if(err)
                if(err)
                        return (err);
                        return (err);
 
 
                /* Socket shut down? */
                /* Socket shut down? */
                if(sk->shutdown & RCV_SHUTDOWN)
                if(sk->shutdown & RCV_SHUTDOWN)
                        return (-ESHUTDOWN);
                        return (-ESHUTDOWN);
 
 
                /* handle signals */
                /* handle signals */
                if(signal_pending(current))
                if(signal_pending(current))
                        return (-ERESTARTSYS);
                        return (-ERESTARTSYS);
 
 
                /* User doesn't want to wait */
                /* User doesn't want to wait */
                if(flags&MSG_DONTWAIT)
                if(flags&MSG_DONTWAIT)
                        return (-EAGAIN);
                        return (-EAGAIN);
 
 
                release_sock(sk);
                release_sock(sk);
                save_flags(flags);
                save_flags(flags);
                cli();
                cli();
                if(skb_peek(&pdata->rcv_queue) == NULL)
                if(skb_peek(&pdata->rcv_queue) == NULL)
                        interruptible_sleep_on(sk->sleep);
                        interruptible_sleep_on(sk->sleep);
                restore_flags(flags);
                restore_flags(flags);
                lock_sock(sk);
                lock_sock(sk);
        }
        }
 
 
        skb = skb_dequeue(&pdata->rcv_queue);
        skb = skb_dequeue(&pdata->rcv_queue);
        if(skb == NULL)
        if(skb == NULL)
                goto restart;
                goto restart;
 
 
        ispxh   = (struct ipxspxhdr *)skb->nh.raw;
        ispxh   = (struct ipxspxhdr *)skb->nh.raw;
        copied  = ntohs(ispxh->ipx.ipx_pktsize) - SPX_SYS_PKT_LEN;
        copied  = ntohs(ispxh->ipx.ipx_pktsize) - SPX_SYS_PKT_LEN;
        if(copied > size)
        if(copied > size)
        {
        {
                copied = size;
                copied = size;
                msg->msg_flags |= MSG_TRUNC;
                msg->msg_flags |= MSG_TRUNC;
        }
        }
 
 
        err = memcpy_toiovec(msg->msg_iov, skb->nh.raw+SPX_SYS_PKT_LEN, copied);
        err = memcpy_toiovec(msg->msg_iov, skb->nh.raw+SPX_SYS_PKT_LEN, copied);
        if(err)
        if(err)
                return (-EFAULT);
                return (-EFAULT);
 
 
        msg->msg_namelen = sizeof(*sipx);
        msg->msg_namelen = sizeof(*sipx);
        if(sipx)
        if(sipx)
        {
        {
                sipx->sipx_family       = AF_IPX;
                sipx->sipx_family       = AF_IPX;
                sipx->sipx_port         = ispxh->ipx.ipx_source.sock;
                sipx->sipx_port         = ispxh->ipx.ipx_source.sock;
                memcpy(sipx->sipx_node,ispxh->ipx.ipx_source.node,IPX_NODE_LEN);
                memcpy(sipx->sipx_node,ispxh->ipx.ipx_source.node,IPX_NODE_LEN);
                sipx->sipx_network      = ispxh->ipx.ipx_source.net;
                sipx->sipx_network      = ispxh->ipx.ipx_source.net;
                sipx->sipx_type         = ispxh->ipx.ipx_type;
                sipx->sipx_type         = ispxh->ipx.ipx_type;
        }
        }
        kfree_skb(skb);
        kfree_skb(skb);
        release_sock(sk);
        release_sock(sk);
 
 
        return (copied);
        return (copied);
}
}
 
 
/*
/*
 * Functions which just wrap their IPX cousins
 * Functions which just wrap their IPX cousins
 */
 */
 
 
static int spx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
static int spx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
{
        int err;
        int err;
        err = ipx_operations->bind(sock, uaddr, addr_len);
        err = ipx_operations->bind(sock, uaddr, addr_len);
        return (err);
        return (err);
}
}
 
 
static int spx_getname (struct socket *sock, struct sockaddr *uaddr,
static int spx_getname (struct socket *sock, struct sockaddr *uaddr,
                         int *usockaddr_len, int peer)
                         int *usockaddr_len, int peer)
{
{
        int err;
        int err;
        err = ipx_operations->getname(sock, uaddr, usockaddr_len, peer);
        err = ipx_operations->getname(sock, uaddr, usockaddr_len, peer);
        return (err);
        return (err);
}
}
 
 
static int spx_ioctl (struct socket *sock, unsigned int cmd,
static int spx_ioctl (struct socket *sock, unsigned int cmd,
                         unsigned long arg)
                         unsigned long arg)
{
{
        int err;
        int err;
        err = ipx_operations->ioctl(sock, cmd, arg);
        err = ipx_operations->ioctl(sock, cmd, arg);
        return (err);
        return (err);
}
}
 
 
static int spx_setsockopt(struct socket *sock, int level, int optname,
static int spx_setsockopt(struct socket *sock, int level, int optname,
                         char *optval, int optlen)
                         char *optval, int optlen)
{
{
        int err;
        int err;
        err = ipx_operations->setsockopt(sock, level, optname, optval, optlen);
        err = ipx_operations->setsockopt(sock, level, optname, optval, optlen);
        return (err);
        return (err);
}
}
 
 
static int spx_getsockopt(struct socket *sock, int level, int optname,
static int spx_getsockopt(struct socket *sock, int level, int optname,
                         char *optval, int *optlen)
                         char *optval, int *optlen)
{
{
        int err;
        int err;
        err = ipx_operations->getsockopt(sock, level, optname, optval, optlen);
        err = ipx_operations->getsockopt(sock, level, optname, optval, optlen);
        return (err);
        return (err);
}
}
 
 
static struct proto_ops SOCKOPS_WRAPPED(spx_ops) = {
static struct proto_ops SOCKOPS_WRAPPED(spx_ops) = {
        family:         PF_IPX,
        family:         PF_IPX,
 
 
        release:        spx_release,
        release:        spx_release,
        bind:           spx_bind,
        bind:           spx_bind,
        connect:        spx_connect,
        connect:        spx_connect,
        socketpair:     sock_no_socketpair,
        socketpair:     sock_no_socketpair,
        accept:         spx_accept,
        accept:         spx_accept,
        getname:        spx_getname,
        getname:        spx_getname,
        poll:           spx_datagram_poll,
        poll:           spx_datagram_poll,
        ioctl:          spx_ioctl,
        ioctl:          spx_ioctl,
        listen:         spx_listen,
        listen:         spx_listen,
        shutdown:       sock_no_shutdown,
        shutdown:       sock_no_shutdown,
        setsockopt:     spx_setsockopt,
        setsockopt:     spx_setsockopt,
        getsockopt:     spx_getsockopt,
        getsockopt:     spx_getsockopt,
        sendmsg:        spx_sendmsg,
        sendmsg:        spx_sendmsg,
        recvmsg:        spx_recvmsg,
        recvmsg:        spx_recvmsg,
        mmap:           sock_no_mmap,
        mmap:           sock_no_mmap,
        sendpage:       sock_no_sendpage,
        sendpage:       sock_no_sendpage,
};
};
 
 
#include <linux/smp_lock.h>
#include <linux/smp_lock.h>
SOCKOPS_WRAP(spx, PF_IPX);
SOCKOPS_WRAP(spx, PF_IPX);
 
 
static struct net_proto_family spx_family_ops = {
static struct net_proto_family spx_family_ops = {
        family:         PF_IPX,
        family:         PF_IPX,
        create:         spx_create,
        create:         spx_create,
};
};
 
 
static char banner[] __initdata = KERN_INFO "NET4: Sequenced Packet eXchange (SPX) 0.02 for Linux NET4.0\n";
static char banner[] __initdata = KERN_INFO "NET4: Sequenced Packet eXchange (SPX) 0.02 for Linux NET4.0\n";
 
 
static int __init spx_proto_init(void)
static int __init spx_proto_init(void)
{
{
        int error;
        int error;
 
 
        connids = (__u16)jiffies;       /* initalize random */
        connids = (__u16)jiffies;       /* initalize random */
 
 
        error = ipx_register_spx(&ipx_operations, &spx_family_ops);
        error = ipx_register_spx(&ipx_operations, &spx_family_ops);
        if (error)
        if (error)
                printk(KERN_ERR "SPX: unable to register with IPX.\n");
                printk(KERN_ERR "SPX: unable to register with IPX.\n");
 
 
        /* route socket(PF_IPX, SOCK_SEQPACKET) calls through spx_create() */
        /* route socket(PF_IPX, SOCK_SEQPACKET) calls through spx_create() */
 
 
        printk(banner);
        printk(banner);
        return 0;
        return 0;
}
}
module_init(spx_proto_init);
module_init(spx_proto_init);
 
 
static void __exit spx_proto_finito(void)
static void __exit spx_proto_finito(void)
{
{
        ipx_unregister_spx();
        ipx_unregister_spx();
        return;
        return;
}
}
module_exit(spx_proto_finito);
module_exit(spx_proto_finito);
 
 

powered by: WebSVN 2.1.0

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