af_spx.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:24k
- /*
- * 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 <jschlst@samba.org>
- * 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.
- * Eduardo Trapani : Added a check for the return value of
- * ipx_if_offset that crashed sock_alloc_send_skb.
- * Added spx_datagram_poll() so that select()
- * works now on SPX sockets. Added updating
- * of the alloc count to follow rmt_seq.
- *
- * 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/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/poll.h>
- static struct proto_ops *ipx_operations;
- static struct proto_ops spx_ops;
- 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);
- extern void ipx_remove_socket(struct sock *sk);
- /* Datagram poll: the same code as datagram_poll() in net/core
- but the right spx buffers are looked at and
- 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)
- {
- struct sock *sk = sock->sk;
- struct spx_opt *pdata = &sk->tp_pinfo.af_spx;
- unsigned int mask;
- poll_wait(file, sk->sleep, wait);
- mask = 0;
- /* exceptional events? */
- if (sk->err || !skb_queue_empty(&sk->error_queue))
- mask |= POLLERR;
- if (sk->shutdown & RCV_SHUTDOWN)
- mask |= POLLHUP;
- /* readable? */
- if (!skb_queue_empty(&pdata->rcv_queue))
- mask |= POLLIN | POLLRDNORM;
- /* Need to check for termination and startup */
- if (sk->state==TCP_CLOSE)
- mask |= POLLHUP;
- /* connection hasn't started yet? */
- if (sk->state == TCP_SYN_SENT)
- return mask;
- /* writable? */
- if (sock_writeable(sk))
- mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
- else
- set_bit(SOCK_ASYNC_NOSPACE,&sk->socket->flags);
- return mask;
- }
- /* 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;
- /*
- * Called on connection receive so cannot be GFP_KERNEL
- */
-
- sk = sk_alloc(PF_IPX, GFP_ATOMIC, 1);
- if(sk == NULL)
- return (-ENOMEM);
- switch(sock->type)
- {
- case SOCK_SEQPACKET:
- sock->ops = &spx_ops;
- 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->no_check = 1;
- MOD_INC_USE_COUNT;
- return (0);
- }
- 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 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);
- 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(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.
- *
- * FixMe: This is quite fake, but will work for now. (JS)
- */
- 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 (CONREQ):
- 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_NOIO);
- if(skb2 == NULL)
- return -ENOBUFS;
- skb_queue_tail(&pdata->retransmit_queue, skb2);
- case (ACK):
- 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;
- unsigned long flags;
- int err;
- if(skb == NULL)
- {
- int offset = ipx_if_offset(pdata->dest_addr.net);
- int size = offset + sizeof(struct ipxspxhdr);
- if (offset < 0) /* ENETUNREACH */
- return(-ENETUNREACH);
- save_flags(flags);
- cli();
- skb = sock_alloc_send_skb(sk, size, 0, &err);
- if(skb == NULL) {
- restore_flags(flags);
- return (-ENOMEM);
- }
- skb_reserve(skb, offset);
- skb->h.raw = 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 */
- mod_timer(&pdata->watchdog, jiffies+VERIFY_TIMEOUT);
- 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): /* ACK */
- pdata->rmt_seq++;
- case (WDACK): /* WD ACK */
- case (CONACK): /* Connection ACK */
- 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 */
- return (spx_route_skb(pdata, skb, type));
- }
- /* 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->state == SPX_CLOSED)
- return;
- 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;
- unsigned long flags;
- int err;
- del_timer(&pdata->retransmit);
- if(pdata->state == SPX_CLOSED)
- return;
- if(pdata->retransmits > RETRY_COUNT)
- {
- spx_close_socket(sk); /* Unilateral Abort */
- return;
- }
- /* Need to leave skb on the queue, aye the fear */
- save_flags(flags);
- cli();
- skb = skb_peek(&pdata->retransmit_queue);
- if(skb_cloned(skb))
- skb = skb_copy(skb, GFP_ATOMIC);
- else
- skb = skb_clone(skb, GFP_ATOMIC);
- restore_flags(flags);
- pdata->retransmit.expires = jiffies + spx_calc_rtt(pdata->retransmits);
- add_timer(&pdata->retransmit);
- err = spx_route_skb(pdata, skb, RETRAN);
- pdata->retransmits++;
- return;
- }
- /* Check packet for retransmission, ConReqAck aware */
- static int spx_retransmit_chk(struct spx_opt *pdata, int ackseq, int type)
- {
- struct ipxspxhdr *ipxh;
- struct sk_buff *skb;
- skb = skb_dequeue(&pdata->retransmit_queue);
- if(!skb)
- return (-ENOENT);
- /* Check Data/ACK seq */
- switch(type)
- {
- case ACK: /* Check Sequence, Should == 1 */
- ipxh = (struct ipxspxhdr *)skb->nh.raw;
- if(!(ntohs(ipxh->spx.sequence) - htons(ackseq)))
- break;
- case CONACK:
- del_timer(&pdata->retransmit);
- pdata->retransmits = 0;
- kfree_skb(skb);
- if(skb_queue_empty(&pdata->retransmit_queue))
- {
- skb = skb_dequeue(&pdata->transmit_queue);
- if(skb != NULL)
- spx_route_skb(pdata, skb, TQUEUE);
- }
- return (0);
- }
- skb_queue_head(&pdata->retransmit_queue, skb);
- return (-1);
- }
- /* SPX packet receive engine */
- void spx_rcv(struct sock *sk, int bytes)
- {
- struct sk_buff *skb;
- struct ipxspxhdr *ipxh;
- 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))
- goto toss_skb;
- if(ntohs(ipxh->ipx.ipx_pktsize) < SPX_SYS_PKT_LEN)
- goto toss_skb;
- if(ipxh->ipx.ipx_type != IPX_TYPE_SPX)
- goto toss_skb;
- if(ntohs(ipxh->spx.ackseq) > pdata->sequence)
- goto toss_skb;
- /* 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);
- goto finish;
- 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;
- pdata->dest_connid = ipxh->spx.sconn;
- if(spx_retransmit_chk(pdata, 0, CONACK) < 0)
- goto toss_skb;
- skb_queue_tail(&sk->receive_queue, skb);
- wake_up_interruptible(sk->sleep);
- goto finish;
- }
- spx_retransmit_chk(pdata, ipxh->spx.ackseq, ACK);
- goto toss_skb;
- case (CCTL_ACK):
- /* Informed Disconnect */
- if(ipxh->spx.dtype == SPX_DTYPE_ECONN)
- {
-
- spx_transmit(sk, skb, DISACK, 0);
- spx_close_socket(sk);
- goto finish;
- }
- /* Fall through */
- default:
- if(ntohs(ipxh->spx.sequence) == pdata->rmt_seq)
- {
- pdata->rmt_seq = ntohs(ipxh->spx.sequence);
- pdata->rmt_ack = ntohs(ipxh->spx.ackseq);
- pdata->alloc = pdata->rmt_seq + 3;
- if(pdata->rmt_ack > 0 || pdata->rmt_ack == 0)
- spx_retransmit_chk(pdata,pdata->rmt_ack, ACK);
- skb_queue_tail(&pdata->rcv_queue, skb);
- wake_up_interruptible(sk->sleep);
- if(ipxh->spx.cctl&CCTL_ACK)
- spx_transmit(sk, NULL, ACK, 0);
- goto finish;
- }
- if(ipxh->spx.dtype == SPX_DTYPE_ECACK)
- {
- if(pdata->state != SPX_CLOSED)
- spx_close_socket(sk);
- goto toss_skb;
- }
- }
- toss_skb: /* Catch All */
- kfree_skb(skb);
- finish:
- 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;
- cli();
- skb = sock_alloc_send_skb(sk, size, flags&MSG_DONTWAIT, &err);
- sti();
- if(skb == NULL)
- return (err);
- skb->sk = sk;
- skb_reserve(skb, offset);
- skb->h.raw = 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 SOCKOPS_WRAPPED(spx_ops) = {
- family: PF_IPX,
- release: spx_release,
- bind: spx_bind,
- connect: spx_connect,
- socketpair: sock_no_socketpair,
- accept: spx_accept,
- getname: spx_getname,
- poll: spx_datagram_poll,
- ioctl: spx_ioctl,
- listen: spx_listen,
- shutdown: sock_no_shutdown,
- setsockopt: spx_setsockopt,
- getsockopt: spx_getsockopt,
- sendmsg: spx_sendmsg,
- recvmsg: spx_recvmsg,
- mmap: sock_no_mmap,
- sendpage: sock_no_sendpage,
- };
- #include <linux/smp_lock.h>
- SOCKOPS_WRAP(spx, PF_IPX);
- static struct net_proto_family spx_family_ops = {
- family: PF_IPX,
- create: spx_create,
- };
- static char banner[] __initdata = KERN_INFO "NET4: Sequenced Packet eXchange (SPX) 0.02 for Linux NET4.0n";
- static int __init 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(PF_IPX, SOCK_SEQPACKET) calls through spx_create() */
- printk(banner);
- return 0;
- }
- module_init(spx_proto_init);
- static void __exit spx_proto_finito(void)
- {
- ipx_unregister_spx();
- return;
- }
- module_exit(spx_proto_finito);