summaryrefslogtreecommitdiffstats
path: root/net/ipx
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
commitdcec8a13bf565e47942a1751a9cec21bec5648fe (patch)
tree548b69625b18cc2e88c3e68d0923be546c9ebb03 /net/ipx
parent2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff)
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash. o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'net/ipx')
-rw-r--r--net/ipx/Config.in6
-rw-r--r--net/ipx/Makefile8
-rw-r--r--net/ipx/af_ipx.c84
-rw-r--r--net/ipx/af_spx.c872
4 files changed, 953 insertions, 17 deletions
diff --git a/net/ipx/Config.in b/net/ipx/Config.in
index d35afbac0..17080b0c6 100644
--- a/net/ipx/Config.in
+++ b/net/ipx/Config.in
@@ -2,5 +2,7 @@
# IPX configuration
#
-comment 'IPX options'
-bool 'Full internal IPX network' CONFIG_IPX_INTERN
+bool 'IPX: Full internal IPX network' CONFIG_IPX_INTERN
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate 'IPX: SPX networking (EXPERIMENTAL)' CONFIG_SPX $CONFIG_IPX
+fi
diff --git a/net/ipx/Makefile b/net/ipx/Makefile
index b9d337a8a..39639c6dc 100644
--- a/net/ipx/Makefile
+++ b/net/ipx/Makefile
@@ -17,6 +17,14 @@ ifeq ($(CONFIG_SYSCTL),y)
O_OBJS += sysctl_net_ipx.o
endif
+ifeq ($(CONFIG_SPX),y)
+OX_OBJS += af_spx.o
+else
+ ifeq ($(CONFIG_SPX),m)
+ MX_OBJS += af_spx.o
+ endif
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 904fa1174..f035e8c62 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1,5 +1,5 @@
/*
- * Implements an IPX socket layer (badly - but I'm working on it).
+ * Implements an IPX socket layer.
*
* This code is derived from work by
* Ross Biro : Writing the original IP stack
@@ -47,6 +47,8 @@
* Revision 0.36: Internal bump up for 2.1
* Revision 0.37: Began adding POSIXisms.
* Revision 0.38: Asynchronous socket stuff made current.
+ * Revision 0.39: SPX interfaces
+ * Revision 0.40: Tiny SIOCGSTAMP fix (chris@cybernet.co.nz)
*
* Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
* pair. Also, now usage count is managed this way
@@ -111,6 +113,8 @@ static struct datalink_proto *pSNAP_datalink = NULL;
static struct proto_ops ipx_dgram_ops;
+static struct net_proto_family *spx_family_ops;
+
static ipx_route *ipx_routes = NULL;
static ipx_interface *ipx_interfaces = NULL;
static ipx_interface *ipx_primary_net = NULL;
@@ -163,7 +167,7 @@ static int ipxcfg_get_config_data(ipx_config_data *arg)
* use this facility.
*/
-static void ipx_remove_socket(struct sock *sk)
+void ipx_remove_socket(struct sock *sk)
{
struct sock *s;
ipx_interface *intrfc;
@@ -624,6 +628,14 @@ static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
if (ipx->ipx_source.net != intrfc->if_netnum)
{
+ /*
+ * Unshare the buffer before modifying the count in
+ * case its a flood or tcpdump
+ */
+ skb=skb_unshare(skb, GFP_ATOMIC);
+ if(!skb)
+ return 0;
+ ipx = skb->nh.ipxh;
if (++(ipx->ipx_tctrl) > ipxcfg_max_hops)
send_to_wire = 0;
}
@@ -722,7 +734,7 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
}
}
- if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 && skb->pkt_type == PACKET_HOST )
+ if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 && skb->pkt_type != PACKET_OTHERHOST )
{
int i;
ipx_interface *ifcs;
@@ -762,8 +774,8 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)==FW_ACCEPT)
{
skb2 = skb_clone(skb, GFP_ATOMIC);
- ipxrtr_route_skb(skb2);
- }
+ ipxrtr_route_skb(skb2);
+ }
}
}
/*
@@ -1264,6 +1276,9 @@ static __u16 ipx_set_checksum(struct ipxhdr *packet,int length)
*/
__u32 i=length>>1;
+ char hops = packet->ipx_tctrl;
+
+ packet->ipx_tctrl = 0; /* hop count excluded from checksum calc */
/*
* Loop through all complete words except the checksum field
@@ -1279,6 +1294,7 @@ static __u16 ipx_set_checksum(struct ipxhdr *packet,int length)
if(packet->ipx_pktsize&htons(1))
sum+=ntohs(0xff00)&*p;
+ packet->ipx_tctrl = hops;
/*
* Do final fixup
*/
@@ -1713,19 +1729,24 @@ static int ipx_getsockopt(struct socket *sock, int level, int optname,
static int ipx_create(struct socket *sock, int protocol)
{
struct sock *sk;
- sk=sk_alloc(AF_IPX, GFP_KERNEL, 1);
- if(sk==NULL)
- return(-ENOMEM);
switch(sock->type)
{
case SOCK_DGRAM:
+ sk=sk_alloc(AF_IPX, GFP_KERNEL, 1);
+ if(sk==NULL)
+ return(-ENOMEM);
sock->ops = &ipx_dgram_ops;
break;
- case SOCK_STREAM: /* Allow higher levels to piggyback */
case SOCK_SEQPACKET:
- printk(KERN_CRIT "IPX: _create-ing non_DGRAM socket\n");
+ /*
+ * From this point on SPX sockets are handled
+ * by af_spx.c and the methods replaced.
+ */
+ if(spx_family_ops)
+ return spx_family_ops->create(sock,protocol);
+ /* Fall through if SPX is not loaded */
+ case SOCK_STREAM: /* Allow higher levels to piggyback */
default:
- sk_free(sk);
return(-ESOCKTNOSUPPORT);
}
sock_init_data(sock,sk);
@@ -2157,6 +2178,7 @@ static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size,
copied);
if (err)
goto out_free;
+ sk->stamp=skb->stamp;
msg->msg_namelen = sizeof(*sipx);
@@ -2249,6 +2271,34 @@ static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
return(0);
}
+/*
+ * SPX interface support
+ */
+
+int ipx_register_spx(struct proto_ops **p, struct net_proto_family *spx)
+{
+ if(spx_family_ops!=NULL)
+ return -EBUSY;
+ cli();
+ MOD_INC_USE_COUNT;
+ *p=&ipx_dgram_ops;
+ spx_family_ops=spx;
+ sti();
+ return 0;
+}
+
+int ipx_unregister_spx(void)
+{
+ spx_family_ops=NULL;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+/*
+ * Socket family declarations
+ */
+
static struct net_proto_family ipx_family_ops = {
AF_IPX,
ipx_create
@@ -2256,7 +2306,6 @@ static struct net_proto_family ipx_family_ops = {
static struct proto_ops ipx_dgram_ops = {
AF_IPX,
-
sock_no_dup,
ipx_release,
ipx_bind,
@@ -2280,7 +2329,7 @@ static struct proto_ops ipx_dgram_ops = {
static struct packet_type ipx_8023_packet_type =
{
- 0, /* MUTTER ntohs(ETH_P_8023),*/
+ 0, /* MUTTER ntohs(ETH_P_802_3),*/
NULL, /* All devices */
ipx_rcv,
NULL,
@@ -2371,6 +2420,10 @@ int ipx_if_offset(unsigned long ipx_net_number)
/* Export symbols for higher layers */
EXPORT_SYMBOL(ipxrtr_route_skb);
EXPORT_SYMBOL(ipx_if_offset);
+EXPORT_SYMBOL(ipx_remove_socket);
+EXPORT_SYMBOL(ipx_register_spx);
+EXPORT_SYMBOL(ipx_unregister_spx);
+
#ifdef MODULE
/* Note on MOD_{INC,DEC}_USE_COUNT:
@@ -2386,8 +2439,9 @@ EXPORT_SYMBOL(ipx_if_offset);
* sockets be closed from user space.
*/
-__initfunc(static void ipx_proto_finito(void))
-{ ipx_interface *ifc;
+static void ipx_proto_finito(void)
+{
+ ipx_interface *ifc;
while (ipx_interfaces) {
ifc = ipx_interfaces;
diff --git a/net/ipx/af_spx.c b/net/ipx/af_spx.c
new file mode 100644
index 000000000..a14ad0a31
--- /dev/null
+++ b/net/ipx/af_spx.c
@@ -0,0 +1,872 @@
+/*
+ * This module implements the (SPP-derived) Sequenced Packet eXchange
+ * (SPX) protocol for Linux 2.1.X as specified in
+ * NetWare SPX Services Specification, Semantics and API
+ * Revision: 1.00
+ * Revision Date: February 9, 1993
+ *
+ * Developers:
+ * Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ * Jim Freeman <jfree@caldera.com>
+ *
+ * Changes:
+ * Alan Cox : Fixed an skb_unshare check for NULL
+ * that crashed it under load. Renamed and
+ * made static the ipx ops. Removed the hack
+ * ipx methods interface. Dropped AF_SPX - its
+ * the wrong abstraction.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * None of the authors or maintainers or their employers admit
+ * liability nor provide warranty for any of this software.
+ * This material is provided "as is" and at no charge.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_SPX) || defined(CONFIG_SPX_MODULE)
+#include <linux/module.h>
+#include <net/ipx.h>
+#include <net/spx.h>
+#include <net/sock.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <linux/uio.h>
+#include <linux/unistd.h>
+#include <linux/firewall.h>
+
+static struct proto_ops *ipx_operations;
+static struct proto_ops spx_operations;
+static __u16 connids;
+
+/* Functions needed for SPX connection start up */
+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_watchdog(unsigned long data);
+void spx_rcv(struct sock *sk, int bytes);
+
+/* Create the SPX specific data */
+static int spx_sock_init(struct sock *sk)
+{
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+
+ pdata->state = SPX_CLOSED;
+ pdata->sequence = 0;
+ pdata->acknowledge = 0;
+ pdata->source_connid = htons(connids);
+ pdata->rmt_seq = 0;
+ connids++;
+
+ pdata->owner = (void *)sk;
+ pdata->sndbuf = sk->sndbuf;
+
+ pdata->watchdog.function = spx_watchdog;
+ pdata->watchdog.data = (unsigned long)sk;
+ pdata->wd_interval = VERIFY_TIMEOUT;
+ pdata->retransmit.function = spx_retransmit;
+ pdata->retransmit.data = (unsigned long)sk;
+ pdata->retransmits = 0;
+ pdata->retries = 0;
+ pdata->max_retries = RETRY_COUNT;
+
+ skb_queue_head_init(&pdata->rcv_queue);
+ skb_queue_head_init(&pdata->transmit_queue);
+ skb_queue_head_init(&pdata->retransmit_queue);
+
+ return (0);
+}
+
+static int spx_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ sk = sk_alloc(AF_IPX, GFP_KERNEL, 1);
+ if(sk == NULL)
+ return (-ENOMEM);
+
+ switch(sock->type)
+ {
+ case SOCK_SEQPACKET:
+ sock->ops = &spx_operations;
+ break;
+ default:
+ sk_free(sk);
+ return (-ESOCKTNOSUPPORT);
+ }
+
+ sock_init_data(sock, sk);
+ spx_sock_init(sk);
+ sk->data_ready = spx_rcv;
+ sk->destruct = NULL;
+ sk->mtu = IPX_MTU;
+ sk->no_check = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return (0);
+}
+
+static int spx_shutdown(struct socket *sk,int how)
+{
+ return (-EOPNOTSUPP);
+}
+
+void spx_close_socket(struct sock *sk)
+{
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+
+ pdata->state = SPX_CLOSED;
+ sk->state = TCP_CLOSE;
+ del_timer(&pdata->retransmit);
+ del_timer(&pdata->watchdog);
+}
+
+void spx_destroy_socket(struct sock *sk)
+{
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+ struct sk_buff *skb;
+
+ ipx_remove_socket(sk);
+ while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
+ kfree_skb(skb);
+ while((skb = skb_dequeue(&pdata->transmit_queue)) != NULL)
+ kfree_skb(skb);
+ while((skb = skb_dequeue(&pdata->retransmit_queue)) != NULL)
+ kfree_skb(skb);
+ while((skb = skb_dequeue(&pdata->rcv_queue)) != NULL)
+ kfree_skb(skb);
+
+ sk_free(sk);
+ MOD_DEC_USE_COUNT;
+}
+
+/* Release an SPX socket */
+static int spx_release(struct socket *sock, struct socket *peer)
+{
+ struct sock *sk = sock->sk;
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+
+ if(sk == NULL)
+ return (0);
+ if(!sk->dead)
+ sk->state_change(sk);
+ sk->dead = 1;
+
+ if(pdata->state != SPX_CLOSED)
+ {
+ spx_transmit(sk, NULL, DISCON, 0);
+ spx_close_socket(sk);
+ }
+
+ sock->sk = NULL;
+ sk->socket = NULL;
+ spx_destroy_socket(sk);
+
+ return (0);
+}
+
+/* Move a socket into listening state. */
+static int spx_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ if(sock->state != SS_UNCONNECTED)
+ return (-EINVAL);
+ if(sock->type != SOCK_SEQPACKET)
+ return (-EOPNOTSUPP);
+ if(sk->zapped != 0)
+ return (-EAGAIN);
+
+ if((unsigned) backlog == 0) /* BSDism */
+ backlog = 1;
+ if((unsigned) backlog > SOMAXCONN)
+ backlog = SOMAXCONN;
+ sk->max_ack_backlog = backlog;
+ if(sk->state != TCP_LISTEN)
+ {
+ sk->ack_backlog = 0;
+ sk->state = TCP_LISTEN;
+ }
+ sk->socket->flags |= SO_ACCEPTCON;
+
+ return (0);
+}
+
+/* Accept a pending SPX connection */
+static int spx_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk;
+ struct sock *newsk;
+ struct sk_buff *skb;
+ int err;
+
+ if(newsock->sk != NULL)
+ spx_destroy_socket(newsock->sk);
+ newsock->sk = NULL;
+
+ if(sock->sk == NULL)
+ return (-EINVAL);
+ sk = sock->sk;
+
+ if((sock->state != SS_UNCONNECTED) || !(sock->flags & SO_ACCEPTCON))
+ return (-EINVAL);
+ if(sock->type != SOCK_SEQPACKET)
+ return (-EOPNOTSUPP);
+ if(sk->state != TCP_LISTEN)
+ return (-EINVAL);
+
+ cli();
+ do {
+ skb = skb_dequeue(&sk->receive_queue);
+ if(skb == NULL)
+ {
+ if(flags & O_NONBLOCK)
+ {
+ sti();
+ return (-EWOULDBLOCK);
+ }
+ interruptible_sleep_on(sk->sleep);
+ if(signal_pending(current))
+ {
+ sti();
+ return (-ERESTARTSYS);
+ }
+ }
+ } while (skb == NULL);
+
+ newsk = skb->sk;
+ newsk->pair = NULL;
+ sti();
+
+ err = spx_transmit(newsk, skb, CONACK, 0); /* Connection ACK */
+ if(err)
+ return (err);
+
+ /* Now attach up the new socket */
+ sock->sk = NULL;
+ sk->ack_backlog--;
+ newsock->sk = newsk;
+ newsk->state = TCP_ESTABLISHED;
+ newsk->protinfo.af_ipx.dest_addr = newsk->tp_pinfo.af_spx.dest_addr;
+
+ return (0);
+}
+
+/* Build a connection to an SPX socket */
+static int spx_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+ struct sockaddr_ipx src;
+ struct sk_buff *skb;
+ int size, err;
+
+ size = sizeof(src);
+ err = ipx_operations->getname(sock, (struct sockaddr *)&src, &size, 0);
+ if(err)
+ return (err);
+
+ pdata->source_addr.net = src.sipx_network;
+ memcpy(pdata->source_addr.node, src.sipx_node, IPX_NODE_LEN);
+ pdata->source_addr.sock = (unsigned short)src.sipx_port;
+
+ err = ipx_operations->connect(sock, uaddr, addr_len, flags);
+ if(err)
+ return (err);
+
+ pdata->dest_addr = sk->protinfo.af_ipx.dest_addr;
+ pdata->state = SPX_CONNECTING;
+ sock->state = SS_CONNECTING;
+ sk->state = TCP_SYN_SENT;
+
+ /* Send Connection request */
+ err = spx_transmit(sk, NULL, CONREQ, 0);
+ if(err)
+ return (err);
+
+ cli();
+ do {
+ skb = skb_dequeue(&sk->receive_queue);
+ if(skb == NULL)
+ {
+ if(flags & O_NONBLOCK)
+ {
+ sti();
+ return (-EWOULDBLOCK);
+ }
+ interruptible_sleep_on(sk->sleep);
+ if(signal_pending(current))
+ {
+ sti();
+ return (-ERESTARTSYS);
+ }
+ }
+ } while (skb == NULL);
+
+ if(pdata->state == SPX_CLOSED)
+ {
+ sti();
+ del_timer(&pdata->watchdog);
+ return (-ETIMEDOUT);
+ }
+
+ sock->state = SS_CONNECTED;
+ sk->state = TCP_ESTABLISHED;
+ kfree_skb(skb);
+ sti();
+
+ return (0);
+}
+
+/*
+ * Calculate the timeout for a packet. Thankfully SPX has a large
+ * 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
+ * time of 5*HZ. Between those values we increase the timeout based
+ * on the number of retransmit tries.
+ */
+static inline unsigned long spx_calc_rtt(int tries)
+{
+ if(tries < 1)
+ return (RETRY_TIME);
+ if(tries > 5)
+ return (MAX_RETRY_DELAY);
+ return (tries * HZ);
+}
+
+static int spx_route_skb(struct spx_opt *pdata, struct sk_buff *skb, int type)
+{
+ struct sk_buff *skb2;
+ int err = 0;
+
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if(skb==NULL)
+ return -ENOBUFS;
+
+ switch(type)
+ {
+ case (DATA):
+ if(!skb_queue_empty(&pdata->retransmit_queue))
+ {
+ skb_queue_tail(&pdata->transmit_queue, skb);
+ return 0;
+ }
+
+ case (TQUEUE):
+ pdata->retransmit.expires = jiffies + spx_calc_rtt(0);
+ add_timer(&pdata->retransmit);
+
+ skb2 = skb_clone(skb, GFP_BUFFER);
+ if(skb2 == NULL)
+ return -ENOBUFS;
+ skb_queue_tail(&pdata->retransmit_queue, skb2);
+
+ case (ACK):
+ case (CONREQ):
+ case (CONACK):
+ case (WDREQ):
+ case (WDACK):
+ case (DISCON):
+ case (DISACK):
+ case (RETRAN):
+ default:
+ /* Send data */
+ err = ipxrtr_route_skb(skb);
+ if(err)
+ kfree_skb(skb);
+ }
+
+ return (err);
+}
+
+/* SPX packet transmit engine */
+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 ipxspxhdr *ipxh;
+ int flags, err;
+
+ if(skb == NULL)
+ {
+ int offset = ipx_if_offset(pdata->dest_addr.net);
+ int size = offset + sizeof(struct ipxspxhdr);
+
+ save_flags(flags);
+ cli();
+ skb = sock_alloc_send_skb(sk, size, 0, 0, &err);
+ if(skb == NULL)
+ return (-ENOMEM);
+ skb_reserve(skb, offset);
+ skb->nh.raw = skb_put(skb, sizeof(struct ipxspxhdr));
+ restore_flags(flags);
+ }
+
+ /* IPX header */
+ ipxh = (struct ipxspxhdr *)skb->nh.raw;
+ ipxh->ipx.ipx_checksum = 0xFFFF;
+ ipxh->ipx.ipx_pktsize = htons(SPX_SYS_PKT_LEN);
+ ipxh->ipx.ipx_tctrl = 0;
+ ipxh->ipx.ipx_type = IPX_TYPE_SPX;
+ ipxh->ipx.ipx_dest = pdata->dest_addr;
+ ipxh->ipx.ipx_source = pdata->source_addr;
+
+ /* SPX header */
+ ipxh->spx.dtype = 0;
+ ipxh->spx.sequence = htons(pdata->sequence);
+ ipxh->spx.ackseq = htons(pdata->rmt_seq);
+ ipxh->spx.sconn = pdata->source_connid;
+ ipxh->spx.dconn = pdata->dest_connid;
+ ipxh->spx.allocseq = htons(pdata->alloc);
+
+ /* Reset/Set WD timer */
+ del_timer(&pdata->watchdog);
+ pdata->watchdog.expires = jiffies + VERIFY_TIMEOUT;
+ add_timer(&pdata->watchdog);
+
+ switch(type)
+ {
+ case (DATA): /* Data */
+ ipxh->ipx.ipx_pktsize = htons(SPX_SYS_PKT_LEN + len);
+ ipxh->spx.cctl = (CCTL_ACK | CCTL_EOM);
+ pdata->sequence++;
+ break;
+
+ case (ACK): /* Connection/WD/Data ACK */
+ pdata->rmt_seq++;
+ case (WDACK):
+ case (CONACK):
+ ipxh->spx.cctl = CCTL_SYS;
+ ipxh->spx.ackseq = htons(pdata->rmt_seq);
+ break;
+
+ case (CONREQ): /* Connection Request */
+ del_timer(&pdata->watchdog);
+ case (WDREQ): /* WD Request */
+ pdata->source_connid = htons(connids++);
+ pdata->dest_connid = 0xFFFF;
+ pdata->alloc = 3 + pdata->rmt_seq;
+ ipxh->spx.cctl = (CCTL_ACK | CCTL_SYS);
+ ipxh->spx.sconn = pdata->source_connid;
+ ipxh->spx.dconn = pdata->dest_connid;
+ ipxh->spx.allocseq = htons(pdata->alloc);
+ break;
+
+ case (DISCON): /* Informed Disconnect */
+ ipxh->spx.cctl = CCTL_ACK;
+ ipxh->spx.dtype = SPX_DTYPE_ECONN;
+ break;
+
+ case (DISACK): /* Informed Disconnect ACK */
+ ipxh->spx.cctl = 0;
+ ipxh->spx.dtype = SPX_DTYPE_ECACK;
+ ipxh->spx.sequence = 0;
+ ipxh->spx.ackseq = htons(pdata->rmt_seq++);
+ break;
+
+ default:
+ return (-EOPNOTSUPP);
+ }
+
+ /* Send data */
+ spx_route_skb(pdata, skb, type);
+
+ return (0);
+}
+
+/* Check the state of the connection and send a WD request if needed. */
+static void spx_watchdog(unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+
+ del_timer(&pdata->watchdog);
+ if(pdata->retries > pdata->max_retries)
+ {
+ spx_close_socket(sk); /* Unilateral Abort */
+ return;
+ }
+
+ /* Send WD request */
+ spx_transmit(sk, NULL, WDREQ, 0);
+ pdata->retries++;
+
+ return;
+}
+
+static void spx_retransmit(unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+ struct sk_buff *skb;
+ int err;
+
+ del_timer(&pdata->retransmit);
+ if(pdata->retransmits > RETRY_COUNT)
+ {
+ spx_close_socket(sk); /* Unilateral Abort */
+ return;
+ }
+
+ /* need to leave skb on the queue! */
+ skb = skb_peek(&pdata->retransmit_queue);
+ if(skb_cloned(skb))
+ skb = skb_copy(skb, GFP_ATOMIC);
+ else
+ skb = skb_clone(skb, GFP_ATOMIC);
+
+ pdata->retransmit.expires = jiffies + spx_calc_rtt(pdata->retransmits);
+ add_timer(&pdata->retransmit);
+
+ err = spx_route_skb(pdata, skb, RETRAN);
+ pdata->retransmits++;
+
+ return;
+}
+
+/* SPX packet receive engine */
+void spx_rcv(struct sock *sk, int bytes)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ struct ipxspxhdr *ipxh;
+ struct ipxspxhdr *ipxh2;
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+
+ skb = skb_dequeue(&sk->receive_queue);
+ if(skb == NULL)
+ return;
+ ipxh = (struct ipxspxhdr *)skb->nh.raw;
+
+ /* Can't receive on a closed connection */
+ if((pdata->state == SPX_CLOSED) && (ipxh->spx.sequence != 0))
+ return;
+ if(ntohs(ipxh->ipx.ipx_pktsize) < SPX_SYS_PKT_LEN)
+ return;
+ if(ipxh->ipx.ipx_type != IPX_TYPE_SPX)
+ return;
+
+ /* insanity - rcv'd ACK of unsent data ?? */
+ if(ntohs(ipxh->spx.ackseq) > pdata->sequence)
+ return;
+
+ /* Reset WD timer on any received packet */
+ del_timer(&pdata->watchdog);
+ pdata->retries = 0;
+ pdata->watchdog.expires = jiffies + ABORT_TIMEOUT;
+ add_timer(&pdata->watchdog);
+
+ switch(ipxh->spx.cctl)
+ {
+ case (CCTL_SYS | CCTL_ACK):
+ if((ipxh->spx.sequence == 0) /* ConReq */
+ && (ipxh->spx.ackseq == 0)
+ && (ipxh->spx.dconn == 0xFFFF))
+ {
+ pdata->state = SPX_CONNECTED;
+ pdata->dest_addr = ipxh->ipx.ipx_source;
+ pdata->source_addr = ipxh->ipx.ipx_dest;
+ pdata->dest_connid = ipxh->spx.sconn;
+ pdata->alloc = 3 + ntohs(ipxh->spx.sequence);
+
+ skb_queue_tail(&sk->receive_queue, skb);
+ wake_up_interruptible(sk->sleep);
+ }
+ else /* WD Request */
+ spx_transmit(sk, skb, WDACK, 0);
+ break;
+
+ case CCTL_SYS: /* ACK */
+ if((ipxh->spx.dtype == 0) /* ConReq ACK */
+ && (ipxh->spx.sconn != 0xFFFF)
+ && (ipxh->spx.dconn != 0xFFFF)
+ && (ipxh->spx.sequence == 0)
+ && (ipxh->spx.ackseq == 0)
+ && (pdata->state != SPX_CONNECTED))
+ {
+ pdata->state = SPX_CONNECTED;
+
+ skb_queue_tail(&sk->receive_queue, skb);
+ wake_up_interruptible(sk->sleep);
+ break;
+ }
+
+ /* Check Data/ACK seq */
+ skb2 = skb_dequeue(&pdata->retransmit_queue);
+ if(skb2)
+ {
+ ipxh2 = (struct ipxspxhdr *)skb2->nh.raw;
+ if((ntohs(ipxh2->spx.sequence)
+ == (ntohs(ipxh->spx.ackseq) - 1))
+ || (ntohs(ipxh2->spx.sequence) == 65535
+ && ntohs(ipxh->spx.ackseq) == 0))
+ {
+ del_timer(&pdata->retransmit);
+ pdata->retransmits = 0;
+ kfree_skb(skb2);
+ if(skb_queue_empty(&pdata->retransmit_queue))
+ {
+ skb2 = skb_dequeue(&pdata->transmit_queue);
+ if(skb2 != NULL)
+ spx_route_skb(pdata, skb2, TQUEUE);
+ }
+ }
+ else /* Out of Seq - ERROR! */
+ skb_queue_head(&pdata->retransmit_queue, skb2);
+ }
+
+ kfree_skb(skb);
+ break;
+
+ case (CCTL_ACK): /* Informed Disconnect */
+ if(ipxh->spx.dtype == SPX_DTYPE_ECONN)
+ {
+ spx_transmit(sk, skb, DISACK, 0);
+ spx_close_socket(sk);
+ }
+ break;
+
+ default:
+ if(ntohs(ipxh->spx.sequence) == pdata->rmt_seq)
+ {
+ pdata->rmt_seq = ntohs(ipxh->spx.sequence);
+ skb_queue_tail(&pdata->rcv_queue, skb);
+ wake_up_interruptible(sk->sleep);
+ spx_transmit(sk, NULL, ACK, 0);
+ break;
+ }
+
+ /* Catch All */
+ kfree_skb(skb);
+ break;
+ }
+
+ return;
+}
+
+/* Get message/packet data from user-land */
+static int spx_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ int flags = msg->msg_flags;
+ struct sk_buff *skb;
+ int err, offset, size;
+
+ if(len > 534)
+ return (-EMSGSIZE);
+ if(sk->zapped)
+ return (-ENOTCONN); /* Socket not bound */
+ if(flags&~MSG_DONTWAIT)
+ return (-EINVAL);
+
+ offset = ipx_if_offset(sk->tp_pinfo.af_spx.dest_addr.net);
+ size = offset + sizeof(struct ipxspxhdr) + len;
+ skb = sock_alloc_send_skb(sk, size, 0, flags&MSG_DONTWAIT, &err);
+ if(skb == NULL)
+ return (err);
+
+ skb->sk = sk;
+ skb_reserve(skb, offset);
+ skb->nh.raw = skb_put(skb, sizeof(struct ipxspxhdr));
+
+ err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if(err)
+ {
+ kfree_skb(skb);
+ return (-EFAULT);
+ }
+
+ err = spx_transmit(sk, skb, DATA, len);
+ if(err)
+ return (-EAGAIN);
+
+ return (len);
+}
+
+/* Send message/packet data to user-land */
+static int spx_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
+{
+ struct sk_buff *skb;
+ struct ipxspxhdr *ispxh;
+ struct sock *sk = sock->sk;
+ struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)msg->msg_name;
+ int copied, err;
+
+ if(sk->zapped)
+ return (-ENOTCONN); /* Socket not bound */
+
+ lock_sock(sk);
+restart:
+ while(skb_queue_empty(&pdata->rcv_queue)) /* No data */
+ {
+ /* Socket errors? */
+ err = sock_error(sk);
+ if(err)
+ return (err);
+
+ /* Socket shut down? */
+ if(sk->shutdown & RCV_SHUTDOWN)
+ return (-ESHUTDOWN);
+
+ /* handle signals */
+ if(signal_pending(current))
+ return (-ERESTARTSYS);
+
+ /* User doesn't want to wait */
+ if(flags&MSG_DONTWAIT)
+ return (-EAGAIN);
+
+ release_sock(sk);
+ save_flags(flags);
+ cli();
+ if(skb_peek(&pdata->rcv_queue) == NULL)
+ interruptible_sleep_on(sk->sleep);
+ restore_flags(flags);
+ lock_sock(sk);
+ }
+
+ skb = skb_dequeue(&pdata->rcv_queue);
+ if(skb == NULL)
+ goto restart;
+
+ ispxh = (struct ipxspxhdr *)skb->nh.raw;
+ copied = ntohs(ispxh->ipx.ipx_pktsize) - SPX_SYS_PKT_LEN;
+ if(copied > size)
+ {
+ copied = size;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+ err = memcpy_toiovec(msg->msg_iov, skb->nh.raw+SPX_SYS_PKT_LEN, copied);
+ if(err)
+ return (-EFAULT);
+
+ msg->msg_namelen = sizeof(*sipx);
+ if(sipx)
+ {
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_port = ispxh->ipx.ipx_source.sock;
+ memcpy(sipx->sipx_node,ispxh->ipx.ipx_source.node,IPX_NODE_LEN);
+ sipx->sipx_network = ispxh->ipx.ipx_source.net;
+ sipx->sipx_type = ispxh->ipx.ipx_type;
+ }
+ kfree_skb(skb);
+ release_sock(sk);
+
+ return (copied);
+}
+
+/*
+ * Functions which just wrap their IPX cousins
+ */
+
+static int spx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ int err;
+ err = ipx_operations->bind(sock, uaddr, addr_len);
+ return (err);
+}
+
+static int spx_getname (struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer)
+{
+ int err;
+ err = ipx_operations->getname(sock, uaddr, usockaddr_len, peer);
+ return (err);
+}
+
+static int spx_ioctl (struct socket *sock, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+ err = ipx_operations->ioctl(sock, cmd, arg);
+ return (err);
+}
+
+static int spx_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ int err;
+ err = ipx_operations->setsockopt(sock, level, optname, optval, optlen);
+ return (err);
+}
+
+static int spx_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ int err;
+ err = ipx_operations->getsockopt(sock, level, optname, optval, optlen);
+ return (err);
+}
+
+static struct proto_ops spx_operations = {
+ AF_IPX,
+ sock_no_dup,
+ spx_release,
+ spx_bind,
+ spx_connect,
+ sock_no_socketpair,
+ spx_accept,
+ spx_getname,
+ datagram_poll, /* this does seqpacket too */
+ spx_ioctl,
+ spx_listen,
+ spx_shutdown,
+ spx_setsockopt,
+ spx_getsockopt,
+ sock_no_fcntl,
+ spx_sendmsg,
+ spx_recvmsg
+};
+
+static struct net_proto_family spx_family_ops=
+{
+ AF_IPX,
+ spx_create
+};
+
+
+void spx_proto_init(void)
+{
+ int error;
+
+ connids = (__u16)jiffies; /* initalize random */
+
+ error = ipx_register_spx(&ipx_operations, &spx_family_ops);
+ if (error)
+ printk(KERN_ERR "SPX: unable to register with IPX.\n");
+
+ /* route socket(AF_IPX, SOCK_SEQPACKET) calls through spx_create() */
+
+ printk(KERN_INFO "Sequenced Packet eXchange (SPX) 0.01 for Linux NET3.037\n");
+ return;
+}
+
+void spx_proto_finito(void)
+{
+ ipx_unregister_spx();
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ spx_proto_init();
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ spx_proto_finito();
+ return;
+}
+
+#endif /* MODULE */
+#endif /* CONFIG_SPX || CONFIG_SPX_MODULE */