patch-2.1.96 linux/net/ipx/af_spx.c
Next file: linux/net/netrom/Makefile
Previous file: linux/net/ipx/af_ipx.c
Back to the patch index
Back to the overall index
- Lines: 873
- Date:
Sat Apr 11 17:18:16 1998
- Orig file:
v2.1.95/linux/net/ipx/af_spx.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.95/linux/net/ipx/af_spx.c linux/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 */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov