2 * This module implements the (SPP-derived) Sequenced Packet eXchange
3 * (SPX) protocol for Linux 2.1.X as specified in
4 * NetWare SPX Services Specification, Semantics and API
6 * Revision Date: February 9, 1993
9 * Jay Schulist <jschlst@turbolinux.com>
10 * Jim Freeman <jfree@caldera.com>
13 * Alan Cox : Fixed an skb_unshare check for NULL
14 * that crashed it under load. Renamed and
15 * made static the ipx ops. Removed the hack
16 * ipx methods interface. Dropped AF_SPX - its
17 * the wrong abstraction.
18 * Eduardo Trapani : Added a check for the return value of
19 * ipx_if_offset that crashed sock_alloc_send_skb.
20 * Added spx_datagram_poll() so that select()
21 * works now on SPX sockets. Added updating
22 * of the alloc count to follow rmt_seq.
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version
27 * 2 of the License, or (at your option) any later version.
29 * None of the authors or maintainers or their employers admit
30 * liability nor provide warranty for any of this software.
31 * This material is provided "as is" and at no charge.
34 #include <linux/config.h>
35 #if defined(CONFIG_SPX) || defined(CONFIG_SPX_MODULE)
36 #include <linux/module.h>
40 #include <asm/byteorder.h>
41 #include <asm/uaccess.h>
42 #include <linux/uio.h>
43 #include <linux/unistd.h>
44 #include <linux/poll.h>
46 static struct proto_ops
*ipx_operations
;
47 static struct proto_ops spx_ops
;
50 /* Functions needed for SPX connection start up */
51 static int spx_transmit(struct sock
*sk
,struct sk_buff
*skb
,int type
,int len
);
52 static void spx_retransmit(unsigned long data
);
53 static void spx_watchdog(unsigned long data
);
54 void spx_rcv(struct sock
*sk
, int bytes
);
56 extern void ipx_remove_socket(struct sock
*sk
);
58 /* Datagram poll: the same code as datagram_poll() in net/core
59 but the right spx buffers are looked at and
60 there is no question on the type of the socket
62 static unsigned int spx_datagram_poll(struct file
* file
, struct socket
*sock
, poll_table
*wait
)
64 struct sock
*sk
= sock
->sk
;
65 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
68 poll_wait(file
, sk
->sleep
, wait
);
71 /* exceptional events? */
72 if (sk
->err
|| !skb_queue_empty(&sk
->error_queue
))
74 if (sk
->shutdown
& RCV_SHUTDOWN
)
78 if (!skb_queue_empty(&pdata
->rcv_queue
))
79 mask
|= POLLIN
| POLLRDNORM
;
81 /* Need to check for termination and startup */
82 if (sk
->state
==TCP_CLOSE
)
84 /* connection hasn't started yet? */
85 if (sk
->state
== TCP_SYN_SENT
)
89 if (sock_writeable(sk
))
90 mask
|= POLLOUT
| POLLWRNORM
| POLLWRBAND
;
92 set_bit(SOCK_ASYNC_NOSPACE
,&sk
->socket
->flags
);
97 /* Create the SPX specific data */
98 static int spx_sock_init(struct sock
*sk
)
100 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
102 pdata
->state
= SPX_CLOSED
;
104 pdata
->acknowledge
= 0;
105 pdata
->source_connid
= htons(connids
);
109 pdata
->owner
= (void *)sk
;
110 pdata
->sndbuf
= sk
->sndbuf
;
112 pdata
->watchdog
.function
= spx_watchdog
;
113 pdata
->watchdog
.data
= (unsigned long)sk
;
114 pdata
->wd_interval
= VERIFY_TIMEOUT
;
115 pdata
->retransmit
.function
= spx_retransmit
;
116 pdata
->retransmit
.data
= (unsigned long)sk
;
117 pdata
->retransmits
= 0;
119 pdata
->max_retries
= RETRY_COUNT
;
121 skb_queue_head_init(&pdata
->rcv_queue
);
122 skb_queue_head_init(&pdata
->transmit_queue
);
123 skb_queue_head_init(&pdata
->retransmit_queue
);
128 static int spx_create(struct socket
*sock
, int protocol
)
133 * Called on connection receive so cannot be GFP_KERNEL
136 sk
= sk_alloc(PF_IPX
, GFP_ATOMIC
, 1);
143 sock
->ops
= &spx_ops
;
147 return (-ESOCKTNOSUPPORT
);
150 sock_init_data(sock
, sk
);
152 sk
->data_ready
= spx_rcv
;
162 void spx_close_socket(struct sock
*sk
)
164 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
166 pdata
->state
= SPX_CLOSED
;
167 sk
->state
= TCP_CLOSE
;
168 del_timer(&pdata
->retransmit
);
169 del_timer(&pdata
->watchdog
);
172 void spx_destroy_socket(struct sock
*sk
)
174 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
177 ipx_remove_socket(sk
);
178 while((skb
= skb_dequeue(&sk
->receive_queue
)) != NULL
)
180 while((skb
= skb_dequeue(&pdata
->transmit_queue
)) != NULL
)
182 while((skb
= skb_dequeue(&pdata
->retransmit_queue
)) != NULL
)
184 while((skb
= skb_dequeue(&pdata
->rcv_queue
)) != NULL
)
191 /* Release an SPX socket */
192 static int spx_release(struct socket
*sock
)
194 struct sock
*sk
= sock
->sk
;
195 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
200 sk
->state_change(sk
);
203 if(pdata
->state
!= SPX_CLOSED
)
205 spx_transmit(sk
, NULL
, DISCON
, 0);
206 spx_close_socket(sk
);
211 spx_destroy_socket(sk
);
216 /* Move a socket into listening state. */
217 static int spx_listen(struct socket
*sock
, int backlog
)
219 struct sock
*sk
= sock
->sk
;
221 if(sock
->state
!= SS_UNCONNECTED
)
223 if(sock
->type
!= SOCK_SEQPACKET
)
224 return (-EOPNOTSUPP
);
228 sk
->max_ack_backlog
= backlog
;
229 if(sk
->state
!= TCP_LISTEN
)
232 sk
->state
= TCP_LISTEN
;
234 sk
->socket
->flags
|= __SO_ACCEPTCON
;
239 /* Accept a pending SPX connection */
240 static int spx_accept(struct socket
*sock
, struct socket
*newsock
, int flags
)
251 if((sock
->state
!= SS_UNCONNECTED
) || !(sock
->flags
& __SO_ACCEPTCON
))
253 if(sock
->type
!= SOCK_SEQPACKET
)
254 return (-EOPNOTSUPP
);
255 if(sk
->state
!= TCP_LISTEN
)
260 skb
= skb_dequeue(&sk
->receive_queue
);
263 if(flags
& O_NONBLOCK
)
266 return (-EWOULDBLOCK
);
268 interruptible_sleep_on(sk
->sleep
);
269 if(signal_pending(current
))
272 return (-ERESTARTSYS
);
275 } while (skb
== NULL
);
281 err
= spx_transmit(newsk
, skb
, CONACK
, 0); /* Connection ACK */
285 /* Now attach up the new socket */
289 newsk
->state
= TCP_ESTABLISHED
;
290 newsk
->protinfo
.af_ipx
.dest_addr
= newsk
->tp_pinfo
.af_spx
.dest_addr
;
295 /* Build a connection to an SPX socket */
296 static int spx_connect(struct socket
*sock
, struct sockaddr
*uaddr
,
297 int addr_len
, int flags
)
299 struct sock
*sk
= sock
->sk
;
300 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
301 struct sockaddr_ipx src
;
306 err
= ipx_operations
->getname(sock
, (struct sockaddr
*)&src
, &size
, 0);
310 pdata
->source_addr
.net
= src
.sipx_network
;
311 memcpy(pdata
->source_addr
.node
, src
.sipx_node
, IPX_NODE_LEN
);
312 pdata
->source_addr
.sock
= (unsigned short)src
.sipx_port
;
314 err
= ipx_operations
->connect(sock
, uaddr
, addr_len
, flags
);
318 pdata
->dest_addr
= sk
->protinfo
.af_ipx
.dest_addr
;
319 pdata
->state
= SPX_CONNECTING
;
320 sock
->state
= SS_CONNECTING
;
321 sk
->state
= TCP_SYN_SENT
;
323 /* Send Connection request */
324 err
= spx_transmit(sk
, NULL
, CONREQ
, 0);
330 skb
= skb_dequeue(&sk
->receive_queue
);
333 if(flags
& O_NONBLOCK
)
336 return (-EWOULDBLOCK
);
338 interruptible_sleep_on(sk
->sleep
);
339 if(signal_pending(current
))
342 return (-ERESTARTSYS
);
345 } while (skb
== NULL
);
347 if(pdata
->state
== SPX_CLOSED
)
350 del_timer(&pdata
->watchdog
);
354 sock
->state
= SS_CONNECTED
;
355 sk
->state
= TCP_ESTABLISHED
;
363 * Calculate the timeout for a packet. Thankfully SPX has a large
364 * fudge factor (3/4 secs) and does not pay much attention to RTT.
365 * As we simply have a default retry time of 1*HZ and a max retry
366 * time of 5*HZ. Between those values we increase the timeout based
367 * on the number of retransmit tries.
369 * FixMe: This is quite fake, but will work for now. (JS)
371 static inline unsigned long spx_calc_rtt(int tries
)
376 return (MAX_RETRY_DELAY
);
380 static int spx_route_skb(struct spx_opt
*pdata
, struct sk_buff
*skb
, int type
)
382 struct sk_buff
*skb2
;
385 skb
= skb_unshare(skb
, GFP_ATOMIC
);
393 if(!skb_queue_empty(&pdata
->retransmit_queue
))
395 skb_queue_tail(&pdata
->transmit_queue
, skb
);
400 pdata
->retransmit
.expires
= jiffies
+ spx_calc_rtt(0);
401 add_timer(&pdata
->retransmit
);
403 skb2
= skb_clone(skb
, GFP_BUFFER
);
406 skb_queue_tail(&pdata
->retransmit_queue
, skb2
);
417 err
= ipxrtr_route_skb(skb
);
425 /* SPX packet transmit engine */
426 static int spx_transmit(struct sock
*sk
, struct sk_buff
*skb
, int type
, int len
)
428 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
429 struct ipxspxhdr
*ipxh
;
435 int offset
= ipx_if_offset(pdata
->dest_addr
.net
);
436 int size
= offset
+ sizeof(struct ipxspxhdr
);
438 if (offset
< 0) /* ENETUNREACH */
439 return(-ENETUNREACH
);
443 skb
= sock_alloc_send_skb(sk
, size
, 1, 0, &err
);
445 restore_flags(flags
);
448 skb_reserve(skb
, offset
);
449 skb
->h
.raw
= skb
->nh
.raw
= skb_put(skb
,sizeof(struct ipxspxhdr
));
450 restore_flags(flags
);
454 ipxh
= (struct ipxspxhdr
*)skb
->nh
.raw
;
455 ipxh
->ipx
.ipx_checksum
= 0xFFFF;
456 ipxh
->ipx
.ipx_pktsize
= htons(SPX_SYS_PKT_LEN
);
457 ipxh
->ipx
.ipx_tctrl
= 0;
458 ipxh
->ipx
.ipx_type
= IPX_TYPE_SPX
;
459 ipxh
->ipx
.ipx_dest
= pdata
->dest_addr
;
460 ipxh
->ipx
.ipx_source
= pdata
->source_addr
;
464 ipxh
->spx
.sequence
= htons(pdata
->sequence
);
465 ipxh
->spx
.ackseq
= htons(pdata
->rmt_seq
);
466 ipxh
->spx
.sconn
= pdata
->source_connid
;
467 ipxh
->spx
.dconn
= pdata
->dest_connid
;
468 ipxh
->spx
.allocseq
= htons(pdata
->alloc
);
470 /* Reset/Set WD timer */
471 mod_timer(&pdata
->watchdog
, jiffies
+VERIFY_TIMEOUT
);
475 case (DATA
): /* Data */
476 ipxh
->ipx
.ipx_pktsize
= htons(SPX_SYS_PKT_LEN
+ len
);
477 ipxh
->spx
.cctl
= (CCTL_ACK
| CCTL_EOM
);
481 case (ACK
): /* ACK */
483 case (WDACK
): /* WD ACK */
484 case (CONACK
): /* Connection ACK */
485 ipxh
->spx
.cctl
= CCTL_SYS
;
486 ipxh
->spx
.ackseq
= htons(pdata
->rmt_seq
);
489 case (CONREQ
): /* Connection Request */
490 del_timer(&pdata
->watchdog
);
491 case (WDREQ
): /* WD Request */
492 pdata
->source_connid
= htons(connids
++);
493 pdata
->dest_connid
= 0xFFFF;
494 pdata
->alloc
= 3 + pdata
->rmt_seq
;
495 ipxh
->spx
.cctl
= (CCTL_ACK
| CCTL_SYS
);
496 ipxh
->spx
.sconn
= pdata
->source_connid
;
497 ipxh
->spx
.dconn
= pdata
->dest_connid
;
498 ipxh
->spx
.allocseq
= htons(pdata
->alloc
);
501 case (DISCON
): /* Informed Disconnect */
502 ipxh
->spx
.cctl
= CCTL_ACK
;
503 ipxh
->spx
.dtype
= SPX_DTYPE_ECONN
;
506 case (DISACK
): /* Informed Disconnect ACK */
508 ipxh
->spx
.dtype
= SPX_DTYPE_ECACK
;
509 ipxh
->spx
.sequence
= 0;
510 ipxh
->spx
.ackseq
= htons(pdata
->rmt_seq
++);
514 return (-EOPNOTSUPP
);
518 return (spx_route_skb(pdata
, skb
, type
));
521 /* Check the state of the connection and send a WD request if needed. */
522 static void spx_watchdog(unsigned long data
)
524 struct sock
*sk
= (struct sock
*)data
;
525 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
527 del_timer(&pdata
->watchdog
);
528 if(pdata
->state
== SPX_CLOSED
)
530 if(pdata
->retries
> pdata
->max_retries
)
532 spx_close_socket(sk
); /* Unilateral Abort */
536 /* Send WD request */
537 spx_transmit(sk
, NULL
, WDREQ
, 0);
543 static void spx_retransmit(unsigned long data
)
545 struct sock
*sk
= (struct sock
*)data
;
546 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
551 del_timer(&pdata
->retransmit
);
552 if(pdata
->state
== SPX_CLOSED
)
554 if(pdata
->retransmits
> RETRY_COUNT
)
556 spx_close_socket(sk
); /* Unilateral Abort */
560 /* Need to leave skb on the queue, aye the fear */
563 skb
= skb_peek(&pdata
->retransmit_queue
);
565 skb
= skb_copy(skb
, GFP_ATOMIC
);
567 skb
= skb_clone(skb
, GFP_ATOMIC
);
568 restore_flags(flags
);
570 pdata
->retransmit
.expires
= jiffies
+ spx_calc_rtt(pdata
->retransmits
);
571 add_timer(&pdata
->retransmit
);
573 err
= spx_route_skb(pdata
, skb
, RETRAN
);
574 pdata
->retransmits
++;
579 /* Check packet for retransmission, ConReqAck aware */
580 static int spx_retransmit_chk(struct spx_opt
*pdata
, int ackseq
, int type
)
582 struct ipxspxhdr
*ipxh
;
585 skb
= skb_dequeue(&pdata
->retransmit_queue
);
589 /* Check Data/ACK seq */
592 case ACK
: /* Check Sequence, Should == 1 */
593 ipxh
= (struct ipxspxhdr
*)skb
->nh
.raw
;
594 if(!(ntohs(ipxh
->spx
.sequence
) - htons(ackseq
)))
598 del_timer(&pdata
->retransmit
);
599 pdata
->retransmits
= 0;
601 if(skb_queue_empty(&pdata
->retransmit_queue
))
603 skb
= skb_dequeue(&pdata
->transmit_queue
);
605 spx_route_skb(pdata
, skb
, TQUEUE
);
610 skb_queue_head(&pdata
->retransmit_queue
, skb
);
614 /* SPX packet receive engine */
615 void spx_rcv(struct sock
*sk
, int bytes
)
618 struct ipxspxhdr
*ipxh
;
619 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
621 skb
= skb_dequeue(&sk
->receive_queue
);
624 ipxh
= (struct ipxspxhdr
*)skb
->nh
.raw
;
626 /* Can't receive on a closed connection */
627 if((pdata
->state
== SPX_CLOSED
) && (ipxh
->spx
.sequence
!= 0))
629 if(ntohs(ipxh
->ipx
.ipx_pktsize
) < SPX_SYS_PKT_LEN
)
631 if(ipxh
->ipx
.ipx_type
!= IPX_TYPE_SPX
)
633 if(ntohs(ipxh
->spx
.ackseq
) > pdata
->sequence
)
636 /* Reset WD timer on any received packet */
637 del_timer(&pdata
->watchdog
);
639 pdata
->watchdog
.expires
= jiffies
+ ABORT_TIMEOUT
;
640 add_timer(&pdata
->watchdog
);
642 switch(ipxh
->spx
.cctl
)
644 case (CCTL_SYS
| CCTL_ACK
):
645 if((ipxh
->spx
.sequence
== 0) /* ConReq */
646 && (ipxh
->spx
.ackseq
== 0)
647 && (ipxh
->spx
.dconn
== 0xFFFF))
649 pdata
->state
= SPX_CONNECTED
;
650 pdata
->dest_addr
= ipxh
->ipx
.ipx_source
;
651 pdata
->source_addr
= ipxh
->ipx
.ipx_dest
;
652 pdata
->dest_connid
= ipxh
->spx
.sconn
;
653 pdata
->alloc
= 3 + ntohs(ipxh
->spx
.sequence
);
655 skb_queue_tail(&sk
->receive_queue
, skb
);
656 wake_up_interruptible(sk
->sleep
);
658 else /* WD Request */
659 spx_transmit(sk
, skb
, WDACK
, 0);
662 case CCTL_SYS
: /* ACK */
663 if((ipxh
->spx
.dtype
== 0) /* ConReq ACK */
664 && (ipxh
->spx
.sconn
!= 0xFFFF)
665 && (ipxh
->spx
.dconn
!= 0xFFFF)
666 && (ipxh
->spx
.sequence
== 0)
667 && (ipxh
->spx
.ackseq
== 0)
668 && (pdata
->state
!= SPX_CONNECTED
))
670 pdata
->state
= SPX_CONNECTED
;
671 pdata
->dest_connid
= ipxh
->spx
.sconn
;
673 if(spx_retransmit_chk(pdata
, 0, CONACK
) < 0)
676 skb_queue_tail(&sk
->receive_queue
, skb
);
677 wake_up_interruptible(sk
->sleep
);
681 spx_retransmit_chk(pdata
, ipxh
->spx
.ackseq
, ACK
);
685 /* Informed Disconnect */
686 if(ipxh
->spx
.dtype
== SPX_DTYPE_ECONN
)
689 spx_transmit(sk
, skb
, DISACK
, 0);
690 spx_close_socket(sk
);
696 if(ntohs(ipxh
->spx
.sequence
) == pdata
->rmt_seq
)
698 pdata
->rmt_seq
= ntohs(ipxh
->spx
.sequence
);
699 pdata
->rmt_ack
= ntohs(ipxh
->spx
.ackseq
);
700 pdata
->alloc
= pdata
->rmt_seq
+ 3;
701 if(pdata
->rmt_ack
> 0 || pdata
->rmt_ack
== 0)
702 spx_retransmit_chk(pdata
,pdata
->rmt_ack
, ACK
);
704 skb_queue_tail(&pdata
->rcv_queue
, skb
);
705 wake_up_interruptible(sk
->sleep
);
706 if(ipxh
->spx
.cctl
&CCTL_ACK
)
707 spx_transmit(sk
, NULL
, ACK
, 0);
711 if(ipxh
->spx
.dtype
== SPX_DTYPE_ECACK
)
713 if(pdata
->state
!= SPX_CLOSED
)
714 spx_close_socket(sk
);
719 toss_skb
: /* Catch All */
725 /* Get message/packet data from user-land */
726 static int spx_sendmsg(struct socket
*sock
, struct msghdr
*msg
, int len
,
727 struct scm_cookie
*scm
)
729 struct sock
*sk
= sock
->sk
;
730 int flags
= msg
->msg_flags
;
732 int err
, offset
, size
;
737 return (-ENOTCONN
); /* Socket not bound */
738 if(flags
&~MSG_DONTWAIT
)
741 offset
= ipx_if_offset(sk
->tp_pinfo
.af_spx
.dest_addr
.net
);
742 size
= offset
+ sizeof(struct ipxspxhdr
) + len
;
745 skb
= sock_alloc_send_skb(sk
, size
, 0, flags
&MSG_DONTWAIT
, &err
);
751 skb_reserve(skb
, offset
);
752 skb
->h
.raw
= skb
->nh
.raw
= skb_put(skb
, sizeof(struct ipxspxhdr
));
754 err
= memcpy_fromiovec(skb_put(skb
, len
), msg
->msg_iov
, len
);
761 err
= spx_transmit(sk
, skb
, DATA
, len
);
768 /* Send message/packet data to user-land */
769 static int spx_recvmsg(struct socket
*sock
, struct msghdr
*msg
, int size
,
770 int flags
, struct scm_cookie
*scm
)
773 struct ipxspxhdr
*ispxh
;
774 struct sock
*sk
= sock
->sk
;
775 struct spx_opt
*pdata
= &sk
->tp_pinfo
.af_spx
;
776 struct sockaddr_ipx
*sipx
= (struct sockaddr_ipx
*)msg
->msg_name
;
780 return (-ENOTCONN
); /* Socket not bound */
784 while(skb_queue_empty(&pdata
->rcv_queue
)) /* No data */
787 err
= sock_error(sk
);
791 /* Socket shut down? */
792 if(sk
->shutdown
& RCV_SHUTDOWN
)
796 if(signal_pending(current
))
797 return (-ERESTARTSYS
);
799 /* User doesn't want to wait */
800 if(flags
&MSG_DONTWAIT
)
806 if(skb_peek(&pdata
->rcv_queue
) == NULL
)
807 interruptible_sleep_on(sk
->sleep
);
808 restore_flags(flags
);
812 skb
= skb_dequeue(&pdata
->rcv_queue
);
816 ispxh
= (struct ipxspxhdr
*)skb
->nh
.raw
;
817 copied
= ntohs(ispxh
->ipx
.ipx_pktsize
) - SPX_SYS_PKT_LEN
;
821 msg
->msg_flags
|= MSG_TRUNC
;
824 err
= memcpy_toiovec(msg
->msg_iov
, skb
->nh
.raw
+SPX_SYS_PKT_LEN
, copied
);
828 msg
->msg_namelen
= sizeof(*sipx
);
831 sipx
->sipx_family
= AF_IPX
;
832 sipx
->sipx_port
= ispxh
->ipx
.ipx_source
.sock
;
833 memcpy(sipx
->sipx_node
,ispxh
->ipx
.ipx_source
.node
,IPX_NODE_LEN
);
834 sipx
->sipx_network
= ispxh
->ipx
.ipx_source
.net
;
835 sipx
->sipx_type
= ispxh
->ipx
.ipx_type
;
844 * Functions which just wrap their IPX cousins
847 static int spx_bind(struct socket
*sock
, struct sockaddr
*uaddr
, int addr_len
)
850 err
= ipx_operations
->bind(sock
, uaddr
, addr_len
);
854 static int spx_getname (struct socket
*sock
, struct sockaddr
*uaddr
,
855 int *usockaddr_len
, int peer
)
858 err
= ipx_operations
->getname(sock
, uaddr
, usockaddr_len
, peer
);
862 static int spx_ioctl (struct socket
*sock
, unsigned int cmd
,
866 err
= ipx_operations
->ioctl(sock
, cmd
, arg
);
870 static int spx_setsockopt(struct socket
*sock
, int level
, int optname
,
871 char *optval
, int optlen
)
874 err
= ipx_operations
->setsockopt(sock
, level
, optname
, optval
, optlen
);
878 static int spx_getsockopt(struct socket
*sock
, int level
, int optname
,
879 char *optval
, int *optlen
)
882 err
= ipx_operations
->getsockopt(sock
, level
, optname
, optval
, optlen
);
886 static struct proto_ops
SOCKOPS_WRAPPED(spx_ops
) = {
889 release
: spx_release
,
891 connect
: spx_connect
,
892 socketpair
: sock_no_socketpair
,
894 getname
: spx_getname
,
895 poll
: spx_datagram_poll
,
898 shutdown
: sock_no_shutdown
,
899 setsockopt
: spx_setsockopt
,
900 getsockopt
: spx_getsockopt
,
901 sendmsg
: spx_sendmsg
,
902 recvmsg
: spx_recvmsg
,
906 #include <linux/smp_lock.h>
907 SOCKOPS_WRAP(spx
, PF_IPX
);
910 static struct net_proto_family spx_family_ops
=
917 void spx_proto_init(void)
921 connids
= (__u16
)jiffies
; /* initalize random */
923 error
= ipx_register_spx(&ipx_operations
, &spx_family_ops
);
925 printk(KERN_ERR
"SPX: unable to register with IPX.\n");
927 /* route socket(PF_IPX, SOCK_SEQPACKET) calls through spx_create() */
929 printk(KERN_INFO
"NET4: Sequenced Packet eXchange (SPX) 0.02 for Linux NET4.0\n");
933 void spx_proto_finito(void)
935 ipx_unregister_spx();
941 int init_module(void)
947 void cleanup_module(void)
954 #endif /* CONFIG_SPX || CONFIG_SPX_MODULE */