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

📄 af_econet.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	An implementation of the Acorn Econet and AUN protocols. *	Philip Blundell <philb@gnu.org> * *	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. * */#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/if_ether.h>#include <linux/netdevice.h>#include <linux/inetdevice.h>#include <linux/route.h>#include <linux/inet.h>#include <linux/etherdevice.h>#include <linux/if_arp.h>#include <linux/wireless.h>#include <linux/skbuff.h>#include <linux/udp.h>#include <net/sock.h>#include <net/inet_common.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/if_ec.h>#include <net/udp.h>#include <net/ip.h>#include <linux/spinlock.h>#include <linux/rcupdate.h>#include <linux/bitops.h>#include <linux/mutex.h>#include <asm/uaccess.h>#include <asm/system.h>static const struct proto_ops econet_ops;static struct hlist_head econet_sklist;static DEFINE_RWLOCK(econet_lock);static DEFINE_MUTEX(econet_mutex);/* Since there are only 256 possible network numbers (or fewer, depends   how you count) it makes sense to use a simple lookup table. */static struct net_device *net2dev_map[256];#define EC_PORT_IP	0xd2#ifdef CONFIG_ECONET_AUNUDPstatic DEFINE_SPINLOCK(aun_queue_lock);static struct socket *udpsock;#define AUN_PORT	0x8000struct aunhdr{	unsigned char code;		/* AUN magic protocol byte */	unsigned char port;	unsigned char cb;	unsigned char pad;	unsigned long handle;};static unsigned long aun_seq;/* Queue of packets waiting to be transmitted. */static struct sk_buff_head aun_queue;static struct timer_list ab_cleanup_timer;#endif		/* CONFIG_ECONET_AUNUDP *//* Per-packet information */struct ec_cb{	struct sockaddr_ec sec;	unsigned long cookie;		/* Supplied by user. */#ifdef CONFIG_ECONET_AUNUDP	int done;	unsigned long seq;		/* Sequencing */	unsigned long timeout;		/* Timeout */	unsigned long start;		/* jiffies */#endif#ifdef CONFIG_ECONET_NATIVE	void (*sent)(struct sk_buff *, int result);#endif};static void econet_remove_socket(struct hlist_head *list, struct sock *sk){	write_lock_bh(&econet_lock);	sk_del_node_init(sk);	write_unlock_bh(&econet_lock);}static void econet_insert_socket(struct hlist_head *list, struct sock *sk){	write_lock_bh(&econet_lock);	sk_add_node(sk, list);	write_unlock_bh(&econet_lock);}/* *	Pull a packet from our receive queue and hand it to the user. *	If necessary we block. */static int econet_recvmsg(struct kiocb *iocb, struct socket *sock,			  struct msghdr *msg, size_t len, int flags){	struct sock *sk = sock->sk;	struct sk_buff *skb;	size_t copied;	int err;	msg->msg_namelen = sizeof(struct sockaddr_ec);	mutex_lock(&econet_mutex);	/*	 *	Call the generic datagram receiver. This handles all sorts	 *	of horrible races and re-entrancy so we can forget about it	 *	in the protocol layers.	 *	 *	Now it will return ENETDOWN, if device have just gone down,	 *	but then it will block.	 */	skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err);	/*	 *	An error occurred so return it. Because skb_recv_datagram()	 *	handles the blocking we don't see and worry about blocking	 *	retries.	 */	if(skb==NULL)		goto out;	/*	 *	You lose any data beyond the buffer you gave. If it worries a	 *	user program they can ask the device for its MTU anyway.	 */	copied = skb->len;	if (copied > len)	{		copied=len;		msg->msg_flags|=MSG_TRUNC;	}	/* We can't use skb_copy_datagram here */	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);	if (err)		goto out_free;	sk->sk_stamp = skb->tstamp;	if (msg->msg_name)		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);	/*	 *	Free or return the buffer as appropriate. Again this	 *	hides all the races and re-entrancy issues from us.	 */	err = copied;out_free:	skb_free_datagram(sk, skb);out:	mutex_unlock(&econet_mutex);	return err;}/* *	Bind an Econet socket. */static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;	struct sock *sk;	struct econet_sock *eo;	/*	 *	Check legality	 */	if (addr_len < sizeof(struct sockaddr_ec) ||	    sec->sec_family != AF_ECONET)		return -EINVAL;	mutex_lock(&econet_mutex);	sk = sock->sk;	eo = ec_sk(sk);	eo->cb	    = sec->cb;	eo->port    = sec->port;	eo->station = sec->addr.station;	eo->net	    = sec->addr.net;	mutex_unlock(&econet_mutex);	return 0;}#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)/* *	Queue a transmit result for the user to be told about. */static void tx_result(struct sock *sk, unsigned long cookie, int result){	struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC);	struct ec_cb *eb;	struct sockaddr_ec *sec;	if (skb == NULL)	{		printk(KERN_DEBUG "ec: memory squeeze, transmit result dropped.\n");		return;	}	eb = (struct ec_cb *)&skb->cb;	sec = (struct sockaddr_ec *)&eb->sec;	memset(sec, 0, sizeof(struct sockaddr_ec));	sec->cookie = cookie;	sec->type = ECTYPE_TRANSMIT_STATUS | result;	sec->sec_family = AF_ECONET;	if (sock_queue_rcv_skb(sk, skb) < 0)		kfree_skb(skb);}#endif#ifdef CONFIG_ECONET_NATIVE/* *	Called by the Econet hardware driver when a packet transmit *	has completed.  Tell the user. */static void ec_tx_done(struct sk_buff *skb, int result){	struct ec_cb *eb = (struct ec_cb *)&skb->cb;	tx_result(skb->sk, eb->cookie, result);}#endif/* *	Send a packet.  We have to work out which device it's going out on *	and hence whether to use real Econet or the UDP emulation. */static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,			  struct msghdr *msg, size_t len){	struct sock *sk = sock->sk;	struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;	struct net_device *dev;	struct ec_addr addr;	int err;	unsigned char port, cb;#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)	struct sk_buff *skb;	struct ec_cb *eb;#endif#ifdef CONFIG_ECONET_AUNUDP	struct msghdr udpmsg;	struct iovec iov[msg->msg_iovlen+1];	struct aunhdr ah;	struct sockaddr_in udpdest;	__kernel_size_t size;	int i;	mm_segment_t oldfs;#endif	/*	 *	Check the flags.	 */	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))		return -EINVAL;	/*	 *	Get and verify the address.	 */	mutex_lock(&econet_mutex);	if (saddr == NULL) {		struct econet_sock *eo = ec_sk(sk);		addr.station = eo->station;		addr.net     = eo->net;		port	     = eo->port;		cb	     = eo->cb;	} else {		if (msg->msg_namelen < sizeof(struct sockaddr_ec)) {			mutex_unlock(&econet_mutex);			return -EINVAL;		}		addr.station = saddr->addr.station;		addr.net = saddr->addr.net;		port = saddr->port;		cb = saddr->cb;	}	/* Look for a device with the right network number. */	dev = net2dev_map[addr.net];	/* If not directly reachable, use some default */	if (dev == NULL) {		dev = net2dev_map[0];		/* No interfaces at all? */		if (dev == NULL) {			mutex_unlock(&econet_mutex);			return -ENETDOWN;		}	}	if (len + 15 > dev->mtu) {		mutex_unlock(&econet_mutex);		return -EMSGSIZE;	}	if (dev->type == ARPHRD_ECONET) {		/* Real hardware Econet.  We're not worthy etc. */#ifdef CONFIG_ECONET_NATIVE		unsigned short proto = 0;		int res;		dev_hold(dev);		skb = sock_alloc_send_skb(sk, len+LL_RESERVED_SPACE(dev),					  msg->msg_flags & MSG_DONTWAIT, &err);		if (skb==NULL)			goto out_unlock;		skb_reserve(skb, LL_RESERVED_SPACE(dev));		skb_reset_network_header(skb);		eb = (struct ec_cb *)&skb->cb;		/* BUG: saddr may be NULL */		eb->cookie = saddr->cookie;		eb->sec = *saddr;		eb->sent = ec_tx_done;		err = -EINVAL;		res = dev_hard_header(skb, dev, ntohs(proto), &addr, NULL, len);		if (res < 0)			goto out_free;		if (res > 0) {			struct ec_framehdr *fh;			/* Poke in our control byte and			   port number.  Hack, hack.  */			fh = (struct ec_framehdr *)(skb->data);			fh->cb = cb;			fh->port = port;			if (sock->type != SOCK_DGRAM) {				skb_reset_tail_pointer(skb);				skb->len = 0;			}		}		/* Copy the data. Returns -EFAULT on error */		err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);		skb->protocol = proto;		skb->dev = dev;		skb->priority = sk->sk_priority;		if (err)			goto out_free;		err = -ENETDOWN;		if (!(dev->flags & IFF_UP))			goto out_free;		/*		 *	Now send it		 */		dev_queue_xmit(skb);		dev_put(dev);		mutex_unlock(&econet_mutex);		return(len);	out_free:		kfree_skb(skb);	out_unlock:		if (dev)			dev_put(dev);#else		err = -EPROTOTYPE;#endif		mutex_unlock(&econet_mutex);		return err;	}#ifdef CONFIG_ECONET_AUNUDP	/* AUN virtual Econet. */	if (udpsock == NULL) {		mutex_unlock(&econet_mutex);		return -ENETDOWN;		/* No socket - can't send */	}	/* Make up a UDP datagram and hand it off to some higher intellect. */	memset(&udpdest, 0, sizeof(udpdest));	udpdest.sin_family = AF_INET;	udpdest.sin_port = htons(AUN_PORT);	/* At the moment we use the stupid Acorn scheme of Econet address	   y.x maps to IP a.b.c.x.  This should be replaced with something	   more flexible and more aware of subnet masks.  */	{		struct in_device *idev;		unsigned long network = 0;		rcu_read_lock();		idev = __in_dev_get_rcu(dev);		if (idev) {			if (idev->ifa_list)				network = ntohl(idev->ifa_list->ifa_address) &					0xffffff00;		/* !!! */		}		rcu_read_unlock();		udpdest.sin_addr.s_addr = htonl(network | addr.station);	}	ah.port = port;	ah.cb = cb & 0x7f;	ah.code = 2;		/* magic */	ah.pad = 0;	/* tack our header on the front of the iovec */	size = sizeof(struct aunhdr);	/*	 * XXX: that is b0rken.  We can't mix userland and kernel pointers	 * in iovec, since on a lot of platforms copy_from_user() will	 * *not* work with the kernel and userland ones at the same time,	 * regardless of what we do with set_fs().  And we are talking about	 * econet-over-ethernet here, so "it's only ARM anyway" doesn't	 * apply.  Any suggestions on fixing that code?		-- AV	 */	iov[0].iov_base = (void *)&ah;	iov[0].iov_len = size;	for (i = 0; i < msg->msg_iovlen; i++) {		void __user *base = msg->msg_iov[i].iov_base;		size_t len = msg->msg_iov[i].iov_len;		/* Check it now since we switch to KERNEL_DS later. */		if (!access_ok(VERIFY_READ, base, len)) {			mutex_unlock(&econet_mutex);			return -EFAULT;		}		iov[i+1].iov_base = base;		iov[i+1].iov_len = len;		size += len;	}	/* Get a skbuff (no data, just holds our cb information) */	if ((skb = sock_alloc_send_skb(sk, 0,				       msg->msg_flags & MSG_DONTWAIT,				       &err)) == NULL) {		mutex_unlock(&econet_mutex);		return err;	}	eb = (struct ec_cb *)&skb->cb;	eb->cookie = saddr->cookie;	eb->timeout = (5*HZ);	eb->start = jiffies;	ah.handle = aun_seq;	eb->seq = (aun_seq++);	eb->sec = *saddr;	skb_queue_tail(&aun_queue, skb);	udpmsg.msg_name = (void *)&udpdest;	udpmsg.msg_namelen = sizeof(udpdest);	udpmsg.msg_iov = &iov[0];	udpmsg.msg_iovlen = msg->msg_iovlen + 1;	udpmsg.msg_control = NULL;	udpmsg.msg_controllen = 0;	udpmsg.msg_flags=0;	oldfs = get_fs(); set_fs(KERNEL_DS);	/* More privs :-) */	err = sock_sendmsg(udpsock, &udpmsg, size);	set_fs(oldfs);#else	err = -EPROTOTYPE;#endif	mutex_unlock(&econet_mutex);	return err;}/* *	Look up the address of a socket. */static int econet_getname(struct socket *sock, struct sockaddr *uaddr,			  int *uaddr_len, int peer){	struct sock *sk;	struct econet_sock *eo;	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;	if (peer)		return -EOPNOTSUPP;	mutex_lock(&econet_mutex);	sk = sock->sk;	eo = ec_sk(sk);	sec->sec_family	  = AF_ECONET;	sec->port	  = eo->port;	sec->addr.station = eo->station;	sec->addr.net	  = eo->net;	mutex_unlock(&econet_mutex);	*uaddr_len = sizeof(*sec);	return 0;}static void econet_destroy_timer(unsigned long data){	struct sock *sk=(struct sock *)data;	if (!atomic_read(&sk->sk_wmem_alloc) &&	    !atomic_read(&sk->sk_rmem_alloc)) {		sk_free(sk);		return;	}	sk->sk_timer.expires = jiffies + 10 * HZ;	add_timer(&sk->sk_timer);	printk(KERN_DEBUG "econet socket destroy delayed\n");}/* *	Close an econet socket. */static int econet_release(struct socket *sock){	struct sock *sk;	mutex_lock(&econet_mutex);	sk = sock->sk;	if (!sk)		goto out_unlock;	econet_remove_socket(&econet_sklist, sk);	/*	 *	Now the socket is dead. No more input will appear.	 */	sk->sk_state_change(sk);	/* It is useless. Just for sanity. */	sock->sk = NULL;	sk->sk_socket = NULL;	sock_set_flag(sk, SOCK_DEAD);	/* Purge queues */	skb_queue_purge(&sk->sk_receive_queue);	if (atomic_read(&sk->sk_rmem_alloc) ||	    atomic_read(&sk->sk_wmem_alloc)) {		sk->sk_timer.data     = (unsigned long)sk;		sk->sk_timer.expires  = jiffies + HZ;		sk->sk_timer.function = econet_destroy_timer;		add_timer(&sk->sk_timer);		goto out_unlock;	}	sk_free(sk);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -