URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [net/] [ipx/] [af_ipx.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * Implements an IPX socket layer (badly - but I'm working on it). * * 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 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 modules) * 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> * * 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/module.h> #include <linux/config.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/segment.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/p8022tr.h> #include <net/psnap.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/firewall.h> #ifdef MODULE static void ipx_proto_finito(void); #endif /* def MODULE */ /* Configuration Variables */ static unsigned char ipxcfg_max_hops = 16; static char ipxcfg_auto_select_primary = 0; static char ipxcfg_auto_create_interfaces = 0; /* Global Variables */ static struct datalink_proto *p8022_datalink = NULL; static struct datalink_proto *p8022tr_datalink = NULL; static struct datalink_proto *pEII_datalink = NULL; static struct datalink_proto *p8023_datalink = NULL; static struct datalink_proto *pSNAP_datalink = NULL; static ipx_route *ipx_routes = NULL; static ipx_interface *ipx_interfaces = NULL; static ipx_interface *ipx_primary_net = NULL; static ipx_interface *ipx_internal_net = NULL; static int 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; } return 0; } static int ipxcfg_set_auto_select(char val) { ipxcfg_auto_select_primary = val; if (val && (ipx_primary_net == NULL)) ipx_primary_net = ipx_interfaces; return 0; } 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; memcpy_tofs(arg, &vals, sizeof(vals)); return 0; } /***********************************************************************************************************************\ * * * Handlers for the socket list. * * * \***********************************************************************************************************************/ /* * 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(ipx_socket *sk) { ipx_socket *s; ipx_interface *intrfc; unsigned long flags; save_flags(flags); cli(); /* Determine interface with which socket is associated */ intrfc = sk->protinfo.af_ipx.intrfc; if (intrfc == NULL) { restore_flags(flags); return; } s=intrfc->if_sklist; if(s==sk) { intrfc->if_sklist=s->next; restore_flags(flags); return; } while(s && s->next) { if(s->next==sk) { s->next=sk->next; restore_flags(flags); return; } s=s->next; } restore_flags(flags); } /* * This is only called from user mode. Thus it protects itself against * interrupt users but doesn't worry about being called during work. * Once it is removed from the queue no interrupt or bottom half will * touch it and we are (fairly 8-) ) safe. */ static void ipx_destroy_socket(ipx_socket *sk) { struct sk_buff *skb; ipx_remove_socket(sk); while((skb=skb_dequeue(&sk->receive_queue))!=NULL) { kfree_skb(skb,FREE_READ); } sk_free(sk); MOD_DEC_USE_COUNT; } /* 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(unsigned long); static void ipxitf_clear_primary_net(void) { if (ipxcfg_auto_select_primary && (ipx_interfaces != NULL)) ipx_primary_net = ipx_interfaces; else ipx_primary_net = NULL; } static ipx_interface * ipxitf_find_using_phys(struct device *dev, unsigned short datalink) { ipx_interface *i; for (i=ipx_interfaces; i && ((i->if_dev!=dev) || (i->if_dlink_type!=datalink)); i=i->if_next) ; return i; } static ipx_interface * ipxitf_find_using_net(unsigned long net) { ipx_interface *i; if (net == 0L) return ipx_primary_net; for (i=ipx_interfaces; i && (i->if_netnum!=net); i=i->if_next) ; return i; } /* Sockets are bound to a particular IPX interface. */ static void ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk) { ipx_socket *s; sk->protinfo.af_ipx.intrfc = intrfc; sk->next = NULL; if (intrfc->if_sklist == NULL) { intrfc->if_sklist = sk; } else { for (s = intrfc->if_sklist; s->next != NULL; s = s->next) ; s->next = sk; } } static ipx_socket * ipxitf_find_socket(ipx_interface *intrfc, unsigned short port) { ipx_socket *s; for (s=intrfc->if_sklist; (s != NULL) && (s->protinfo.af_ipx.port != port); s=s->next) ; return s; } #ifdef CONFIG_IPX_INTERN static ipx_socket * ipxitf_find_internal_socket(ipx_interface *intrfc, unsigned char *node, unsigned short port) { ipx_socket *s = intrfc->if_sklist; while (s != NULL) { if ( (s->protinfo.af_ipx.port == port) && (memcmp(node, s->protinfo.af_ipx.node, IPX_NODE_LEN) == 0)) { break; } s = s->next; } return s; } #endif static void ipxrtr_del_routes(ipx_interface *); static void ipxitf_down(ipx_interface *intrfc) { ipx_interface *i; ipx_socket *s, *t; /* Delete all routes associated with this interface */ ipxrtr_del_routes(intrfc); /* error sockets */ for (s = intrfc->if_sklist; s != NULL; ) { 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; /* remove this interface from list */ if (intrfc == ipx_interfaces) { ipx_interfaces = intrfc->if_next; } else { for (i = ipx_interfaces; (i != NULL) && (i->if_next != intrfc); i = i->if_next) ; if ((i != NULL) && (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; kfree_s(intrfc, sizeof(*intrfc)); /* sockets still dangling * - must be closed from user space */ MOD_DEC_USE_COUNT; return; } static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr) { struct device *dev = ptr; ipx_interface *i, *tmp; if(event!=NETDEV_DOWN) return NOTIFY_DONE; for (i = ipx_interfaces; i != NULL; ) { tmp = i->if_next; if (i->if_dev == dev) ipxitf_down(i); i = tmp; } return NOTIFY_DONE; } static int ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb) { int retval; if((retval = sock_queue_rcv_skb(sock, skb))<0) { /* * skb->sk is NULL here, so FREE_WRITE does not hurt * the sending socket. */ kfree_skb(skb,FREE_WRITE); } return retval; } /* * On input skb->sk is NULL. Nobody is charged for the memory. */ #ifdef CONFIG_IPX_INTERN static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) { ipx_packet *ipx = (ipx_packet *)(skb->h.raw); ipx_socket *s; int is_broadcast = (memcmp(ipx->ipx_dest.node, ipx_broadcast_node, IPX_NODE_LEN) == 0); s = intrfc->if_sklist; while (s != NULL) { 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) == 0))) { /* We found a socket to which to send */ struct sk_buff *skb1; if (copy != 0) { skb1 = skb_clone(skb, GFP_ATOMIC); if (skb1 != NULL) { skb1->arp = skb1->free = 1; } else { return -ENOMEM; } } else { skb1 = skb; copy = 1; /* skb may only be used once */ } ipxitf_def_skb_handler(s, skb1); if (intrfc != ipx_internal_net) { /* on an external interface, at most * one socket can listen. */ break; } } s = s->next; } if (copy == 0) { /* skb was solely for us, and we did not make a copy, * so free it. FREE_WRITE does not hurt, because * skb->sk is NULL here. */ kfree_skb(skb, FREE_WRITE); } return 0; } #else static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) { ipx_packet *ipx = (ipx_packet *)(skb->h.raw); ipx_socket *sock1 = NULL, *sock2 = NULL; struct sk_buff *skb1 = NULL, *skb2 = NULL; if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451) { /* * 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. */ int connection = 0; if ( *((char*)(ipx+1)) == 0x22 && *((char*)(ipx+1)+1) == 0x22) { /* * The packet is a NCP request */ connection = ( ((int) *((char*)(ipx+1)+5)) << 8 ) | (int) *((char*)(ipx+1)+3); } else if ( *((char*)(ipx+1)) == 0x77 && *((char*)(ipx+1)+1) == 0x77) { /* * The packet is a BURST packet */ connection = ( ((int) *((char*)(ipx+1)+9)) << 8 ) | (int) *((char*)(ipx+1)+8); } if (connection) { /* * Now we have to look for a special NCP connection handling * socket. Only these sockets have ipx_ncp_conn != 0, set * by SIOCIPXNCPCONN. */ for (sock1=intrfc->if_sklist; (sock1 != NULL) && (sock1->protinfo.af_ipx.ipx_ncp_conn != connection); sock1=sock1->next);; } } if (sock1 == NULL) { /* 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)) { switch (ntohs(ipx->ipx_dest.sock)) { case 0x452: case 0x453: case 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); break; default: break; } } /* * if there is nothing to do, return. The kfree will * cancel any charging. */ if (sock1 == NULL && sock2 == NULL) { if (!copy) kfree_skb(skb,FREE_WRITE); return 0; } /* * 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. */ if (copy) { skb1 = skb_clone(skb, GFP_ATOMIC); if (skb1 != NULL) skb1->arp = skb1->free = 1; } else { skb1 = skb; } if (skb1 == NULL) return -ENOMEM; /* * Do we need 2 SKBs? */ if (sock1 && sock2) { skb2 = skb_clone(skb1, GFP_ATOMIC); if (skb2 != NULL) skb2->arp = skb2->free = 1; } else skb2 = skb1; if (sock1) (void) ipxitf_def_skb_handler(sock1, skb1); if (skb2 == NULL) return -ENOMEM; if (sock2) (void) ipxitf_def_skb_handler(sock2, skb2); return 0; } #endif static struct sk_buff * ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb) { struct sk_buff *skb2; int in_offset = skb->h.raw - skb->head; int out_offset = intrfc->if_ipx_offset; int len; /* Hopefully, most cases */ if (in_offset >= out_offset) { skb->arp = skb->free = 1; return skb; } /* Need new SKB */ len = skb->len + out_offset; skb2 = alloc_skb(len, GFP_ATOMIC); if (skb2 != NULL) { skb_reserve(skb2,out_offset); skb2->h.raw=skb_put(skb2,skb->len); skb2->free=1; skb2->arp=1; memcpy(skb2->h.raw, skb->h.raw, skb->len); } kfree_skb(skb, FREE_WRITE); return skb2; } static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node) { ipx_packet *ipx = (ipx_packet *)(skb->h.raw); struct device *dev = intrfc->if_dev; struct datalink_proto *dl = intrfc->if_dlink; char dest_node[IPX_NODE_LEN]; int send_to_wire = 1; int addr_len; /* * We need to know how many skbuffs it will take to send out this * packet to avoid unnecessary copies. */ if ((dl == NULL) || (dev == NULL) || (dev->flags & IFF_LOOPBACK)) send_to_wire = 0; /* No non looped */ /* * See if this should be demuxed to sockets on this interface * * We want to ensure the original was eaten or that we only use * up clones. */ if (ipx->ipx_dest.net == intrfc->if_netnum) { /* * To our own node, loop and free the original. */ if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0) { /* * Don't charge sender */ if(skb->sk) { atomic_sub(skb->truesize, &skb->sk->wmem_alloc); skb->sk=NULL; } /* * Will charge receiver */ return ipxitf_demux_socket(intrfc, skb, 0); } /* * Broadcast, loop and possibly keep to send on. */ if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) { if (!send_to_wire && skb->sk) { atomic_sub(skb->truesize, &skb->sk->wmem_alloc); skb->sk=NULL; } ipxitf_demux_socket(intrfc, skb, send_to_wire); if (!send_to_wire) return 0; } } /* * If the originating net is not equal to our net; this is routed * We are still charging the sender. Which is right - the driver * free will handle this fairly. */ if (ipx->ipx_source.net != intrfc->if_netnum) { if (++(ipx->ipx_tctrl) > ipxcfg_max_hops) send_to_wire = 0; } if (!send_to_wire) { /* * We do a FREE_WRITE here because this indicates how * to treat the socket with which the packet is * associated. If this packet is associated with a * socket at all, it must be the originator of the * packet. Routed packets will have no socket associated * with them. */ kfree_skb(skb,FREE_WRITE); return 0; } /* * Determine the appropriate hardware address */ addr_len = dev->addr_len; if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) memcpy(dest_node, dev->broadcast, addr_len); else memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len); /* * Make any compensation for differing physical/data link size */ skb = ipxitf_adjust_skbuff(intrfc, skb); if (skb == NULL) return 0; /* set up data link and physical headers */ skb->dev = dev; skb->protocol = htons(ETH_P_IPX); dl->datalink_header(dl, skb, dest_node); #if 0 /* * Now log the packet just before transmission */ dump_pkt("IPX snd:", (ipx_packet *)skb->h.raw); dump_data("ETH hdr:", skb->data, skb->h.raw - skb->data); #endif /* * Send it out */ dev_queue_xmit(skb, dev, SOPRI_NORMAL); return 0; } static int ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *); static int ipxitf_add_local_route(ipx_interface *intrfc) { return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL); } static const char * ipx_frame_name(unsigned short); static const char * ipx_device_name(ipx_interface *); static int ipxrtr_route_skb(struct sk_buff *); static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb) { ipx_packet *ipx = (ipx_packet *) (skb->h.raw); ipx_interface *i; #ifdef CONFIG_FIREWALL /* * We firewall first, ask questions later. */ if (call_in_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT) { kfree_skb(skb, FREE_READ); return 0; } #endif /* See if we should update our network number */ if ((intrfc->if_netnum == 0L) && (ipx->ipx_source.net == ipx->ipx_dest.net) && (ipx->ipx_source.net != 0L)) { /* NB: NetWare servers lie about their hop count so we * dropped the test based on it. This is the best way * to determine this is a 0 hop count packet. */ if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL) { intrfc->if_netnum = ipx->ipx_source.net; (void) ipxitf_add_local_route(intrfc); } else { printk(KERN_WARNING "IPX: Network number collision %lx\n %s %s and %s %s\n", htonl(ipx->ipx_source.net), ipx_device_name(i), ipx_frame_name(i->if_dlink_type), ipx_device_name(intrfc), ipx_frame_name(intrfc->if_dlink_type)); } } if (ipx->ipx_dest.net == 0L) ipx->ipx_dest.net = intrfc->if_netnum; if (ipx->ipx_source.net == 0L) ipx->ipx_source.net = intrfc->if_netnum; if (intrfc->if_netnum != ipx->ipx_dest.net) { #ifdef CONFIG_FIREWALL /* * See if we are allowed to firewall forward */ if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT) { kfree_skb(skb, FREE_READ); return 0; } #endif /* We only route point-to-point packets. */ if (skb->pkt_type == PACKET_HOST) return ipxrtr_route_skb(skb); kfree_skb(skb,FREE_READ); return 0; } /* see if we should keep it */ if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0) || (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)) { return ipxitf_demux_socket(intrfc, skb, 0); } /* we couldn't pawn it off so unload it */ kfree_skb(skb,FREE_READ); return 0; } static void ipxitf_insert(ipx_interface *intrfc) { ipx_interface *i; intrfc->if_next = NULL; if (ipx_interfaces == NULL) { ipx_interfaces = intrfc; } else { for (i = ipx_interfaces; i->if_next != NULL; i = i->if_next) ; i->if_next = intrfc; } if (ipxcfg_auto_select_primary && (ipx_primary_net == NULL)) ipx_primary_net = intrfc; MOD_INC_USE_COUNT; return; } static int ipxitf_create_internal(ipx_interface_definition *idef) { ipx_interface *intrfc; /* Only one primary network allowed */ if (ipx_primary_net != NULL) return -EEXIST; /* Must have a valid network number */ if (idef->ipx_network == 0L) return -EADDRNOTAVAIL; if (ipxitf_find_using_net(idef->ipx_network) != NULL) return -EADDRINUSE; intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC); if (intrfc==NULL) return -EAGAIN; intrfc->if_dev=NULL; intrfc->if_netnum=idef->ipx_network; intrfc->if_dlink_type = 0; intrfc->if_dlink = NULL; intrfc->if_sklist = NULL; intrfc->if_internal = 1; intrfc->if_ipx_offset = 0; intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET; memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN); ipx_internal_net = intrfc; ipx_primary_net = intrfc; ipxitf_insert(intrfc); return ipxitf_add_local_route(intrfc); } static int ipx_map_frame_type(unsigned char type) { switch (type) { case IPX_FRAME_ETHERII: return htons(ETH_P_IPX); case IPX_FRAME_8022: return htons(ETH_P_802_2); case IPX_FRAME_TR_8022: return htons(ETH_P_TR_802_2); case IPX_FRAME_SNAP: return htons(ETH_P_SNAP); case IPX_FRAME_8023: return htons(ETH_P_802_3); } return 0; } static int ipxitf_create(ipx_interface_definition *idef) { struct device *dev; unsigned short dlink_type = 0; struct datalink_proto *datalink = NULL; ipx_interface *intrfc; if (idef->ipx_special == IPX_INTERNAL) return ipxitf_create_internal(idef); if ((idef->ipx_special == IPX_PRIMARY) && (ipx_primary_net != NULL)) return -EEXIST; if ((idef->ipx_network != 0L) && (ipxitf_find_using_net(idef->ipx_network) != NULL)) return -EADDRINUSE; switch (idef->ipx_dlink_type) { case IPX_FRAME_ETHERII: dlink_type = htons(ETH_P_IPX); datalink = pEII_datalink; break; case IPX_FRAME_TR_8022: dlink_type = htons(ETH_P_TR_802_2); datalink = p8022tr_datalink; break; case IPX_FRAME_8022: dlink_type = htons(ETH_P_802_2); datalink = p8022_datalink; break; case IPX_FRAME_SNAP: dlink_type = htons(ETH_P_SNAP); datalink = pSNAP_datalink; break; case IPX_FRAME_8023: dlink_type = htons(ETH_P_802_3); datalink = p8023_datalink; break; case IPX_FRAME_NONE: default: break; } if (datalink == NULL) return -EPROTONOSUPPORT; dev=dev_get(idef->ipx_device); if (dev==NULL) return -ENODEV; if (!(dev->flags & IFF_UP)) return -ENETDOWN; /* Check addresses are suitable */ if(dev->addr_len>IPX_NODE_LEN) return -EINVAL; if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) { /* Ok now create */ intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC); if (intrfc==NULL) return -EAGAIN; intrfc->if_dev=dev; intrfc->if_netnum=idef->ipx_network; intrfc->if_dlink_type = dlink_type; intrfc->if_dlink = datalink; intrfc->if_sklist = NULL; intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET; /* Setup primary if necessary */ if ((idef->ipx_special == IPX_PRIMARY)) ipx_primary_net = intrfc; intrfc->if_internal = 0; intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length; if(memcmp(idef->ipx_node, "\000\000\000\000\000\000", IPX_NODE_LEN)==0) { memset(intrfc->if_node, 0, IPX_NODE_LEN); memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len); } else memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN); ipxitf_insert(intrfc); } /* If the network number is known, add a route */ if (intrfc->if_netnum == 0L) return 0; return ipxitf_add_local_route(intrfc); } static int ipxitf_delete(ipx_interface_definition *idef) { struct device *dev = NULL; unsigned short dlink_type = 0; ipx_interface *intrfc; if (idef->ipx_special == IPX_INTERNAL) { if (ipx_internal_net != NULL) { ipxitf_down(ipx_internal_net); return 0; } return -ENOENT; } dlink_type = ipx_map_frame_type(idef->ipx_dlink_type); if (dlink_type == 0) return -EPROTONOSUPPORT; dev=dev_get(idef->ipx_device); if(dev==NULL) return -ENODEV; intrfc = ipxitf_find_using_phys(dev, dlink_type); if (intrfc != NULL) { ipxitf_down(intrfc); return 0; } return -EINVAL; } static ipx_interface * ipxitf_auto_create(struct device *dev, unsigned short dlink_type) { struct datalink_proto *datalink = NULL; ipx_interface *intrfc; switch (htons(dlink_type)) { case ETH_P_IPX: datalink = pEII_datalink; break; case ETH_P_802_2: datalink = p8022_datalink; break; case ETH_P_TR_802_2: datalink = p8022tr_datalink; break; case ETH_P_SNAP: datalink = pSNAP_datalink; break; case ETH_P_802_3: datalink = p8023_datalink; break; default: return NULL; } if (dev == NULL) return NULL; /* Check addresses are suitable */ if(dev->addr_len>IPX_NODE_LEN) return NULL; intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC); if (intrfc!=NULL) { intrfc->if_dev=dev; intrfc->if_netnum=0L; intrfc->if_dlink_type = dlink_type; intrfc->if_dlink = datalink; intrfc->if_sklist = NULL; intrfc->if_internal = 0; intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET; intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length; memset(intrfc->if_node, 0, IPX_NODE_LEN); memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len); ipxitf_insert(intrfc); } return intrfc; } static int ipxitf_ioctl_real(unsigned int cmd, void *arg) { int err; switch(cmd) { case SIOCSIFADDR: { struct ifreq ifr; struct sockaddr_ipx *sipx; ipx_interface_definition f; err=verify_area(VERIFY_READ,arg,sizeof(ifr)); if(err) return err; memcpy_fromfs(&ifr,arg,sizeof(ifr)); sipx=(struct sockaddr_ipx *)&ifr.ifr_addr; if(sipx->sipx_family!=AF_IPX) return -EINVAL; f.ipx_network=sipx->sipx_network; memcpy(f.ipx_device, ifr.ifr_name, sizeof(f.ipx_device)); memcpy(f.ipx_node, sipx->sipx_node, IPX_NODE_LEN); f.ipx_dlink_type=sipx->sipx_type; f.ipx_special=sipx->sipx_special; if(sipx->sipx_action==IPX_DLTITF) return ipxitf_delete(&f); else return ipxitf_create(&f); } case SIOCGIFADDR: { struct ifreq ifr; struct sockaddr_ipx *sipx; ipx_interface *ipxif; struct device *dev; err=verify_area(VERIFY_WRITE,arg,sizeof(ifr)); if(err) return err; memcpy_fromfs(&ifr,arg,sizeof(ifr)); sipx=(struct sockaddr_ipx *)&ifr.ifr_addr; dev=dev_get(ifr.ifr_name); if(!dev) return -ENODEV; ipxif=ipxitf_find_using_phys(dev, ipx_map_frame_type(sipx->sipx_type)); if(ipxif==NULL) return -EADDRNOTAVAIL; sipx->sipx_family=AF_IPX; sipx->sipx_network=ipxif->if_netnum; memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node)); memcpy_tofs(arg,&ifr,sizeof(ifr)); return 0; } case SIOCAIPXITFCRT: err=verify_area(VERIFY_READ,arg,sizeof(char)); if(err) return err; return ipxcfg_set_auto_create(get_fs_byte(arg)); case SIOCAIPXPRISLT: err=verify_area(VERIFY_READ,arg,sizeof(char)); if(err) return err; return ipxcfg_set_auto_select(get_fs_byte(arg)); default: return -EINVAL; } } static int ipxitf_ioctl(unsigned int cmd, void *arg) { int ret; MOD_INC_USE_COUNT; ret = ipxitf_ioctl_real (cmd,arg); MOD_DEC_USE_COUNT; return ret; } /*******************************************************************************************************************\ * * * Routing tables for the IPX socket layer * * * \*******************************************************************************************************************/ static ipx_route * ipxrtr_lookup(unsigned long net) { ipx_route *r; for (r=ipx_routes; (r!=NULL) && (r->ir_net!=net); r=r->ir_next) ; return r; } static int ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node) { ipx_route *rt; /* Get a route structure; either existing or create */ rt = ipxrtr_lookup(network); if (rt==NULL) { rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC); if(rt==NULL) return -EAGAIN; rt->ir_next=ipx_routes; ipx_routes=rt; } else if (intrfc == ipx_internal_net) return(-EEXIST); rt->ir_net = network; rt->ir_intrfc = intrfc; if (node == NULL) { memset(rt->ir_router_node, '\0', IPX_NODE_LEN); rt->ir_routed = 0; } else { memcpy(rt->ir_router_node, node, IPX_NODE_LEN); rt->ir_routed=1; } return 0; } static void ipxrtr_del_routes(ipx_interface *intrfc) { ipx_route **r, *tmp; for (r = &ipx_routes; (tmp = *r) != NULL; ) { if (tmp->ir_intrfc == intrfc) { *r = tmp->ir_next; kfree_s(tmp, sizeof(ipx_route)); } else { r = &(tmp->ir_next); } } } static int ipxrtr_create(ipx_route_definition *rd) { ipx_interface *intrfc; /* Find the appropriate interface */ intrfc = ipxitf_find_using_net(rd->ipx_router_network); if (intrfc == NULL) return -ENETUNREACH; return ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node); } static int ipxrtr_delete(long net) { ipx_route **r; ipx_route *tmp; for (r = &ipx_routes; (tmp = *r) != NULL; ) { if (tmp->ir_net == net) { if (!(tmp->ir_routed)) { /* Directly connected; can't lose route */ return -EPERM; } *r = tmp->ir_next; kfree_s(tmp, sizeof(ipx_route)); return 0; } r = &(tmp->ir_next); } return -ENOENT; } /* * Checksum routine for IPX */ /* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */ static __u16 ipx_set_checksum(ipx_packet *packet,int length) { /* * NOTE: sum is a net byte order quantity, which optimizes the * loop. This only works on big and little endian machines. (I * don't know of a machine that isn't.) */ __u32 sum=0; /* * Pointer to second word - We skip the checksum field */ __u16 *p=(__u16 *)&packet->ipx_pktsize; /* * Number of complete words */ __u32 i=length>>1; char hops = packet->ipx_tctrl; packet->ipx_tctrl = 0; /* hop count excluded from checksum calc */ /* * Loop through all complete words except the checksum field */ while(--i) sum+=*p++; /* * Add on the last part word if it exists */ if(packet->ipx_pktsize&htons(1)) sum+=ntohs(0xff00)&*p; packet->ipx_tctrl = hops; /* * Do final fixup */ sum=(sum&0xffff)+(sum>>16); /* * It's a pity there's no concept of carry in C */ if(sum>=0x10000) sum++; return ~sum; }; /* * Route an outgoing frame from a socket. */ static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock) { struct sk_buff *skb; ipx_interface *intrfc; ipx_packet *ipx; int size; int ipx_offset; ipx_route *rt = NULL; int err; /* Find the appropriate interface on which to send packet */ if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL)) { usipx->sipx_network = ipx_primary_net->if_netnum; intrfc = ipx_primary_net; } else { rt = ipxrtr_lookup(usipx->sipx_network); if (rt==NULL) { return -ENETUNREACH; } intrfc = rt->ir_intrfc; } ipx_offset = intrfc->if_ipx_offset; size=sizeof(ipx_packet)+len; size += ipx_offset; skb=sock_alloc_send_skb(sk, size, 0, noblock, &err); if(skb==NULL) return err; skb_reserve(skb,ipx_offset); skb->free=1; skb->arp=1; skb->sk=sk; /* Fill in IPX header */ ipx=(ipx_packet *)skb_put(skb,sizeof(ipx_packet)); ipx->ipx_pktsize=htons(len+sizeof(ipx_packet)); ipx->ipx_tctrl=0; ipx->ipx_type=usipx->sipx_type; skb->h.raw = (unsigned char *)ipx; ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum; #ifdef CONFIG_IPX_INTERN memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN); #else if ((err = ntohs(sk->protinfo.af_ipx.port)) == 0x453 || err == 0x452) { /* RIP/SAP special handling for mars_nwe */ ipx->ipx_source.net = intrfc->if_netnum; memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN); } else { ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum; memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); } #endif ipx->ipx_source.sock = sk->protinfo.af_ipx.port; ipx->ipx_dest.net=usipx->sipx_network; memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN); ipx->ipx_dest.sock=usipx->sipx_port; memcpy_fromiovec(skb_put(skb,len),iov,len); /* * Apply checksum. Not allowed on 802.3 links. */ if(sk->no_check || intrfc->if_dlink_type==IPX_FRAME_8023) ipx->ipx_checksum=0xFFFF; else ipx->ipx_checksum=ipx_set_checksum(ipx, len+sizeof(ipx_packet)); #ifdef CONFIG_FIREWALL if(call_out_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT) { kfree_skb(skb, FREE_WRITE); return -EPERM; } #endif return ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? rt->ir_router_node : ipx->ipx_dest.node); } static int ipxrtr_route_skb(struct sk_buff *skb) { ipx_packet *ipx = (ipx_packet *) (skb->h.raw); ipx_route *r; ipx_interface *i; r = ipxrtr_lookup(ipx->ipx_dest.net); if (r == NULL) { /* no known route */ kfree_skb(skb,FREE_READ); return 0; } i = r->ir_intrfc; (void)ipxitf_send(i, skb, (r->ir_routed) ? r->ir_router_node : ipx->ipx_dest.node); return 0; } /* * We use a normal struct rtentry for route handling */ static int ipxrtr_ioctl(unsigned int cmd, void *arg) { int err; struct rtentry rt; /* Use these to behave like 'other' stacks */ struct sockaddr_ipx *sg,*st; err=verify_area(VERIFY_READ,arg,sizeof(rt)); if(err) return err; memcpy_fromfs(&rt,arg,sizeof(rt)); sg=(struct sockaddr_ipx *)&rt.rt_gateway; st=(struct sockaddr_ipx *)&rt.rt_dst; if(!(rt.rt_flags&RTF_GATEWAY)) return -EINVAL; /* Direct routes are fixed */ if(sg->sipx_family!=AF_IPX) return -EINVAL; if(st->sipx_family!=AF_IPX) return -EINVAL; switch(cmd) { case SIOCDELRT: return ipxrtr_delete(st->sipx_network); case SIOCADDRT: { struct ipx_route_definition f; f.ipx_network=st->sipx_network; f.ipx_router_network=sg->sipx_network; memcpy(f.ipx_router_node, sg->sipx_node, IPX_NODE_LEN); return ipxrtr_create(&f); } default: return -EINVAL; } } static const char * ipx_frame_name(unsigned short frame) { switch (ntohs(frame)) { case ETH_P_IPX: return "EtherII"; case ETH_P_802_2: return "802.2"; case ETH_P_SNAP: return "SNAP"; case ETH_P_802_3: return "802.3"; case ETH_P_TR_802_2: return "802.2TR"; default: return "None"; } } static const char * ipx_device_name(ipx_interface *intrfc) { return (intrfc->if_internal ? "Internal" : (intrfc->if_dev ? intrfc->if_dev->name : "Unknown")); } /* Called from proc fs */ static int ipx_interface_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { ipx_interface *i; int len=0; off_t pos=0; off_t begin=0; /* Theory.. Keep printing in the same place until we pass offset */ len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network", "Node_Address", "Primary", "Device", "Frame_Type"); for (i = ipx_interfaces; i != NULL; i = i->if_next) { len += sprintf(buffer+len, "%08lX ", ntohl(i->if_netnum)); len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X ", i->if_node[0], i->if_node[1], i->if_node[2], i->if_node[3], i->if_node[4], i->if_node[5]); len += sprintf(buffer+len, "%-9s", (i == ipx_primary_net) ? "Yes" : "No"); len += sprintf (buffer+len, "%-11s", ipx_device_name(i)); len += sprintf (buffer+len, "%s\n", ipx_frame_name(i->if_dlink_type)); /* Are we still dumping unwanted data then discard the record */ pos=begin+len; if(pos<offset) { len=0; /* Keep dumping into the buffer start */ begin=pos; } if(pos>offset+length) /* We have dumped enough */ break; } /* The data in question runs from begin to begin+len */ *start=buffer+(offset-begin); /* Start of wanted data */ len-=(offset-begin); /* Remove unwanted header data from length */ if(len>length) len=length; /* Remove unwanted tail data from length */ return len; } static int ipx_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { ipx_socket *s; ipx_interface *i; int len=0; off_t pos=0; off_t begin=0; /* Theory.. Keep printing in the same place until we pass offset */ #ifdef CONFIG_IPX_INTERN len += sprintf (buffer,"%-28s%-28s%-10s%-10s%-7s%s\n", "Local_Address", #else len += sprintf (buffer,"%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address", #endif "Remote_Address", "Tx_Queue", "Rx_Queue", "State", "Uid"); for (i = ipx_interfaces; i != NULL; i = i->if_next) { for (s = i->if_sklist; s != NULL; s = s->next) { #ifdef CONFIG_IPX_INTERN len += sprintf(buffer+len, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", htonl(s->protinfo.af_ipx.intrfc->if_netnum), s->protinfo.af_ipx.node[0], s->protinfo.af_ipx.node[1], s->protinfo.af_ipx.node[2], s->protinfo.af_ipx.node[3], s->protinfo.af_ipx.node[4], s->protinfo.af_ipx.node[5], htons(s->protinfo.af_ipx.port)); #else len += sprintf(buffer+len,"%08lX:%04X ", htonl(i->if_netnum), htons(s->protinfo.af_ipx.port)); #endif if (s->state!=TCP_ESTABLISHED) { len += sprintf(buffer+len, "%-28s", "Not_Connected"); } else { len += sprintf (buffer+len, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", htonl(s->protinfo.af_ipx.dest_addr.net), s->protinfo.af_ipx.dest_addr.node[0], s->protinfo.af_ipx.dest_addr.node[1], s->protinfo.af_ipx.dest_addr.node[2], s->protinfo.af_ipx.dest_addr.node[3], s->protinfo.af_ipx.dest_addr.node[4], s->protinfo.af_ipx.dest_addr.node[5], htons(s->protinfo.af_ipx.dest_addr.sock)); } len += sprintf (buffer+len,"%08X %08X ", s->wmem_alloc, s->rmem_alloc); len += sprintf (buffer+len,"%02X %03d\n", s->state, SOCK_INODE(s->socket)->i_uid); /* Are we still dumping unwanted data then discard the record */ pos=begin+len; if(pos<offset) { len=0; /* Keep dumping into the buffer start */ begin=pos; } if(pos>offset+length) /* We have dumped enough */ break; } } /* The data in question runs from begin to begin+len */ *start=buffer+(offset-begin); /* Start of wanted data */ len-=(offset-begin); /* Remove unwanted header data from length */ if(len>length) len=length; /* Remove unwanted tail data from length */ return len; } static int ipx_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { ipx_route *rt; int len=0; off_t pos=0; off_t begin=0; len += sprintf (buffer,"%-11s%-13s%s\n", "Network", "Router_Net", "Router_Node"); for (rt = ipx_routes; rt != NULL; rt = rt->ir_next) { len += sprintf (buffer+len,"%08lX ", ntohl(rt->ir_net)); if (rt->ir_routed) { len += sprintf (buffer+len,"%08lX %02X%02X%02X%02X%02X%02X\n", ntohl(rt->ir_intrfc->if_netnum), rt->ir_router_node[0], rt->ir_router_node[1], rt->ir_router_node[2], rt->ir_router_node[3], rt->ir_router_node[4], rt->ir_router_node[5]); } else { len += sprintf (buffer+len, "%-13s%s\n", "Directly", "Connected"); } pos=begin+len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; return len; } /*******************************************************************************************************************\ * * * Handling for system calls applied via the various interfaces to an IPX socket object * * * \*******************************************************************************************************************/ static int ipx_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) { switch(cmd) { default: return(-EINVAL); } } static int ipx_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { ipx_socket *sk; int err,opt; sk=(ipx_socket *)sock->data; if(optval==NULL) return(-EINVAL); err=verify_area(VERIFY_READ,optval,sizeof(int)); if(err) return err; opt=get_fs_long((unsigned long *)optval); switch(level) { case SOL_IPX: switch(optname) { case IPX_TYPE: sk->protinfo.af_ipx.type=opt; return 0; default: return -EOPNOTSUPP; } break; case SOL_SOCKET: return sock_setsockopt(sk,level,optname,optval,optlen); default: return -EOPNOTSUPP; } } static int ipx_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { ipx_socket *sk; int val=0; int err; sk=(ipx_socket *)sock->data; switch(level) { case SOL_IPX: switch(optname) { case IPX_TYPE: val=sk->protinfo.af_ipx.type; break; default: return -ENOPROTOOPT; } break; case SOL_SOCKET: return sock_getsockopt(sk,level,optname,optval,optlen); default: return -EOPNOTSUPP; } err=verify_area(VERIFY_WRITE,optlen,sizeof(int)); if(err) return err; put_fs_long(sizeof(int),(unsigned long *)optlen); err=verify_area(VERIFY_WRITE,optval,sizeof(int)); if (err) return err; put_fs_long(val,(unsigned long *)optval); return(0); } static int ipx_listen(struct socket *sock, int backlog) { return -EOPNOTSUPP; } static void def_callback1(struct sock *sk) { if(!sk->dead) wake_up_interruptible(sk->sleep); } static void def_callback2(struct sock *sk, int len) { if(!sk->dead) { wake_up_interruptible(sk->sleep); sock_wake_async(sk->socket, 1); } } static int ipx_create(struct socket *sock, int protocol) { ipx_socket *sk; sk=(ipx_socket *)sk_alloc(GFP_KERNEL); if(sk==NULL) return(-ENOMEM); switch(sock->type) { case SOCK_DGRAM: break; default: kfree_s((void *)sk,sizeof(*sk)); return(-ESOCKTNOSUPPORT); } sk->rcvbuf=SK_RMEM_MAX; sk->sndbuf=SK_WMEM_MAX; sk->allocation=GFP_KERNEL; sk->prot=NULL; /* So we use default free mechanisms */ skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); sk->send_head=NULL; skb_queue_head_init(&sk->back_log); sk->state=TCP_CLOSE; sk->socket=sock; sk->type=sock->type; sk->mtu=IPX_MTU; sk->no_check = 1; /* Checksum off by default */ if(sock!=NULL) { sock->data=(void *)sk; sk->sleep=sock->wait; } sk->state_change=def_callback1; sk->data_ready=def_callback2; sk->write_space=def_callback1; sk->error_report=def_callback1; sk->zapped=1; MOD_INC_USE_COUNT; return 0; } static int ipx_release(struct socket *sock, struct socket *peer) { ipx_socket *sk=(ipx_socket *)sock->data; if(sk==NULL) return(0); if(!sk->dead) sk->state_change(sk); sk->dead=1; sock->data=NULL; ipx_destroy_socket(sk); return(0); } static int ipx_dup(struct socket *newsock,struct socket *oldsock) { return(ipx_create(newsock,SOCK_DGRAM)); } static unsigned short ipx_first_free_socketnum(ipx_interface *intrfc) { unsigned short socketNum = intrfc->if_sknum; if (socketNum < IPX_MIN_EPHEMERAL_SOCKET) socketNum = IPX_MIN_EPHEMERAL_SOCKET; while (ipxitf_find_socket(intrfc, ntohs(socketNum)) != NULL) if (socketNum > IPX_MAX_EPHEMERAL_SOCKET) socketNum = IPX_MIN_EPHEMERAL_SOCKET; else socketNum++; intrfc->if_sknum = socketNum; return ntohs(socketNum); } static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) { ipx_socket *sk; ipx_interface *intrfc; struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr; sk=(ipx_socket *)sock->data; if(sk->zapped==0) return -EIO; if(addr_len!=sizeof(struct sockaddr_ipx)) return -EINVAL; intrfc = ipxitf_find_using_net(addr->sipx_network); if (intrfc == NULL) return -EADDRNOTAVAIL; if (addr->sipx_port == 0) { addr->sipx_port = ipx_first_free_socketnum(intrfc); if (addr->sipx_port == 0) return -EINVAL; } if(ntohs(addr->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser()) return -EPERM; /* protect IPX system stuff like routing/sap */ sk->protinfo.af_ipx.port=addr->sipx_port; #ifdef CONFIG_IPX_INTERN if (intrfc == ipx_internal_net) { /* The source address is to be set explicitly if the * socket is to be bound on the internal network. If a * node number 0 was specified, the default is used. */ if (memcmp(addr->sipx_node, ipx_broadcast_node, IPX_NODE_LEN) == 0) { return -EINVAL; } if (memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN) == 0) { memcpy(sk->protinfo.af_ipx.node, intrfc->if_node, IPX_NODE_LEN); } else { memcpy(sk->protinfo.af_ipx.node, addr->sipx_node, IPX_NODE_LEN); } if (ipxitf_find_internal_socket(intrfc, sk->protinfo.af_ipx.node, sk->protinfo.af_ipx.port) != NULL) { if(sk->debug) printk("IPX: bind failed because port %X in" " use.\n", (int)addr->sipx_port); return -EADDRINUSE; } } else { /* Source addresses are easy. It must be our * network:node pair for an interface routed to IPX * with the ipx routing ioctl() */ memcpy(sk->protinfo.af_ipx.node, intrfc->if_node, IPX_NODE_LEN); if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) { if(sk->debug) printk("IPX: bind failed because port %X in" " use.\n", (int)addr->sipx_port); return -EADDRINUSE; } } #else /* Source addresses are easy. It must be our network:node pair for an interface routed to IPX with the ipx routing ioctl() */ if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) { if(sk->debug) printk("IPX: bind failed because port %X in use.\n", (int)addr->sipx_port); return -EADDRINUSE; } #endif ipxitf_insert_socket(intrfc, sk); sk->zapped=0; if(sk->debug) printk("IPX: socket is bound.\n"); return 0; } static int ipx_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { ipx_socket *sk=(ipx_socket *)sock->data; struct sockaddr_ipx *addr; sk->state = TCP_CLOSE; sock->state = SS_UNCONNECTED; if(addr_len!=sizeof(*addr)) return(-EINVAL); addr=(struct sockaddr_ipx *)uaddr; if(sk->protinfo.af_ipx.port==0) /* put the autobinding in */ { struct sockaddr_ipx uaddr; int ret; uaddr.sipx_port = 0; uaddr.sipx_network = 0L; #ifdef CONFIG_IPX_INTERN memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); #endif ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx)); if (ret != 0) return (ret); } if(ipxrtr_lookup(addr->sipx_network)==NULL) return -ENETUNREACH; sk->protinfo.af_ipx.dest_addr.net=addr->sipx_network; sk->protinfo.af_ipx.dest_addr.sock=addr->sipx_port; memcpy(sk->protinfo.af_ipx.dest_addr.node, addr->sipx_node,IPX_NODE_LEN); sk->protinfo.af_ipx.type=addr->sipx_type; sock->state = SS_CONNECTED; sk->state=TCP_ESTABLISHED; return 0; } static int ipx_socketpair(struct socket *sock1, struct socket *sock2) { return(-EOPNOTSUPP); } static int ipx_accept(struct socket *sock, struct socket *newsock, int flags) { if(newsock->data) { kfree_s(newsock->data,sizeof(ipx_socket)); MOD_DEC_USE_COUNT; } return -EOPNOTSUPP; } static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { ipx_address *addr; struct sockaddr_ipx sipx; ipx_socket *sk; sk=(ipx_socket *)sock->data; *uaddr_len = sizeof(struct sockaddr_ipx); if(peer) { if(sk->state!=TCP_ESTABLISHED) return -ENOTCONN; addr=&sk->protinfo.af_ipx.dest_addr; sipx.sipx_network = addr->net; memcpy(sipx.sipx_node,addr->node,IPX_NODE_LEN); sipx.sipx_port = addr->sock; } else { if (sk->protinfo.af_ipx.intrfc != NULL) { sipx.sipx_network = sk->protinfo.af_ipx.intrfc->if_netnum; #ifdef CONFIG_IPX_INTERN memcpy(sipx.sipx_node, sk->protinfo.af_ipx.node, IPX_NODE_LEN); #else memcpy(sipx.sipx_node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); #endif } else { sipx.sipx_network = 0L; memset(sipx.sipx_node, '\0', IPX_NODE_LEN); } sipx.sipx_port = sk->protinfo.af_ipx.port; } sipx.sipx_family = AF_IPX; sipx.sipx_type = sk->protinfo.af_ipx.type; memcpy(uaddr,&sipx,sizeof(sipx)); return 0; } #if 0 /* * User to dump IPX packets (debugging) */ void dump_data(char *str,unsigned char *d, int len) { static char h2c[] = "0123456789ABCDEF"; int l,i; char *p, b[64]; for (l=0;len > 0 && l<16;l++) { p = b; for (i=0; i < 8 ; i++, --len) { if (len > 0) { *(p++) = h2c[(d[i] >> 4) & 0x0f]; *(p++) = h2c[d[i] & 0x0f]; } else { *(p++) = ' '; *(p++) = ' '; } *(p++) = ' '; } *(p++) = '-'; *(p++) = ' '; len += 8; for (i=0; i < 8 ; i++, --len) if (len > 0) *(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.'; else *(p++) = ' '; *p = '\000'; d += i; printk("%s-%04X: %s\n",str,l*8,b); } } void dump_addr(char *str,ipx_address *p) { printk("%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n", str,ntohl(p->net),p->node[0],p->node[1],p->node[2], p->node[3],p->node[4],p->node[5],ntohs(p->sock)); } void dump_hdr(char *str,ipx_packet *p) { printk("%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n", str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize), p->ipx_tctrl,p->ipx_tctrl,p->ipx_type); dump_addr(" IPX-DST",&p->ipx_dest); dump_addr(" IPX-SRC",&p->ipx_source); } void dump_pkt(char *str,ipx_packet *p) { int len = ntohs(p->ipx_pktsize); dump_hdr(str,p); if (len > 30) dump_data(str,(unsigned char *)p + 30, len - 30); } #endif int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { /* NULL here for pt means the packet was looped back */ ipx_interface *intrfc; ipx_packet *ipx; ipx=(ipx_packet *)skb->h.raw; /* Too small */ if(ntohs(ipx->ipx_pktsize)<sizeof(ipx_packet)) { kfree_skb(skb,FREE_READ); return 0; } if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) { if(ipx_set_checksum(ipx, ntohs(ipx->ipx_pktsize))!=ipx->ipx_checksum) { kfree_skb(skb,FREE_READ); return 0; } } /* Determine what local ipx endpoint this is */ intrfc = ipxitf_find_using_phys(dev, pt->type); if (intrfc == NULL) { if (ipxcfg_auto_create_interfaces && ntohl(ipx->ipx_dest.net)!=0L) { intrfc = ipxitf_auto_create(dev, pt->type); } if (intrfc == NULL) { /* Not one of ours */ kfree_skb(skb,FREE_READ); return 0; } } return ipxitf_rcv(intrfc, skb); } static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags) { ipx_socket *sk=(ipx_socket *)sock->data; struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)msg->msg_name; struct sockaddr_ipx local_sipx; int retval; if (sk->zapped) return -EIO; /* Socket not bound */ if(flags) return -EINVAL; if(usipx) { if(sk->protinfo.af_ipx.port == 0) { struct sockaddr_ipx uaddr; int ret; uaddr.sipx_port = 0; uaddr.sipx_network = 0L; #ifdef CONFIG_IPX_INTERN memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc ->if_node, IPX_NODE_LEN); #endif ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx)); if (ret != 0) return ret; } if(msg->msg_namelen <sizeof(*usipx)) return -EINVAL; if(usipx->sipx_family != AF_IPX) return -EINVAL; } else { if(sk->state!=TCP_ESTABLISHED) return -ENOTCONN; usipx=&local_sipx; usipx->sipx_family=AF_IPX; usipx->sipx_type=sk->protinfo.af_ipx.type; usipx->sipx_port=sk->protinfo.af_ipx.dest_addr.sock; usipx->sipx_network=sk->protinfo.af_ipx.dest_addr.net; memcpy(usipx->sipx_node,sk->protinfo.af_ipx.dest_addr.node,IPX_NODE_LEN); } retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, noblock); if (retval < 0) return retval; return len; } static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) { ipx_socket *sk=(ipx_socket *)sock->data; struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)msg->msg_name; struct ipx_packet *ipx = NULL; int copied = 0; int truesize; struct sk_buff *skb; int er; if(sk->err) return sock_error(sk); if (sk->zapped) return -EIO; skb=skb_recv_datagram(sk,flags,noblock,&er); if(skb==NULL) return er; if(addr_len) *addr_len=sizeof(*sipx); ipx = (ipx_packet *)(skb->h.raw); truesize=ntohs(ipx->ipx_pktsize) - sizeof(ipx_packet); copied = (truesize > size) ? size : truesize; skb_copy_datagram_iovec(skb,sizeof(struct ipx_packet),msg->msg_iov,copied); if(sipx) { sipx->sipx_family=AF_IPX; sipx->sipx_port=ipx->ipx_source.sock; memcpy(sipx->sipx_node,ipx->ipx_source.node,IPX_NODE_LEN); sipx->sipx_network=ipx->ipx_source.net; sipx->sipx_type = ipx->ipx_type; } skb_free_datagram(sk, skb); return(truesize); } static int ipx_shutdown(struct socket *sk,int how) { return -EOPNOTSUPP; } static int ipx_select(struct socket *sock , int sel_type, select_table *wait) { ipx_socket *sk=(ipx_socket *)sock->data; return datagram_select(sk,sel_type,wait); } static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg) { int err; long amount=0; ipx_socket *sk=(ipx_socket *)sock->data; switch(cmd) { case TIOCOUTQ: err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long)); if(err) return err; amount=sk->sndbuf-sk->wmem_alloc; if(amount<0) amount=0; put_fs_long(amount,(unsigned long *)arg); return 0; case TIOCINQ: { struct sk_buff *skb; /* These two are safe on a single CPU system as only user tasks fiddle here */ if((skb=skb_peek(&sk->receive_queue))!=NULL) amount=skb->len-sizeof(struct ipx_packet); err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long)); if(err) return err; put_fs_long(amount,(unsigned long *)arg); return 0; } case SIOCADDRT: case SIOCDELRT: if(!suser()) return -EPERM; return(ipxrtr_ioctl(cmd,(void *)arg)); case SIOCSIFADDR: case SIOCAIPXITFCRT: case SIOCAIPXPRISLT: if(!suser()) return -EPERM; case SIOCGIFADDR: return(ipxitf_ioctl(cmd,(void *)arg)); case SIOCIPXCFGDATA: { err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(ipx_config_data)); if(err) return err; return(ipxcfg_get_config_data((void *)arg)); } case SIOCIPXNCPCONN: { /* * This socket wants to take care of the NCP connection * handed to us in arg. */ if (!suser()) return(-EPERM); err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned short)); if (err) return err; sk->protinfo.af_ipx.ipx_ncp_conn = get_fs_word(arg); return 0; } case SIOCGSTAMP: if (sk) { if(sk->stamp.tv_sec==0) return -ENOENT; err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval)); if(err) return err; memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval)); return 0; } return -EINVAL; case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: return -EINVAL; default: return(dev_ioctl(cmd,(void *) arg)); } /*NOTREACHED*/ return(0); } static struct proto_ops ipx_proto_ops = { AF_IPX, ipx_create, ipx_dup, ipx_release, ipx_bind, ipx_connect, ipx_socketpair, ipx_accept, ipx_getname, ipx_select, ipx_ioctl, ipx_listen, ipx_shutdown, ipx_setsockopt, ipx_getsockopt, ipx_fcntl, ipx_sendmsg, ipx_recvmsg }; /* Called by protocol.c on kernel start up */ static struct packet_type ipx_8023_packet_type = { 0, /* MUTTER ntohs(ETH_P_8023),*/ NULL, /* All devices */ ipx_rcv, NULL, NULL, }; static struct packet_type ipx_dix_packet_type = { 0, /* MUTTER ntohs(ETH_P_IPX),*/ NULL, /* All devices */ ipx_rcv, NULL, NULL, }; static struct notifier_block ipx_dev_notifier={ ipxitf_device_event, NULL, 0 }; extern struct datalink_proto *make_EII_client(void); extern struct datalink_proto *make_8023_client(void); extern void destroy_EII_client(struct datalink_proto *); extern void destroy_8023_client(struct datalink_proto *); #ifdef CONFIG_PROC_FS struct proc_dir_entry ipx_procinfo = { PROC_NET_IPX, 3, "ipx", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, ipx_get_info }; struct proc_dir_entry ipx_if_procinfo = { PROC_NET_IPX_INTERFACE, 13, "ipx_interface", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, ipx_interface_get_info }; struct proc_dir_entry ipx_rt_procinfo = { PROC_NET_IPX_ROUTE, 9, "ipx_route", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, ipx_rt_get_info }; #endif static unsigned char ipx_8022_type = 0xE0; static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 }; void ipx_proto_init(struct net_proto *pro) { (void) sock_register(ipx_proto_ops.family, &ipx_proto_ops); pEII_datalink = make_EII_client(); ipx_dix_packet_type.type=htons(ETH_P_IPX); dev_add_pack(&ipx_dix_packet_type); p8023_datalink = make_8023_client(); ipx_8023_packet_type.type=htons(ETH_P_802_3); dev_add_pack(&ipx_8023_packet_type); if ((p8022_datalink = register_8022_client(ipx_8022_type, ipx_rcv)) == NULL) printk(KERN_CRIT "IPX: Unable to register with 802.2\n"); if ((p8022tr_datalink = register_8022tr_client(ipx_8022_type, ipx_rcv)) == NULL) printk(KERN_CRIT "IPX: Unable to register with 802.2TR\n"); if ((pSNAP_datalink = register_snap_client(ipx_snap_id, ipx_rcv)) == NULL) printk(KERN_CRIT "IPX: Unable to register with SNAP\n"); register_netdevice_notifier(&ipx_dev_notifier); #ifdef CONFIG_PROC_FS proc_net_register(&ipx_procinfo); proc_net_register(&ipx_if_procinfo); proc_net_register(&ipx_rt_procinfo); #endif printk(KERN_INFO "Swansea University Computer Society IPX 0.34 for NET3.035\n"); printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n"); } #ifdef MODULE /* Note on MOD_{INC,DEC}_USE_COUNT: * * Use counts are incremented/decremented when * sockets are created/deleted. * * Routes are always associated with an interface, and * allocs/frees will remain properly accounted for by * their associated interfaces. * * Ergo, before the ipx module can be removed, all IPX * sockets be closed from user space. */ static void ipx_proto_finito(void) { ipx_interface *ifc; while (ipx_interfaces) { ifc = ipx_interfaces; ipx_interfaces = ifc->if_next; ifc->if_next = NULL; ipxitf_down(ifc); } #ifdef CONFIG_PROC_FS proc_net_unregister(PROC_NET_IPX_ROUTE); proc_net_unregister(PROC_NET_IPX_INTERFACE); proc_net_unregister(PROC_NET_IPX); #endif unregister_netdevice_notifier(&ipx_dev_notifier); unregister_snap_client(ipx_snap_id); pSNAP_datalink = NULL; unregister_8022tr_client(ipx_8022_type); p8022tr_datalink = NULL; unregister_8022_client(ipx_8022_type); p8022_datalink = NULL; dev_remove_pack(&ipx_8023_packet_type); destroy_8023_client(p8023_datalink); p8023_datalink = NULL; dev_remove_pack(&ipx_dix_packet_type); destroy_EII_client(pEII_datalink); pEII_datalink = NULL; (void) sock_unregister(ipx_proto_ops.family); return; } int init_module(void) { ipx_proto_init(NULL); register_symtab(0); return 0; } void cleanup_module(void) { ipx_proto_finito(); return; } #endif /* def MODULE */