📄 af_ipx.c
字号:
/* * 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 + -