/* * 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 * Revision 0.26: Device drop kills IPX routes via it. (needed for modules) * Revision 0.27: Autobind * Revision 0.28: Small fix for multiple local networks * Revision 0.29: Assorted major errors removed * Small correction to promisc mode error fix * Asynchronous I/O support. * Changed to use notifiers and the newer packet_type stuff. * Assorted major fixes * Revision 0.30: Moved to net/ipx/... * 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. * Revision 0.32: Using sock_alloc_send_skb, firewall hooks. * Supports sendmsg/recvmsg * Revision 0.33: Internal network support, routing changes, uses a * protocol private area for ipx data. * Revision 0.34: Module support. * Revision 0.35: Checksum support. , hooked in by * Handles WIN95 discovery packets * Revision 0.36: Internal bump up for 2.1 * Revision 0.37: Began adding POSIXisms. * Revision 0.38: Asynchronous socket stuff made current. * * 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. * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For TIOCOUTQ/INQ */ #include #include #include #include #include #include #include #include #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 struct proto_ops ipx_dgram_ops; 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; return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 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(struct sock *sk) { struct sock *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(struct sock *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, struct sock *sk) { struct sock *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 struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short port) { struct sock *s; for (s=intrfc->if_sklist; (s != NULL) && (s->protinfo.af_ipx.port != port); s=s->next) ; return s; } #ifdef CONFIG_IPX_INTERN static struct sock *ipxitf_find_internal_socket(ipx_interface *intrfc, unsigned char *node, unsigned short port) { struct sock *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; struct sock *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) { struct ipxhdr *ipx = skb->nh.ipxh; struct sock *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 = 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) { struct ipxhdr *ipx = skb->nh.ipxh; struct sock *sock1 = NULL, *sock2 = NULL; struct sk_buff *skb1 = NULL, *skb2 = NULL; 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) skb1->arp=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 = 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 = 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->nh.raw= skb2->h.raw=skb_put(skb2,skb->len); 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) { struct ipxhdr *ipx = skb->nh.ipxh; 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. * The internal net will receive on all node address. */ if ((intrfc == ipx_internal_net) || memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0) { /* * Don't charge sender */ skb_orphan(skb); /* * 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_orphan(skb); 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:", skb->nh.ipxh); dump_data("ETH hdr:", skb->mac.raw, skb->nh.raw - skb->mac.raw); #endif /* * Send it out */ skb->priority = SOPRI_NORMAL; dev_queue_xmit(skb); 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) { struct ipxhdr *ipx = skb->nh.ipxh; ipx_interface *i; /* * We firewall first, ask questions later. */ if (call_in_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT) { kfree_skb(skb, FREE_READ); return 0; } /* 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)); } } #ifdef CONFIG_IPX_PPROP_ROUTING if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 && skb->pkt_type == PACKET_HOST ) { int i; ipx_interface *ifcs; struct sk_buff *skb2; long *l; char *c; c = (char *) skb->data; c += sizeof( struct ipxhdr ); l = (long *) c; i = 0; /* * Dump packet if too many hops or already seen this net */ if( ipx->ipx_tctrl < 8 ) for( ; i < ipx->ipx_tctrl ; i++ ) if( *l++ == intrfc->if_netnum ) break; if( i == ipx->ipx_tctrl ) { /* < 8 hops && input itfc not in list */ *l = intrfc->if_netnum; /* insert recvd netnum into list */ /* xmit on all other interfaces... */ for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) { /* That aren't in the list */ l = (long *) c; for( i = 0 ; i <= ipx->ipx_tctrl ; i++ ) if( ifcs->if_netnum == *l++ ) break; if( i - 1 == ipx->ipx_tctrl ) { ipx->ipx_dest.net = ifcs->if_netnum; /* See if we are allowed to firewall forward */ if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)==FW_ACCEPT) { skb2 = skb_clone(skb, GFP_ATOMIC); ipxrtr_route_skb(skb2); } } } /* * Reset network number in packet */ ipx->ipx_dest.net = intrfc->if_netnum; } } #endif 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) { /* * See if we are allowed to firewall forward */ if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT) { kfree_skb(skb, FREE_READ); return 0; } /* We only route point-to-point packets. */ if (skb->pkt_type == PACKET_HOST) { skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ); if(skb) return ipxrtr_route_skb(skb); else return 0; } 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) { switch(cmd) { case SIOCSIFADDR: { struct ifreq ifr; struct sockaddr_ipx *sipx; ipx_interface_definition f; if (copy_from_user(&ifr,arg,sizeof(ifr))) return -EFAULT; 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; int err; if (copy_from_user(&ifr,arg,sizeof(ifr))) return -EFAULT; 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)); err = copy_to_user(arg,&ifr,sizeof(ifr)); if (err) return -EFAULT; return err; } case SIOCAIPXITFCRT: { int err, val; err = get_user(val, (unsigned char *) arg); if (err) return err; return ipxcfg_set_auto_create(val); } case SIOCAIPXPRISLT: { int err, val; err = get_user(val, (unsigned char *) arg); if (err) return err; return ipxcfg_set_auto_select(val); } 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(struct ipxhdr *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; /* * 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; /* * 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(struct sock *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock) { struct sk_buff *skb; ipx_interface *intrfc; struct ipxhdr *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(struct ipxhdr)+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->arp=1; skb->sk=sk; /* Fill in IPX header */ ipx=(struct ipxhdr *)skb_put(skb,sizeof(struct ipxhdr)); ipx->ipx_pktsize=htons(len+sizeof(struct ipxhdr)); ipx->ipx_tctrl=0; ipx->ipx_type=usipx->sipx_type; skb->h.raw = (void *)skb->nh.ipxh = 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; err = memcpy_fromiovec(skb_put(skb,len),iov,len); if (err) { kfree_skb(skb, FREE_WRITE); return -EFAULT; } /* * 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(struct ipxhdr)); if(call_out_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT) { kfree_skb(skb, FREE_WRITE); return -EPERM; } 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) { struct ipxhdr *ipx = skb->nh.ipxh; 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 = copy_from_user(&rt,arg,sizeof(rt)); if (err) return -EFAULT; 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(posoffset+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) { struct sock *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 ", atomic_read(&s->wmem_alloc), atomic_read(&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(posoffset+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(posoffset+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_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk; int err,opt; sk=sock->sk; if (optlen!=sizeof(int)) return(-EINVAL); err = get_user(opt, (unsigned int *)optval); if (err) return err; switch(level) { case SOL_IPX: switch(optname) { case IPX_TYPE: sk->protinfo.af_ipx.type=opt; return 0; default: return -ENOPROTOOPT; } break; default: return -ENOPROTOOPT; } } static int ipx_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk; int val=0; int len; sk=sock->sk; switch(level) { case SOL_IPX: switch(optname) { case IPX_TYPE: val=sk->protinfo.af_ipx.type; break; default: return -ENOPROTOOPT; } break; default: return -ENOPROTOOPT; } if(get_user(len,optlen)) return -EFAULT; len=min(len,sizeof(int)); if(put_user(len, optlen)) return -EFAULT; if(copy_to_user(optval,&val,len)) return -EFAULT; return 0; } static int ipx_create(struct socket *sock, int protocol) { struct sock *sk; sk=sk_alloc(GFP_KERNEL); if(sk==NULL) return(-ENOMEM); switch(sock->type) { case SOCK_DGRAM: sock->ops = &ipx_dgram_ops; break; default: sk_free(sk); return(-ESOCKTNOSUPPORT); } sock_init_data(sock,sk); sk->destruct=NULL; sk->mtu=IPX_MTU; sk->no_check = 1; /* Checksum off by default */ MOD_INC_USE_COUNT; return 0; } static int ipx_release(struct socket *sock, struct socket *peer) { struct sock *sk=sock->sk; if(sk==NULL) return(0); if(!sk->dead) sk->state_change(sk); sk->dead=1; sock->sk=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) { struct sock *sk; ipx_interface *intrfc; struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr; sk=sock->sk; if(sk->zapped==0) return -EINVAL; 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)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) { SOCK_DEBUG(sk, "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) { SOCK_DEBUG(sk, "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) { SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port); return -EADDRINUSE; } #endif ipxitf_insert_socket(intrfc, sk); sk->zapped=0; SOCK_DEBUG(sk, "IPX: socket is bound.\n"); return 0; } static int ipx_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk=sock->sk; 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->sk) { sk_free(newsock->sk); 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; struct sock *sk; sk=sock->sk; *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(KERN_DEBUG"%s-%04X: %s\n",str,l*8,b); } } void dump_addr(char *str,ipx_address *p) { printk(KERN_DEBUG"%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,struct ipxhdr *p) { printk(KERN_DEBUG"%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,struct ipxhdr *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; struct ipxhdr *ipx; ipx = skb->nh.ipxh; /* Too small? */ if(ntohs(ipx->ipx_pktsize)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, struct scm_cookie *scm) { struct sock *sk=sock->sk; struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)msg->msg_name; struct sockaddr_ipx local_sipx; int retval; int flags = msg->msg_flags; if (sk->zapped) return -EIO; /* Socket not bound */ if (flags&~MSG_DONTWAIT) 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 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, flags&MSG_DONTWAIT); if (retval < 0) return retval; return len; } static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk=sock->sk; struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)msg->msg_name; struct ipxhdr *ipx = NULL; int copied = 0; int truesize; struct sk_buff *skb; int err; if (sk->zapped) return -ENOTCONN; skb=skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&err); if(skb==NULL) return err; ipx = skb->nh.ipxh; truesize=ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr); copied = truesize; if(copied > size) { copied=size; msg->msg_flags|=MSG_TRUNC; } err = skb_copy_datagram_iovec(skb,sizeof(struct ipxhdr),msg->msg_iov,copied); if (err) return err; msg->msg_namelen = sizeof(*sipx); 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(copied); } /* * FIXME: We have to support shutdown really. */ static int ipx_shutdown(struct socket *sk,int how) { return -EOPNOTSUPP; } static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg) { long amount=0; struct sock *sk=sock->sk; switch(cmd) { case TIOCOUTQ: amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if(amount<0) amount=0; return put_user(amount, (int *)arg); 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 ipxhdr); return put_user(amount, (int *)arg); } 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: { return(ipxcfg_get_config_data((void *)arg)); } case SIOCGSTAMP: { int ret = -EINVAL; if (sk) { if(sk->stamp.tv_sec==0) return -ENOENT; ret = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)); if (ret) ret = -EFAULT; } return 0; } 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 net_proto_family ipx_family_ops = { AF_IPX, ipx_create }; static struct proto_ops ipx_dgram_ops = { AF_IPX, ipx_dup, ipx_release, ipx_bind, ipx_connect, ipx_socketpair, ipx_accept, ipx_getname, datagram_poll, ipx_ioctl, sock_no_listen, ipx_shutdown, ipx_setsockopt, ipx_getsockopt, sock_no_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 *); 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 }; 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_family_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.38 for NET3.037\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. */ __initfunc(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_family_ops.family); return; } EXPORT_NO_SYMBOLS; int init_module(void) { ipx_proto_init(NULL); return 0; } void cleanup_module(void) { ipx_proto_finito(); return; } #endif /* def MODULE */