⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 af_spx.c

📁 嵌入式系统设计与实验教材二源码linux内核移植与编译
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	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 + -