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

📄 af_ipx.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	Implements an IPX socket layer. * *	This code is derived from work by *		Ross Biro	: 	Writing the original IP stack *		Fred Van Kempen :	Tidying up the TCP/IP * *	Many thanks go to Keith Baker, Institute For Industrial Information *	Technology Ltd, Swansea University for allowing me to work on this *	in my own time even though it was in some ways related to commercial *	work I am currently employed to do there. * *	All the material in this file is subject to the Gnu license version 2. *	Neither Alan Cox nor the Swansea University Computer Society admit *	liability nor provide warranty for any of this software. This material *	is provided as is and at no charge. * *	Portions Copyright (c) 2000-2003 Conectiva, Inc. <acme@conectiva.com.br> *	Neither Arnaldo Carvalho de Melo nor Conectiva, Inc. admit liability nor *	provide warranty for any of this software. This material is provided *	"AS-IS" and at no charge. * * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com> *	Neither Greg Page nor Caldera, Inc. admit liability nor provide *	warranty for any of this software. This material is provided *	"AS-IS" and at no charge. * *	See net/ipx/ChangeLog. */#include <linux/capability.h>#include <linux/errno.h>#include <linux/if_arp.h>#include <linux/if_ether.h>#include <linux/init.h>#include <linux/ipx.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/module.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/uio.h>#include <linux/skbuff.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/string.h>#include <linux/types.h>#include <linux/termios.h>#include <net/ipx.h>#include <net/p8022.h>#include <net/psnap.h>#include <net/sock.h>#include <net/tcp_states.h>#include <asm/uaccess.h>#ifdef CONFIG_SYSCTLextern void ipx_register_sysctl(void);extern void ipx_unregister_sysctl(void);#else#define ipx_register_sysctl()#define ipx_unregister_sysctl()#endif/* Configuration Variables */static unsigned char ipxcfg_max_hops = 16;static char ipxcfg_auto_select_primary;static char ipxcfg_auto_create_interfaces;int sysctl_ipx_pprop_broadcasting = 1;/* Global Variables */static struct datalink_proto *p8022_datalink;static struct datalink_proto *pEII_datalink;static struct datalink_proto *p8023_datalink;static struct datalink_proto *pSNAP_datalink;static const struct proto_ops ipx_dgram_ops;LIST_HEAD(ipx_interfaces);DEFINE_SPINLOCK(ipx_interfaces_lock);struct ipx_interface *ipx_primary_net;struct ipx_interface *ipx_internal_net;extern int ipxrtr_add_route(__be32 network, struct ipx_interface *intrfc,			    unsigned char *node);extern void ipxrtr_del_routes(struct ipx_interface *intrfc);extern int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,			       struct iovec *iov, size_t len, int noblock);extern int ipxrtr_route_skb(struct sk_buff *skb);extern struct ipx_route *ipxrtr_lookup(__be32 net);extern int ipxrtr_ioctl(unsigned int cmd, void __user *arg);struct ipx_interface *ipx_interfaces_head(void){	struct ipx_interface *rc = NULL;	if (!list_empty(&ipx_interfaces))		rc = list_entry(ipx_interfaces.next,				struct ipx_interface, node);	return rc;}static void ipxcfg_set_auto_select(char val){	ipxcfg_auto_select_primary = val;	if (val && !ipx_primary_net)		ipx_primary_net = ipx_interfaces_head();}static int ipxcfg_get_config_data(struct ipx_config_data __user *arg){	struct ipx_config_data vals;	vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;	vals.ipxcfg_auto_select_primary	   = ipxcfg_auto_select_primary;	return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0;}/* * Note: Sockets may not be removed _during_ an interrupt or inet_bh * handler using this technique. They can be added although we do not * use this facility. */static void ipx_remove_socket(struct sock *sk){	/* Determine interface with which socket is associated */	struct ipx_interface *intrfc = ipx_sk(sk)->intrfc;	if (!intrfc)		goto out;	ipxitf_hold(intrfc);	spin_lock_bh(&intrfc->if_sklist_lock);	sk_del_node_init(sk);	spin_unlock_bh(&intrfc->if_sklist_lock);	ipxitf_put(intrfc);out:	return;}static void ipx_destroy_socket(struct sock *sk){	ipx_remove_socket(sk);	skb_queue_purge(&sk->sk_receive_queue);	sk_refcnt_debug_dec(sk);	sock_put(sk);}/* * The following code is used to support IPX Interfaces (IPXITF).  An * IPX interface is defined by a physical device and a frame type. *//* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */static void ipxitf_clear_primary_net(void){	ipx_primary_net = NULL;	if (ipxcfg_auto_select_primary)		ipx_primary_net = ipx_interfaces_head();}static struct ipx_interface *__ipxitf_find_using_phys(struct net_device *dev,						      __be16 datalink){	struct ipx_interface *i;	list_for_each_entry(i, &ipx_interfaces, node)		if (i->if_dev == dev && i->if_dlink_type == datalink)			goto out;	i = NULL;out:	return i;}static struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev,						    __be16 datalink){	struct ipx_interface *i;	spin_lock_bh(&ipx_interfaces_lock);	i = __ipxitf_find_using_phys(dev, datalink);	if (i)		ipxitf_hold(i);	spin_unlock_bh(&ipx_interfaces_lock);	return i;}struct ipx_interface *ipxitf_find_using_net(__be32 net){	struct ipx_interface *i;	spin_lock_bh(&ipx_interfaces_lock);	if (net) {		list_for_each_entry(i, &ipx_interfaces, node)			if (i->if_netnum == net)				goto hold;		i = NULL;		goto unlock;	}	i = ipx_primary_net;	if (i)hold:		ipxitf_hold(i);unlock:	spin_unlock_bh(&ipx_interfaces_lock);	return i;}/* Sockets are bound to a particular IPX interface. */static void ipxitf_insert_socket(struct ipx_interface *intrfc, struct sock *sk){	ipxitf_hold(intrfc);	spin_lock_bh(&intrfc->if_sklist_lock);	ipx_sk(sk)->intrfc = intrfc;	sk_add_node(sk, &intrfc->if_sklist);	spin_unlock_bh(&intrfc->if_sklist_lock);	ipxitf_put(intrfc);}/* caller must hold intrfc->if_sklist_lock */static struct sock *__ipxitf_find_socket(struct ipx_interface *intrfc,					 __be16 port){	struct sock *s;	struct hlist_node *node;	sk_for_each(s, node, &intrfc->if_sklist)		if (ipx_sk(s)->port == port)			goto found;	s = NULL;found:	return s;}/* caller must hold a reference to intrfc */static struct sock *ipxitf_find_socket(struct ipx_interface *intrfc,					__be16 port){	struct sock *s;	spin_lock_bh(&intrfc->if_sklist_lock);	s = __ipxitf_find_socket(intrfc, port);	if (s)		sock_hold(s);	spin_unlock_bh(&intrfc->if_sklist_lock);	return s;}#ifdef CONFIG_IPX_INTERNstatic struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc,						unsigned char *ipx_node,						__be16 port){	struct sock *s;	struct hlist_node *node;	ipxitf_hold(intrfc);	spin_lock_bh(&intrfc->if_sklist_lock);	sk_for_each(s, node, &intrfc->if_sklist) {		struct ipx_sock *ipxs = ipx_sk(s);		if (ipxs->port == port &&		    !memcmp(ipx_node, ipxs->node, IPX_NODE_LEN))			goto found;	}	s = NULL;found:	spin_unlock_bh(&intrfc->if_sklist_lock);	ipxitf_put(intrfc);	return s;}#endifstatic void __ipxitf_down(struct ipx_interface *intrfc){	struct sock *s;	struct hlist_node *node, *t;	/* Delete all routes associated with this interface */	ipxrtr_del_routes(intrfc);	spin_lock_bh(&intrfc->if_sklist_lock);	/* error sockets */	sk_for_each_safe(s, node, t, &intrfc->if_sklist) {		struct ipx_sock *ipxs = ipx_sk(s);		s->sk_err = ENOLINK;		s->sk_error_report(s);		ipxs->intrfc = NULL;		ipxs->port   = 0;		sock_set_flag(s, SOCK_ZAPPED); /* Indicates it is no longer bound */		sk_del_node_init(s);	}	INIT_HLIST_HEAD(&intrfc->if_sklist);	spin_unlock_bh(&intrfc->if_sklist_lock);	/* remove this interface from list */	list_del(&intrfc->node);	/* remove this interface from *special* networks */	if (intrfc == ipx_primary_net)		ipxitf_clear_primary_net();	if (intrfc == ipx_internal_net)		ipx_internal_net = NULL;	if (intrfc->if_dev)		dev_put(intrfc->if_dev);	kfree(intrfc);}void ipxitf_down(struct ipx_interface *intrfc){	spin_lock_bh(&ipx_interfaces_lock);	__ipxitf_down(intrfc);	spin_unlock_bh(&ipx_interfaces_lock);}static __inline__ void __ipxitf_put(struct ipx_interface *intrfc){	if (atomic_dec_and_test(&intrfc->refcnt))		__ipxitf_down(intrfc);}static int ipxitf_device_event(struct notifier_block *notifier,				unsigned long event, void *ptr){	struct net_device *dev = ptr;	struct ipx_interface *i, *tmp;	if (dev->nd_net != &init_net)		return NOTIFY_DONE;	if (event != NETDEV_DOWN && event != NETDEV_UP)		goto out;	spin_lock_bh(&ipx_interfaces_lock);	list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)		if (i->if_dev == dev) {			if (event == NETDEV_UP)				ipxitf_hold(i);			else				__ipxitf_put(i);		}	spin_unlock_bh(&ipx_interfaces_lock);out:	return NOTIFY_DONE;}static __exit void ipxitf_cleanup(void){	struct ipx_interface *i, *tmp;	spin_lock_bh(&ipx_interfaces_lock);	list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)		__ipxitf_put(i);	spin_unlock_bh(&ipx_interfaces_lock);}static void ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb){	if (sock_queue_rcv_skb(sock, skb) < 0)		kfree_skb(skb);}/* * On input skb->sk is NULL. Nobody is charged for the memory. *//* caller must hold a reference to intrfc */#ifdef CONFIG_IPX_INTERNstatic int ipxitf_demux_socket(struct ipx_interface *intrfc,			       struct sk_buff *skb, int copy){	struct ipxhdr *ipx = ipx_hdr(skb);	int is_broadcast = !memcmp(ipx->ipx_dest.node, ipx_broadcast_node,				   IPX_NODE_LEN);	struct sock *s;	struct hlist_node *node;	int rc;	spin_lock_bh(&intrfc->if_sklist_lock);	sk_for_each(s, node, &intrfc->if_sklist) {		struct ipx_sock *ipxs = ipx_sk(s);		if (ipxs->port == ipx->ipx_dest.sock &&		    (is_broadcast || !memcmp(ipx->ipx_dest.node,					     ipxs->node, IPX_NODE_LEN))) {			/* We found a socket to which to send */			struct sk_buff *skb1;			if (copy) {				skb1 = skb_clone(skb, GFP_ATOMIC);				rc = -ENOMEM;				if (!skb1)					goto out;			} else {				skb1 = skb;				copy = 1; /* skb may only be used once */			}			ipxitf_def_skb_handler(s, skb1);			/* On an external interface, one socket can listen */			if (intrfc != ipx_internal_net)				break;		}	}	/* skb was solely for us, and we did not make a copy, so free it. */	if (!copy)		kfree_skb(skb);	rc = 0;out:	spin_unlock_bh(&intrfc->if_sklist_lock);	return rc;}#elsestatic struct sock *ncp_connection_hack(struct ipx_interface *intrfc,					struct ipxhdr *ipx){	/* The packet's target is a NCP connection handler. We want to hand it	 * to the correct socket directly within the kernel, so that the	 * mars_nwe packet distribution process does not have to do it. Here we	 * only care about NCP and BURST packets.	 *	 * You might call this a hack, but believe me, you do not want a	 * complete NCP layer in the kernel, and this is VERY fast as well. */	struct sock *sk = NULL;	int connection = 0;	u8 *ncphdr = (u8 *)(ipx + 1);	if (*ncphdr == 0x22 && *(ncphdr + 1) == 0x22) /* NCP request */		connection = (((int) *(ncphdr + 5)) << 8) | (int) *(ncphdr + 3);	else if (*ncphdr == 0x77 && *(ncphdr + 1) == 0x77) /* BURST packet */		connection = (((int) *(ncphdr + 9)) << 8) | (int) *(ncphdr + 8);	if (connection) {		struct hlist_node *node;		/* Now we have to look for a special NCP connection handling		 * socket. Only these sockets have ipx_ncp_conn != 0, set by		 * SIOCIPXNCPCONN. */		spin_lock_bh(&intrfc->if_sklist_lock);		sk_for_each(sk, node, &intrfc->if_sklist)			if (ipx_sk(sk)->ipx_ncp_conn == connection) {				sock_hold(sk);				goto found;			}		sk = NULL;	found:		spin_unlock_bh(&intrfc->if_sklist_lock);	}	return sk;}static int ipxitf_demux_socket(struct ipx_interface *intrfc,			       struct sk_buff *skb, int copy){	struct ipxhdr *ipx = ipx_hdr(skb);	struct sock *sock1 = NULL, *sock2 = NULL;	struct sk_buff *skb1 = NULL, *skb2 = NULL;	int rc;	if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451)		sock1 = ncp_connection_hack(intrfc, ipx);	if (!sock1)		/* No special socket found, forward the packet the normal way */		sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);	/*	 * We need to check if there is a primary net and if	 * this is addressed to one of the *SPECIAL* sockets because	 * these need to be propagated to the primary net.	 * The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and	 * 0x456(Diagnostic).	 */	if (ipx_primary_net && intrfc != ipx_primary_net) {		const int dsock = ntohs(ipx->ipx_dest.sock);		if (dsock == 0x452 || dsock == 0x453 || dsock == 0x456)			/* The appropriate thing to do here is to dup the			 * packet and route to the primary net interface via			 * ipxitf_send; however, we'll cheat and just demux it			 * here. */			sock2 = ipxitf_find_socket(ipx_primary_net,							ipx->ipx_dest.sock);	}	/*	 * If there is nothing to do return. The kfree will cancel any charging.	 */	rc = 0;	if (!sock1 && !sock2) {		if (!copy)			kfree_skb(skb);		goto out;	}	/*	 * This next segment of code is a little awkward, but it sets it up	 * so that the appropriate number of copies of the SKB are made and	 * that skb1 and skb2 point to it (them) so that it (they) can be	 * demuxed to sock1 and/or sock2.  If we are unable to make enough	 * copies, we do as much as is possible.	 */

⌨️ 快捷键说明

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