📄 af_spx.c
字号:
/* * 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -