📄 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. * * Revision 0.21: Uses the new generic socket option code. * Revision 0.22: Gcc clean ups and drop out device registration. Use the * new multi-protocol edition of hard_header * Revision 0.23: IPX /proc by Mark Evans. Adding a route will * will overwrite any existing route to the same network. * Revision 0.24: Supports new /proc with no 4K limit * Revision 0.25: Add ephemeral sockets, passive local network * identification, support for local net 0 and * multiple datalinks <Greg Page> * Revision 0.26: Device drop kills IPX routes via it. (needed for module) * Revision 0.27: Autobind <Mark Evans> * Revision 0.28: Small fix for multiple local networks <Thomas Winder> * Revision 0.29: Assorted major errors removed <Mark Evans> * Small correction to promisc mode error fix <Alan Cox> * Asynchronous I/O support. Changed to use notifiers * and the newer packet_type stuff. Assorted major * fixes <Alejandro Liu> * Revision 0.30: Moved to net/ipx/... <Alan Cox> * Don't set address length on recvfrom that errors. * Incorrect verify_area. * Revision 0.31: New sk_buffs. This still needs a lot of * testing. <Alan Cox> * Revision 0.32: Using sock_alloc_send_skb, firewall hooks. <Alan Cox> * Supports sendmsg/recvmsg * Revision 0.33: Internal network support, routing changes, uses a * protocol private area for ipx data. * Revision 0.34: Module support. <Jim Freeman> * Revision 0.35: Checksum support. <Neil Turton>, hooked in by <Alan Cox> * Handles WIN95 discovery packets <Volker Lendecke> * Revision 0.36: Internal bump up for 2.1 * Revision 0.37: Began adding POSIXisms. * Revision 0.38: Asynchronous socket stuff made current. * Revision 0.39: SPX interfaces * Revision 0.40: Tiny SIOCGSTAMP fix (chris@cybernet.co.nz) * Revision 0.41: 802.2TR removed (p.norton@computer.org) * Fixed connecting to primary net, * Automatic binding on send & receive, * Martijn van Oosterhout <kleptogimp@geocities.com> * Revision 042: Multithreading - use spinlocks and refcounting to * protect some structures: ipx_interface sock list, list * of ipx interfaces, etc. * Bugfixes - do refcounting on net_devices, check function * results, etc. Thanks to davem and freitag for * suggestions and guidance. * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, * November, 2000 * Revision 043: Shared SKBs, don't mangle packets, some cleanups * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, * December, 2000 * Revision 044: Call ipxitf_hold on NETDEV_UP (acme) * Revision 045: fix PPROP routing bug (acme) * Revision 046: Further fixes to PPROP, ipxitf_create_internal was * doing an unneeded MOD_INC_USE_COUNT, implement * sysctl for ipx_pprop_broacasting, fix the ipx sysctl * handling, making it dynamic, some cleanups, thanks to * Petr Vandrovec for review and good suggestions. (acme) * Revision 047: Cleanups, CodingStyle changes, move the ncp connection * hack out of line (acme) * * Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT * pair. Also, now usage count is managed this way * -Count one if the auto_interface mode is on * -Count one per configured interface * * Jacques Gelinas (jacques@solucorp.qc.ca) * * * 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. */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/netdevice.h>#include <net/ipx.h>#include <linux/inet.h>#include <linux/route.h>#include <net/sock.h>#include <asm/uaccess.h>#include <asm/system.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/termios.h> /* For TIOCOUTQ/INQ */#include <linux/interrupt.h>#include <net/p8022.h>#include <net/psnap.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/if_arp.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 struct proto_ops ipx_dgram_ops;static struct net_proto_family *spx_family_ops;static ipx_route *ipx_routes;static rwlock_t ipx_routes_lock = RW_LOCK_UNLOCKED;static ipx_interface *ipx_interfaces;static spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED;static ipx_interface *ipx_primary_net;static ipx_interface *ipx_internal_net;#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0]))#undef IPX_REFCNT_DEBUG#ifdef IPX_REFCNT_DEBUGatomic_t ipx_sock_nr;#endifstatic void ipxcfg_set_auto_create(char val){ if (ipxcfg_auto_create_interfaces != val) { if (val) MOD_INC_USE_COUNT; else MOD_DEC_USE_COUNT; ipxcfg_auto_create_interfaces = val; }}static void ipxcfg_set_auto_select(char val){ ipxcfg_auto_select_primary = val; if (val && !ipx_primary_net) ipx_primary_net = ipx_interfaces;}static int ipxcfg_get_config_data(ipx_config_data *arg){ 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;}/* Handlers for the socket list. */static inline void ipxitf_hold(ipx_interface *intrfc){ atomic_inc(&intrfc->refcnt);}static void ipxitf_down(ipx_interface *intrfc);static inline void ipxitf_put(ipx_interface *intrfc){ if (atomic_dec_and_test(&intrfc->refcnt)) ipxitf_down(intrfc);}static void __ipxitf_down(ipx_interface *intrfc);static inline void __ipxitf_put(ipx_interface *intrfc){ if (atomic_dec_and_test(&intrfc->refcnt)) __ipxitf_down(intrfc);}/* * 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. */void ipx_remove_socket(struct sock *sk){ struct sock *s; ipx_interface *intrfc; /* Determine interface with which socket is associated */ intrfc = sk->protinfo.af_ipx.intrfc; if (!intrfc) goto out; ipxitf_hold(intrfc); spin_lock_bh(&intrfc->if_sklist_lock); s = intrfc->if_sklist; if (s == sk) { intrfc->if_sklist = s->next; goto out_unlock; } while (s && s->next) { if (s->next == sk) { s->next = sk->next; goto out_unlock; } s = s->next; }out_unlock: spin_unlock_bh(&intrfc->if_sklist_lock); sock_put(sk); ipxitf_put(intrfc);out: return;}static void ipx_destroy_socket(struct sock *sk){ ipx_remove_socket(sk); skb_queue_purge(&sk->receive_queue);#ifdef IPX_REFCNT_DEBUG atomic_dec(&ipx_sock_nr); printk(KERN_DEBUG "IPX socket %p released, %d are still alive\n", sk, atomic_read(&ipx_sock_nr)); if (atomic_read(&sk->refcnt) != 1) printk(KERN_DEBUG "Destruction sock ipx %p delayed, cnt=%d\n", sk, atomic_read(&sk->refcnt));#endif 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. */static ipx_route * ipxrtr_lookup(__u32);/* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */static void ipxitf_clear_primary_net(void){ ipx_primary_net = ipxcfg_auto_select_primary ? ipx_interfaces : NULL;}static ipx_interface *__ipxitf_find_using_phys(struct net_device *dev, unsigned short datalink){ ipx_interface *i = ipx_interfaces; while (i && (i->if_dev != dev || i->if_dlink_type != datalink)) i = i->if_next; return i;}static ipx_interface *ipxitf_find_using_phys(struct net_device *dev, unsigned short datalink){ 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;}static ipx_interface *ipxitf_find_using_net(__u32 net){ ipx_interface *i; spin_lock_bh(&ipx_interfaces_lock); if (net) for (i = ipx_interfaces; i && i->if_netnum != net; i = i->if_next) ; else i = ipx_primary_net; if (i) ipxitf_hold(i); spin_unlock_bh(&ipx_interfaces_lock); return i;}/* Sockets are bound to a particular IPX interface. */static void ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk){ ipxitf_hold(intrfc); sock_hold(sk); spin_lock_bh(&intrfc->if_sklist_lock); sk->protinfo.af_ipx.intrfc = intrfc; sk->next = NULL; if (!intrfc->if_sklist) intrfc->if_sklist = sk; else { struct sock *s = intrfc->if_sklist; while (s->next) s = s->next; s->next = sk; } spin_unlock_bh(&intrfc->if_sklist_lock); ipxitf_put(intrfc);}/* caller must hold intrfc->if_sklist_lock */static struct sock *__ipxitf_find_socket(ipx_interface *intrfc, unsigned short port){ struct sock *s = intrfc->if_sklist; while (s && s->protinfo.af_ipx.port != port) s = s->next; return s;}/* caller must hold a reference to intrfc */static struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short 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(ipx_interface *intrfc, unsigned char *node, unsigned short port){ struct sock *s; ipxitf_hold(intrfc); spin_lock_bh(&intrfc->if_sklist_lock); s = intrfc->if_sklist; while (s) { if (s->protinfo.af_ipx.port == port && !memcmp(node, s->protinfo.af_ipx.node, IPX_NODE_LEN)) break; s = s->next; } spin_unlock_bh(&intrfc->if_sklist_lock); ipxitf_put(intrfc); return s;}#endifstatic void ipxrtr_del_routes(ipx_interface *);static void __ipxitf_down(ipx_interface *intrfc){ struct sock *s, *t; /* Delete all routes associated with this interface */ ipxrtr_del_routes(intrfc); spin_lock_bh(&intrfc->if_sklist_lock); /* error sockets */ for (s = intrfc->if_sklist; s;) { s->err = ENOLINK; s->error_report(s); s->protinfo.af_ipx.intrfc = NULL; s->protinfo.af_ipx.port = 0; s->zapped = 1; /* Indicates it is no longer bound */ t = s; s = s->next; t->next = NULL; } intrfc->if_sklist = NULL; spin_unlock_bh(&intrfc->if_sklist_lock); /* remove this interface from list */ if (intrfc == ipx_interfaces) ipx_interfaces = intrfc->if_next; else { ipx_interface *i = ipx_interfaces; while (i && i->if_next != intrfc) i = i->if_next; if (i && i->if_next == intrfc) i->if_next = intrfc->if_next; } /* 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); MOD_DEC_USE_COUNT;}static void ipxitf_down(ipx_interface *intrfc){ spin_lock_bh(&ipx_interfaces_lock); __ipxitf_down(intrfc); spin_unlock_bh(&ipx_interfaces_lock);}static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr){ struct net_device *dev = ptr; ipx_interface *i, *tmp; if (event != NETDEV_DOWN && event != NETDEV_UP) goto out; spin_lock_bh(&ipx_interfaces_lock); for (i = ipx_interfaces; i;) { tmp = i->if_next; if (i->if_dev == dev) { if (event == NETDEV_UP) ipxitf_hold(i); else __ipxitf_put(i); } i = tmp; } spin_unlock_bh(&ipx_interfaces_lock);out: return NOTIFY_DONE;}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(ipx_interface *intrfc, struct sk_buff *skb, int copy){ struct ipxhdr *ipx = skb->nh.ipxh; int is_broadcast = !memcmp(ipx->ipx_dest.node, ipx_broadcast_node, IPX_NODE_LEN); struct sock *s; int ret; spin_lock_bh(&intrfc->if_sklist_lock); s = intrfc->if_sklist; while (s) { if (s->protinfo.af_ipx.port == ipx->ipx_dest.sock && (is_broadcast || !memcmp(ipx->ipx_dest.node, s->protinfo.af_ipx.node, IPX_NODE_LEN))) { /* We found a socket to which to send */ struct sk_buff *skb1; if (copy) { skb1 = skb_clone(skb, GFP_ATOMIC); ret = -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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -