patch-2.4.3 linux/net/ipx/af_ipx.c

Next file: linux/net/ipx/sysctl_net_ipx.c
Previous file: linux/net/ipv6/tcp_ipv6.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.2/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
@@ -65,6 +65,12 @@
  *			Arnaldo Carvalho de Melo <acme@conectiva.com.br>,
  *			December, 2000
  *	Revision 044:	Call ipxitf_hold on NETDEV_UP (acme)
+ *	Revision 045:	fix PPROP routing bug (acme)
+ *	Revision 046:	Further fixes to PPROP, ipxitf_create_internal was
+ *			doing an unneeded MOD_INC_USE_COUNT, implement
+ *			sysctl for ipx_pprop_broacasting, fix the ipx sysctl
+ *			handling, making it dynamic, some cleanups, thanks to
+ *			Petr Vandrovec for review and good suggestions. (acme)
  *
  *	Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
  *	pair. Also, now usage count is managed this way
@@ -81,7 +87,6 @@
  */
 
 #include <linux/config.h>
-#if defined (CONFIG_IPX) || defined (CONFIG_IPX_MODULE)
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
@@ -111,14 +116,14 @@
 #include <linux/init.h>
 #include <linux/if_arp.h>
 
-#ifdef MODULE
-static void ipx_proto_finito(void);
-#endif /* def MODULE */
+extern void ipx_register_sysctl(void);
+extern void ipx_unregister_sysctl(void);
 
 /* Configuration Variables */
 static unsigned char ipxcfg_max_hops = 16;
 static char ipxcfg_auto_select_primary;
 static char ipxcfg_auto_create_interfaces;
+static int sysctl_ipx_pprop_broadcasting = 1;
 
 /* Global Variables */
 static struct datalink_proto *p8022_datalink;
@@ -139,6 +144,8 @@
 static ipx_interface *ipx_primary_net;
 static ipx_interface *ipx_internal_net;
 
+#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0]))
+
 #undef IPX_REFCNT_DEBUG
 #ifdef IPX_REFCNT_DEBUG
 atomic_t ipx_sock_nr;
@@ -450,11 +457,12 @@
 	spin_lock_bh(&ipx_interfaces_lock);
 	for (i = ipx_interfaces; i;) {
 		tmp = i->if_next;
-		if (i->if_dev == dev)
+		if (i->if_dev == dev) {
 			if (event == NETDEV_UP)
 				ipxitf_hold(i);
 			else
 				__ipxitf_put(i);
+		}
 		i = tmp;
 	}
 	spin_unlock_bh(&ipx_interfaces_lock);
@@ -674,17 +682,30 @@
 	return skb2;
 }
 
-/* caller must hold a reference to intrfc */
+/* caller must hold a reference to intrfc and the skb has to be unshared */
 
 static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
 {
 	struct ipxhdr *ipx = skb->nh.ipxh;
-	struct ipx_cb *cb = (struct ipx_cb *) skb->cb;
 	struct net_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;
+
+	ipx->ipx_tctrl = IPX_SKB_CB(skb)->ipx_tctrl;
+	ipx->ipx_dest.net = IPX_SKB_CB(skb)->ipx_dest_net;
+	ipx->ipx_source.net = IPX_SKB_CB(skb)->ipx_source_net;
+
+	/* see if we need to include the netnum in the route list */
+	if (IPX_SKB_CB(skb)->last_hop.index >= 0) {
+		u32 *last_hop = (u32 *)(((u8 *) skb->data) +
+				sizeof(struct ipxhdr) +
+				IPX_SKB_CB(skb)->last_hop.index *
+				sizeof(u32));
+		*last_hop = IPX_SKB_CB(skb)->last_hop.netnum;
+		IPX_SKB_CB(skb)->last_hop.index = -1;
+	}
 	
 	/* 
 	 * We need to know how many skbuffs it will take to send out this
@@ -701,7 +722,7 @@
 	 * up clones.
 	 */
 	 
-	if (cb->ipx_dest_net == intrfc->if_netnum) {
+	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.
@@ -730,7 +751,7 @@
 	 * We are still charging the sender. Which is right - the driver
 	 * free will handle this fairly.
 	 */
-	if (cb->ipx_source_net != intrfc->if_netnum) {
+	if (ipx->ipx_source.net != intrfc->if_netnum) {
 		/*
 		 * Unshare the buffer before modifying the count in
 		 * case its a flood or tcpdump
@@ -738,7 +759,7 @@
 		skb = skb_unshare(skb, GFP_ATOMIC);
 		if (!skb)
 			return 0;
-		if (++(cb->ipx_tctrl) > ipxcfg_max_hops)
+		if (++ipx->ipx_tctrl > ipxcfg_max_hops)
 			send_to_wire = 0;
 	}
 
@@ -759,17 +780,6 @@
 	if (!skb)
 		return 0;
 
-	ipx->ipx_tctrl = cb->ipx_tctrl;
-	ipx->ipx_dest.net = cb->ipx_dest_net;
-	ipx->ipx_source.net = cb->ipx_source_net;
-	/* see if we need to include the netnum in the route list */
-	if (cb->last_hop_index >= 0) {
-		u32 *last_hop = (u32 *)(((u8 *) skb->data) +
-				sizeof(struct ipxhdr) + cb->last_hop_index *
-				sizeof(u32));
-		*last_hop = intrfc->if_netnum;
-	}
-
 	/* set up data link and physical headers */
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_IPX);
@@ -789,93 +799,40 @@
 
 static const char * ipx_frame_name(unsigned short);
 static const char * ipx_device_name(ipx_interface *);
+static void ipxitf_discover_netnum(ipx_interface *intrfc, struct sk_buff *skb);
+static int ipxitf_pprop(ipx_interface *intrfc, struct sk_buff *skb);
 
 static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
 {
 	struct ipxhdr	*ipx = skb->nh.ipxh;
-	struct ipx_cb *cb = (struct ipx_cb *) skb->cb;
 	int ret = 0;
 
 	ipxitf_hold(intrfc);
 
 	/* See if we should update our network number */
-	if (!intrfc->if_netnum && /* net number of intrfc not known yet */
-	   cb->ipx_source_net == cb->ipx_dest_net && /* intra packet */
-	   cb->ipx_source_net) {
-		ipx_interface *i = ipxitf_find_using_net(cb->ipx_source_net);
-		/* 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) {
-			intrfc->if_netnum = cb->ipx_source_net;
-			ipxitf_add_local_route(intrfc);
-		} else {
-			printk(KERN_WARNING "IPX: Network number collision %lx\n        %s %s and %s %s\n",
-				(long unsigned int) htonl(cb->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));
-			ipxitf_put(i);
-		}
-	}
+	if (!intrfc->if_netnum) /* net number of intrfc not known yet */
+ 		ipxitf_discover_netnum(intrfc, skb);
 	
-	cb->last_hop_index = -1;
-
-	if (ipx->ipx_type == IPX_TYPE_PPROP && cb->ipx_tctrl < 8 &&
-	    skb->pkt_type != PACKET_OTHERHOST &&
-	    /* header + 8 network numbers */ 
-	    ntohs(ipx->ipx_pktsize) >= sizeof(struct ipxhdr) + 8 * 4) {
-		int i;
-        	ipx_interface *ifcs;
-		struct sk_buff *skb2;
-		char *c = ((char *) skb->data) + sizeof(struct ipxhdr);
-  		u32 *l = (u32 *) c;
-
-		/* Dump packet if already seen this net */
-		for (i = 0; i < cb->ipx_tctrl; i++)
-			if (*l++ == intrfc->if_netnum)
-				break;
-
-		if (i == cb->ipx_tctrl) {
-			/* < 8 hops && input itfc not in list */
- 			/* insert recvd netnum into list */
-			cb->last_hop_index = i;
-			cb->ipx_tctrl++;
-			/* xmit on all other interfaces... */
-			spin_lock_bh(&ipx_interfaces_lock);
-			for (ifcs = ipx_interfaces; ifcs;
-			     ifcs = ifcs->if_next) {
-				/* Except unconfigured interfaces */
-				if (!ifcs->if_netnum)
-					continue;
-					
-				/* That aren't in the list */
-				l = (__u32 *) c;
-				for (i = 0; i <= cb->ipx_tctrl; i++)
-					if (ifcs->if_netnum == *l++)
-						break;
-				if (i - 1 == cb->ipx_tctrl) {
-					cb->ipx_dest_net = ifcs->if_netnum;
-					skb2=skb_clone(skb, GFP_ATOMIC);
-					if (skb2)
-						ipxrtr_route_skb(skb2);
-				}
-			}
-			spin_unlock_bh(&ipx_interfaces_lock);
-		}
+	IPX_SKB_CB(skb)->last_hop.index = -1;
+	if (ipx->ipx_type == IPX_TYPE_PPROP) {
+		ret = ipxitf_pprop(intrfc, skb);
+		if (ret)
+			goto out_free_skb;
 	}
 
-	if (!cb->ipx_dest_net)
-		cb->ipx_dest_net = intrfc->if_netnum;
-	if (!cb->ipx_source_net)
-		cb->ipx_source_net = intrfc->if_netnum;
-
-	if (intrfc->if_netnum != cb->ipx_dest_net) {
+	/* local processing follows */
+	if (!IPX_SKB_CB(skb)->ipx_dest_net)
+		IPX_SKB_CB(skb)->ipx_dest_net = intrfc->if_netnum;
+	if (!IPX_SKB_CB(skb)->ipx_source_net)
+		IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
+
+	/* it doesn't make sense to route a pprop packet, there's no meaning
+	 * in the ipx_dest_net for such packets */
+	if (ipx->ipx_type != IPX_TYPE_PPROP &&
+	    intrfc->if_netnum != IPX_SKB_CB(skb)->ipx_dest_net) {
 		/* We only route point-to-point packets. */
 		if (skb->pkt_type == PACKET_HOST) {
-			skb=skb_unshare(skb, GFP_ATOMIC);
+			skb = skb_unshare(skb, GFP_ATOMIC);
 			if (skb)
 				ret = ipxrtr_route_skb(skb);
 			goto out_intrfc;
@@ -899,6 +856,124 @@
 	return ret;
 }
 
+static void ipxitf_discover_netnum(ipx_interface *intrfc, struct sk_buff *skb)
+{ 
+	const struct ipx_cb *cb = IPX_SKB_CB(skb);
+
+	/* see if this is an intra packet: source_net == dest_net */
+	if (cb->ipx_source_net == cb->ipx_dest_net && cb->ipx_source_net) {
+		ipx_interface *i = ipxitf_find_using_net(cb->ipx_source_net);
+		/* 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) {
+			intrfc->if_netnum = cb->ipx_source_net;
+			ipxitf_add_local_route(intrfc);
+		} else {
+			printk(KERN_WARNING "IPX: Network number collision "
+				"%lx\n        %s %s and %s %s\n",
+				(unsigned long) htonl(cb->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));
+			ipxitf_put(i);
+		}
+	}
+}
+
+/**
+ * ipxitf_pprop - Process packet propagation IPX packet type 0x14, used for
+ * 		  NetBIOS broadcasts
+ * @intrfc: IPX interface receiving this packet
+ * @skb: Received packet
+ *
+ * Checks if packet is valid: if its more than %IPX_MAX_PPROP_HOPS hops or if it
+ * is smaller than a IPX header + the room for %IPX_MAX_PPROP_HOPS hops we drop
+ * it, not even processing it locally, if it has exact %IPX_MAX_PPROP_HOPS we
+ * don't broadcast it, but process it locally. See chapter 5 of Novell's "IPX
+ * RIP and SAP Router Specification", Part Number 107-000029-001.
+ * 
+ * If it is valid, check if we have pprop broadcasting enabled by the user,
+ * if not, just return zero for local processing.
+ *
+ * If it is enabled check the packet and don't broadcast it if we have already
+ * seen this packet.
+ *
+ * Broadcast: send it to the interfaces that aren't on the packet visited nets
+ * array, just after the IPX header.
+ *
+ * Returns -EINVAL for invalid packets, so that the calling function drops
+ * the packet without local processing. 0 if packet is to be locally processed.
+ */
+static int ipxitf_pprop(ipx_interface *intrfc, struct sk_buff *skb)
+{
+	struct ipxhdr *ipx = skb->nh.ipxh;
+	int i, ret = -EINVAL;
+	ipx_interface *ifcs;
+	char *c;
+	u32 *l;
+
+	/* Illegal packet - too many hops or too short */
+	/* We decide to throw it away: no broadcasting, no local processing.
+	 * NetBIOS unaware implementations route them as normal packets -
+	 * tctrl <= 15, any data payload... */
+	if (IPX_SKB_CB(skb)->ipx_tctrl > IPX_MAX_PPROP_HOPS ||
+	    ntohs(ipx->ipx_pktsize) < sizeof(struct ipxhdr) +
+	    				IPX_MAX_PPROP_HOPS * sizeof(u32))
+		goto out;
+	/* are we broadcasting this damn thing? */
+	ret = 0;
+	if (!sysctl_ipx_pprop_broadcasting)
+		goto out;
+	/* We do broadcast packet on the IPX_MAX_PPROP_HOPS hop, but we
+	 * process it locally. All previous hops broadcasted it, and process it
+	 * locally. */
+	if (IPX_SKB_CB(skb)->ipx_tctrl == IPX_MAX_PPROP_HOPS)
+		goto out;
+	
+	c = ((u8 *) ipx) + sizeof(struct ipxhdr);
+	l = (u32 *) c;
+
+	/* Don't broadcast packet if already seen this net */
+	for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
+		if (*l++ == intrfc->if_netnum)
+			goto out;
+
+	/* < IPX_MAX_PPROP_HOPS hops && input interface not in list. Save the
+	 * position where we will insert recvd netnum into list, later on,
+	 * in ipxitf_send */
+	IPX_SKB_CB(skb)->last_hop.index = i;
+	IPX_SKB_CB(skb)->last_hop.netnum = intrfc->if_netnum;
+	/* xmit on all other interfaces... */
+	spin_lock_bh(&ipx_interfaces_lock);
+	for (ifcs = ipx_interfaces; ifcs; ifcs = ifcs->if_next) {
+		/* Except unconfigured interfaces */
+		if (!ifcs->if_netnum)
+			continue;
+					
+		/* That aren't in the list */
+		if (ifcs == intrfc)
+			continue;
+		l = (__u32 *) c;
+		/* don't consider the last entry in the packet list,
+		 * it is our netnum, and it is not there yet */
+		for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
+			if (ifcs->if_netnum == *l++)
+				break;
+		if (i == IPX_SKB_CB(skb)->ipx_tctrl) {
+			struct sk_buff *s = skb_copy(skb, GFP_ATOMIC);
+
+			if (s) {
+				IPX_SKB_CB(s)->ipx_dest_net = ifcs->if_netnum;
+				ipxrtr_route_skb(s);
+			}
+		}
+	}
+	spin_unlock_bh(&ipx_interfaces_lock);
+out:	return ret;
+}
+
 static void ipxitf_insert(ipx_interface *intrfc)
 {
 	ipx_interface *i;
@@ -918,6 +993,30 @@
 		ipx_primary_net = intrfc;
 }
 
+static ipx_interface *ipxitf_alloc(struct net_device *dev, __u32 netnum,
+				   unsigned short dlink_type,
+				   struct datalink_proto *dlink,
+				   unsigned char internal, int ipx_offset)
+{
+	ipx_interface *intrfc = kmalloc(sizeof(ipx_interface), GFP_ATOMIC);
+
+	if (intrfc) {
+		intrfc->if_dev		= dev;
+		intrfc->if_netnum	= netnum;
+		intrfc->if_dlink_type 	= dlink_type;
+		intrfc->if_dlink 	= dlink;
+		intrfc->if_internal 	= internal;
+		intrfc->if_ipx_offset 	= ipx_offset;
+		intrfc->if_sknum 	= IPX_MIN_EPHEMERAL_SOCKET;
+		intrfc->if_sklist 	= NULL;
+		atomic_set(&intrfc->refcnt, 1);
+		spin_lock_init(&intrfc->if_sklist_lock);
+		MOD_INC_USE_COUNT;
+	}
+
+	return intrfc;
+}
+
 static int ipxitf_create_internal(ipx_interface_definition *idef)
 {
 	ipx_interface *intrfc;
@@ -935,23 +1034,11 @@
 		ipxitf_put(intrfc);
 		return -EADDRINUSE;
 	}
-
-	intrfc = kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+	intrfc = ipxitf_alloc(NULL, idef->ipx_network, 0, NULL, 1, 0);
 	if (!intrfc)
 		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 = ipx_primary_net = intrfc;
-	spin_lock_init(&intrfc->if_sklist_lock);
-	atomic_set(&intrfc->refcnt, 1);
-	MOD_INC_USE_COUNT;
 	ipxitf_hold(intrfc);
 	ipxitf_insert(intrfc);
 
@@ -1040,7 +1127,8 @@
 
 		case IPX_FRAME_NONE:
 		default:
-			break;
+			err = -EPROTONOSUPPORT;
+			goto out_dev;
 	}
 
 	err = -ENETDOWN;
@@ -1052,28 +1140,18 @@
 	if (dev->addr_len > IPX_NODE_LEN)
 		goto out_dev;
 
-	err = -EPROTONOSUPPORT;
-	if (!datalink)
-		goto out_dev;
-
 	intrfc = ipxitf_find_using_phys(dev, dlink_type);
 	if (!intrfc) {
 		/* Ok now create */
-		intrfc = kmalloc(sizeof(ipx_interface), GFP_ATOMIC);
+		intrfc = ipxitf_alloc(dev, idef->ipx_network, dlink_type,
+				      datalink, 0, dev->hard_header_len +
+					datalink->header_length);
 		err = -EAGAIN;
 		if (!intrfc)
 			goto out_dev;
-		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)) {
 			memset(intrfc->if_node, 0, IPX_NODE_LEN);
@@ -1081,9 +1159,6 @@
 				dev->dev_addr, dev->addr_len);
 		} else
 			memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN);
-		spin_lock_init(&intrfc->if_sklist_lock);
-		atomic_set(&intrfc->refcnt, 1);
-		MOD_INC_USE_COUNT;
 		ipxitf_hold(intrfc);
 		ipxitf_insert(intrfc);
 	}
@@ -1145,8 +1220,15 @@
 static ipx_interface *ipxitf_auto_create(struct net_device *dev, 
 					 unsigned short dlink_type)
 {
-	struct datalink_proto *datalink = NULL;
-	ipx_interface *intrfc;
+	ipx_interface *intrfc = NULL;
+	struct datalink_proto *datalink;
+
+	if (!dev)
+		goto out;
+
+	/* Check addresses are suitable */
+	if (dev->addr_len > IPX_NODE_LEN)
+		goto out;
 
 	switch (htons(dlink_type)) {
 		case ETH_P_IPX:
@@ -1166,38 +1248,23 @@
 			break;
 
 		default:
-			return NULL;
+			goto out;
 	}
 
-	if (!dev)
-		return NULL;
-
-	/* Check addresses are suitable */
-	if (dev->addr_len > IPX_NODE_LEN)
-		return NULL;
+	intrfc = ipxitf_alloc(dev, 0, dlink_type, datalink, 0,
+				dev->hard_header_len + datalink->header_length);
 
-	intrfc = kmalloc(sizeof(ipx_interface), GFP_ATOMIC);
 	if (intrfc) {
-		intrfc->if_dev		= dev;
-		intrfc->if_netnum	= 0;
-		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);
 		spin_lock_init(&intrfc->if_sklist_lock);
 		atomic_set(&intrfc->refcnt, 1);
-		MOD_INC_USE_COUNT;
 		ipxitf_insert(intrfc);
 		dev_hold(dev);
 	}
 
-	return intrfc;
+out:	return intrfc;
 }
 
 static int ipxitf_ioctl(unsigned int cmd, void *arg)
@@ -1283,6 +1350,17 @@
 *                                                                          *
 \**************************************************************************/
 
+static inline void ipxrtr_hold(ipx_route *rt)
+{
+	atomic_inc(&rt->refcnt);
+}
+
+static inline void ipxrtr_put(ipx_route *rt)
+{
+	if (atomic_dec_and_test(&rt->refcnt))
+		kfree(rt);
+}
+
 static ipx_route *ipxrtr_lookup(__u32 net)
 {
 	ipx_route *r;
@@ -1290,6 +1368,8 @@
 	read_lock_bh(&ipx_routes_lock);
 	for (r = ipx_routes; r && r->ir_net != net; r = r->ir_next)
 		;
+	if (r)
+		ipxrtr_hold(r);
 	read_unlock_bh(&ipx_routes_lock);
 
 	return r;
@@ -1300,21 +1380,27 @@
 static int ipxrtr_add_route(__u32 network, ipx_interface *intrfc, unsigned char *node)
 {
 	ipx_route *rt;
+	int ret;
 
 	/* Get a route structure; either existing or create */
 	rt = ipxrtr_lookup(network);
 	if (!rt) {
 		rt = kmalloc(sizeof(ipx_route),GFP_ATOMIC);
+		ret = -EAGAIN;
 		if (!rt)
-			return -EAGAIN;
+			goto out;
 
+		atomic_set(&rt->refcnt, 1);
+		ipxrtr_hold(rt);
 		write_lock_bh(&ipx_routes_lock);
 		rt->ir_next	= ipx_routes;
 		ipx_routes	= rt;
 		write_unlock_bh(&ipx_routes_lock);
+	} else {
+		ret = -EEXIST;
+		if (intrfc == ipx_internal_net)
+			goto out_put;
 	}
-	else if (intrfc == ipx_internal_net)
-		return -EEXIST;
 
 	rt->ir_net 	= network;
 	rt->ir_intrfc 	= intrfc;
@@ -1326,7 +1412,10 @@
 		rt->ir_routed = 1;
 	}
 
-	return 0;
+	ret = 0;
+out_put:
+	ipxrtr_put(rt);
+out:	return ret;
 }
 
 static void ipxrtr_del_routes(ipx_interface *intrfc)
@@ -1337,7 +1426,7 @@
 	for (r = &ipx_routes; (tmp = *r) != NULL;) {
 		if (tmp->ir_intrfc == intrfc) {
 			*r = tmp->ir_next;
-			kfree(tmp);
+			ipxrtr_put(tmp);
 		} else
 			r = &(tmp->ir_next);
 	}
@@ -1373,7 +1462,7 @@
 				goto out;
 
 			*r = tmp->ir_next;
-			kfree(tmp);
+			ipxrtr_put(tmp);
 			err = 0;
 			goto out;
 		}
@@ -1432,7 +1521,6 @@
 	struct sk_buff *skb;
 	ipx_interface *intrfc;
 	struct ipxhdr *ipx;
-	struct ipx_cb *cb;
 	int size;
 	int ipx_offset;
 	ipx_route *rt = NULL;
@@ -1444,9 +1532,9 @@
 		intrfc = ipx_primary_net;
 	} else {
 		rt = ipxrtr_lookup(usipx->sipx_network);
+		err = -ENETUNREACH;
 		if (!rt)
-			return -ENETUNREACH;
-
+			goto out;
 		intrfc = rt->ir_intrfc;
 	}
 
@@ -1456,45 +1544,44 @@
 
 	skb = sock_alloc_send_skb(sk, size, 0, noblock, &err);
 	if (!skb)
-		goto out;
+		goto out_put;
 
 	skb_reserve(skb,ipx_offset);
 	skb->sk = sk;
-	cb = (struct ipx_cb *) skb->cb;
 
 	/* Fill in IPX header */
 	ipx = (struct ipxhdr *)skb_put(skb, sizeof(struct ipxhdr));
 	ipx->ipx_pktsize= htons(len + sizeof(struct ipxhdr));
-	cb->ipx_tctrl = 0;
+	IPX_SKB_CB(skb)->ipx_tctrl = 0;
 	ipx->ipx_type 	= usipx->sipx_type;
 	skb->h.raw 	= (void *)skb->nh.ipxh = ipx;
 
-	cb->last_hop_index = -1;
-
+	IPX_SKB_CB(skb)->last_hop.index = -1;
 #ifdef CONFIG_IPX_INTERN
-	cb->ipx_source_net = sk->protinfo.af_ipx.intrfc->if_netnum;
+	IPX_SKB_CB(skb)->ipx_source_net = sk->protinfo.af_ipx.intrfc->if_netnum;
 	memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
 #else
 	err = ntohs(sk->protinfo.af_ipx.port);
 	if (err == 0x453 || err == 0x452) {
 		/* RIP/SAP special handling for mars_nwe */
-		cb->ipx_source_net = intrfc->if_netnum;
+		IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
 		memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN);
 	} else {
-		cb->ipx_source_net = sk->protinfo.af_ipx.intrfc->if_netnum;
+		IPX_SKB_CB(skb)->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	/* CONFIG_IPX_INTERN */
 
 	ipx->ipx_source.sock 	= sk->protinfo.af_ipx.port;
-	cb->ipx_dest_net = usipx->sipx_network;
+	IPX_SKB_CB(skb)->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);
-		goto out;
+		goto out_put;
 	}	
 
 	/* Apply checksum. Not allowed on 802.3 links. */
@@ -1505,15 +1592,19 @@
 
 	err = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? 
 				rt->ir_router_node : ipx->ipx_dest.node);
-out:	ipxitf_put(intrfc);
-	return err;
+out_put:
+	ipxitf_put(intrfc);
+	if (rt)
+		ipxrtr_put(rt);
+out:	return err;
 }
-	
+
+/* the skb has to be unshared, we'll end up calling ipxitf_send, that'll
+ * modify the packet */
 int ipxrtr_route_skb(struct sk_buff *skb)
 {
 	struct ipxhdr *ipx = skb->nh.ipxh;
-	struct ipx_cb *cb = (struct ipx_cb *) skb->cb;
-	ipx_route *r = ipxrtr_lookup(cb->ipx_dest_net);
+	ipx_route *r = ipxrtr_lookup(IPX_SKB_CB(skb)->ipx_dest_net);
 
 	if (!r) {	/* no known route */
 		kfree_skb(skb);
@@ -1524,6 +1615,7 @@
 	ipxitf_send(r->ir_intrfc, skb, (r->ir_routed) ?
 			r->ir_router_node : ipx->ipx_dest.node);
 	ipxitf_put(r->ir_intrfc);
+	ipxrtr_put(r);
 
 	return 0;
 }
@@ -2033,6 +2125,7 @@
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_ipx *addr;
+	ipx_route *rt;
 
 	sk->state	= TCP_CLOSE;
 	sock->state 	= SS_UNCONNECTED;
@@ -2064,8 +2157,8 @@
 
         /* We can either connect to primary network or somewhere
 	 * we can route to */
-	if (!(!addr->sipx_network && ipx_primary_net) &&
-	    !ipxrtr_lookup(addr->sipx_network))
+	rt = ipxrtr_lookup(addr->sipx_network);
+	if (!rt && !(!addr->sipx_network && ipx_primary_net))
 		return -ENETUNREACH;
 
 	sk->protinfo.af_ipx.dest_addr.net  = addr->sipx_network;
@@ -2079,6 +2172,8 @@
 		sk->state 	= TCP_ESTABLISHED;
 	}
 
+	if (rt)
+		ipxrtr_put(rt);
 	return 0;
 }
 
@@ -2129,7 +2224,6 @@
 	/* NULL here for pt means the packet was looped back */
 	ipx_interface *intrfc;
 	struct ipxhdr *ipx;
-	struct ipx_cb *cb;
 	u16 ipx_pktsize;
 	int ret;
 		
@@ -2151,21 +2245,22 @@
 	   ipx->ipx_checksum != ipx_cksum(ipx, ipx_pktsize))
 		goto drop;
 
-	cb = (struct ipx_cb *) skb->cb;
-	cb->ipx_tctrl = ipx->ipx_tctrl;
-	cb->ipx_dest_net = ipx->ipx_dest.net;
-	cb->ipx_source_net = ipx->ipx_source.net;
+	IPX_SKB_CB(skb)->ipx_tctrl = ipx->ipx_tctrl;
+	IPX_SKB_CB(skb)->ipx_dest_net = ipx->ipx_dest.net;
+	IPX_SKB_CB(skb)->ipx_source_net = ipx->ipx_source.net;
 
 	/* Determine what local ipx endpoint this is */
 	intrfc = ipxitf_find_using_phys(dev, pt->type);
 	if (!intrfc) {
 		if (ipxcfg_auto_create_interfaces &&
-		   ntohl(cb->ipx_dest_net)) {
+		   ntohl(IPX_SKB_CB(skb)->ipx_dest_net)) {
 			intrfc = ipxitf_auto_create(dev, pt->type);
-			ipxitf_hold(intrfc);
+			if (intrfc)
+				ipxitf_hold(intrfc);
 		}
 
 		if (!intrfc)	/* Not one of ours */
+				/* or invalid packet for auto creation */
 			goto drop;
 	}
 
@@ -2290,11 +2385,10 @@
 	msg->msg_namelen = sizeof(*sipx);
 
 	if (sipx) {
-		struct ipx_cb *cb = (struct ipx_cb *) skb->cb;
 		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	= cb->ipx_source_net;
+		sipx->sipx_network	= IPX_SKB_CB(skb)->ipx_source_net;
 		sipx->sipx_type 	= ipx->ipx_type;
 	}
 	err = copied;
@@ -2469,6 +2563,10 @@
 
 static unsigned char ipx_8022_type = 0xE0;
 static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
+static const char banner[] __initdata =
+	KERN_INFO "NET4: Linux IPX 0.46 for NET4.0\n"
+	KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n" \
+	KERN_INFO "IPX Portions Copyright (c) 2000, 2001 Conectiva, Inc.\n";
 
 static int __init ipx_init(void)
 {
@@ -2489,14 +2587,13 @@
 		printk(KERN_CRIT "IPX: Unable to register with SNAP\n");
 
 	register_netdevice_notifier(&ipx_dev_notifier);
+	ipx_register_sysctl();
 #ifdef CONFIG_PROC_FS
 	proc_net_create("ipx", 0, ipx_get_info);
 	proc_net_create("ipx_interface", 0, ipx_interface_get_info);
 	proc_net_create("ipx_route", 0, ipx_rt_get_info);
 #endif
-	printk(KERN_INFO "NET4: Linux IPX 0.44 for NET4.0\n");
-	printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
-	printk(KERN_INFO "IPX Portions Copyright (c) 2000 Conectiva, Inc.\n");
+	printk(banner);
 	return 0;
 }
 
@@ -2506,8 +2603,13 @@
 int ipx_if_offset(unsigned long ipx_net_number)
 {
 	ipx_route *rt = ipxrtr_lookup(ipx_net_number);
+	int ret = -ENETUNREACH;
 
-	return rt ? rt->ir_intrfc->if_ipx_offset : -ENETUNREACH;
+	if (!rt)
+		goto out;
+	ret = rt->ir_intrfc->if_ipx_offset;
+	ipxrtr_put(rt);
+out:	return ret;
 }
 
 /* Export symbols for higher layers */
@@ -2530,8 +2632,7 @@
  * sockets be closed from user space.
  */
 
-#ifdef MODULE
-static void ipx_proto_finito(void)
+static void __exit ipx_proto_finito(void)
 {
 	/* no need to worry about having anything on the ipx_interfaces
 	 * list, when a interface is created we increment the module
@@ -2541,6 +2642,7 @@
 	proc_net_remove("ipx_route");
 	proc_net_remove("ipx_interface");
 	proc_net_remove("ipx");
+	ipx_unregister_sysctl();
 
 	unregister_netdevice_notifier(&ipx_dev_notifier);
 
@@ -2562,5 +2664,3 @@
 }
 
 module_exit(ipx_proto_finito);
-#endif /* MODULE */
-#endif /* CONFIG_IPX || CONFIG_IPX_MODULE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)