11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Implements an IPX socket layer.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	This code is derived from work by
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Ross Biro	: 	Writing the original IP stack
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Fred Van Kempen :	Tidying up the TCP/IP
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Many thanks go to Keith Baker, Institute For Industrial Information
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Technology Ltd, Swansea University for allowing me to work on this
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	in my own time even though it was in some ways related to commercial
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	work I am currently employed to do there.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	All the material in this file is subject to the Gnu license version 2.
14981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki *	Neither Alan Cox nor the Swansea University Computer Society admit
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	liability nor provide warranty for any of this software. This material
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	is provided as is and at no charge.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Portions Copyright (c) 2000-2003 Conectiva, Inc. <acme@conectiva.com.br>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Neither Arnaldo Carvalho de Melo nor Conectiva, Inc. admit liability nor
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	provide warranty for any of this software. This material is provided
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	"AS-IS" and at no charge.
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Neither Greg Page nor Caldera, Inc. admit liability nor provide
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	warranty for any of this software. This material is provided
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	"AS-IS" and at no charge.
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	See net/ipx/ChangeLog.
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
314fc268d24ceb9f4150777c1b5b2b8e6214e56b2bRandy Dunlap#include <linux/capability.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/if_arp.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/if_ether.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ipx.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/list.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/net.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/uio.h>
435a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/socket.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sockios.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/termios.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/ipx.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/p8022.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/psnap.h>
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/sock.h>
557780d8ae4ae1960ef7c0570de0a1ecd7b60c8152Rashika Kheria#include <net/datalink.h>
56c752f0739f09b803aed191c4765a3b6650a08653Arnaldo Carvalho de Melo#include <net/tcp_states.h>
57535d3ae9c808e6a5248f0524dbc7a9e997cf3288Rashika Kheria#include <net/net_namespace.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Configuration Variables */
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char ipxcfg_max_hops = 16;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char ipxcfg_auto_select_primary;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char ipxcfg_auto_create_interfaces;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint sysctl_ipx_pprop_broadcasting = 1;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Global Variables */
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct datalink_proto *p8022_datalink;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct datalink_proto *pEII_datalink;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct datalink_proto *p8023_datalink;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct datalink_proto *pSNAP_datalink;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7390ddc4f0470427df306f308ad03db6b6b21644b8Eric Dumazetstatic const struct proto_ops ipx_dgram_ops;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsLIST_HEAD(ipx_interfaces);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsDEFINE_SPINLOCK(ipx_interfaces_lock);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ipx_interface *ipx_primary_net;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ipx_interface *ipx_internal_net;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ipx_interface *ipx_interfaces_head(void)
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *rc = NULL;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!list_empty(&ipx_interfaces))
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = list_entry(ipx_interfaces.next,
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				struct ipx_interface, node);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxcfg_set_auto_select(char val)
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxcfg_auto_select_primary = val;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (val && !ipx_primary_net)
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipx_primary_net = ipx_interfaces_head();
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxcfg_get_config_data(struct ipx_config_data __user *arg)
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_config_data vals;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vals.ipxcfg_auto_select_primary	   = ipxcfg_auto_select_primary;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: Sockets may not be removed _during_ an interrupt or inet_bh
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * handler using this technique. They can be added although we do not
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * use this facility.
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipx_remove_socket(struct sock *sk)
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Determine interface with which socket is associated */
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc = ipx_sk(sk)->intrfc;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc)
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_hold(intrfc);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sk_del_node_init(sk);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipx_destroy_socket(struct sock *sk)
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_remove_socket(sk);
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb_queue_purge(&sk->sk_receive_queue);
135c2b42336f4a733020360157ba629d37f1410923aPavel Emelyanov	sk_refcnt_debug_dec(sk);
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
138981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki/*
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The following code is used to support IPX Interfaces (IPXITF).  An
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * IPX interface is defined by a physical device and a frame type.
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxitf_clear_primary_net(void)
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_primary_net = NULL;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipxcfg_auto_select_primary)
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipx_primary_net = ipx_interfaces_head();
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ipx_interface *__ipxitf_find_using_phys(struct net_device *dev,
1534833ed094097323f5f219820f6ebdc8dd66f501fAl Viro						      __be16 datalink)
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *i;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry(i, &ipx_interfaces, node)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i->if_dev == dev && i->if_dlink_type == datalink)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = NULL;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return i;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev,
1664833ed094097323f5f219820f6ebdc8dd66f501fAl Viro						    __be16 datalink)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *i;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = __ipxitf_find_using_phys(dev, datalink);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_hold(i);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return i;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1784833ed094097323f5f219820f6ebdc8dd66f501fAl Virostruct ipx_interface *ipxitf_find_using_net(__be32 net)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *i;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (net) {
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_for_each_entry(i, &ipx_interfaces, node)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (i->if_netnum == net)
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto hold;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i = NULL;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto unlock;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = ipx_primary_net;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i)
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldshold:
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_hold(i);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsunlock:
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return i;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Sockets are bound to a particular IPX interface. */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxitf_insert_socket(struct ipx_interface *intrfc, struct sock *sk)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_hold(intrfc);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_sk(sk)->intrfc = intrfc;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sk_add_node(sk, &intrfc->if_sklist);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller must hold intrfc->if_sklist_lock */
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sock *__ipxitf_find_socket(struct ipx_interface *intrfc,
2134833ed094097323f5f219820f6ebdc8dd66f501fAl Viro					 __be16 port)
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *s;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
217b67bfe0d42cac56c512dd5da4b1b347a23f4b70aSasha Levin	sk_for_each(s, &intrfc->if_sklist)
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipx_sk(s)->port == port)
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto found;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	s = NULL;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfound:
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return s;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller must hold a reference to intrfc */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sock *ipxitf_find_socket(struct ipx_interface *intrfc,
2274833ed094097323f5f219820f6ebdc8dd66f501fAl Viro					__be16 port)
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *s;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	s = __ipxitf_find_socket(intrfc, port);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (s)
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock_hold(s);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return s;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc,
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						unsigned char *ipx_node,
2434833ed094097323f5f219820f6ebdc8dd66f501fAl Viro						__be16 port)
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *s;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_hold(intrfc);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
250b67bfe0d42cac56c512dd5da4b1b347a23f4b70aSasha Levin	sk_for_each(s, &intrfc->if_sklist) {
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct ipx_sock *ipxs = ipx_sk(s);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipxs->port == port &&
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    !memcmp(ipx_node, ipxs->node, IPX_NODE_LEN))
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto found;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	s = NULL;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfound:
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return s;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __ipxitf_down(struct ipx_interface *intrfc)
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *s;
268b67bfe0d42cac56c512dd5da4b1b347a23f4b70aSasha Levin	struct hlist_node *t;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Delete all routes associated with this interface */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxrtr_del_routes(intrfc);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* error sockets */
275b67bfe0d42cac56c512dd5da4b1b347a23f4b70aSasha Levin	sk_for_each_safe(s, t, &intrfc->if_sklist) {
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct ipx_sock *ipxs = ipx_sk(s);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		s->sk_err = ENOLINK;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		s->sk_error_report(s);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxs->intrfc = NULL;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxs->port   = 0;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock_set_flag(s, SOCK_ZAPPED); /* Indicates it is no longer bound */
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sk_del_node_init(s);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_HLIST_HEAD(&intrfc->if_sklist);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* remove this interface from list */
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del(&intrfc->node);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* remove this interface from *special* networks */
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc == ipx_primary_net)
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_clear_primary_net();
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc == ipx_internal_net)
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipx_internal_net = NULL;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc->if_dev)
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_put(intrfc->if_dev);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(intrfc);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid ipxitf_down(struct ipx_interface *intrfc)
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__ipxitf_down(intrfc);
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __inline__ void __ipxitf_put(struct ipx_interface *intrfc)
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (atomic_dec_and_test(&intrfc->refcnt))
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__ipxitf_down(intrfc);
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_device_event(struct notifier_block *notifier,
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				unsigned long event, void *ptr)
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
318351638e7deeed2ec8ce451b53d33921b3da68f83Jiri Pirko	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *i, *tmp;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321721499e8931c5732202481ae24f2dfbf9910f129YOSHIFUJI Hideaki	if (!net_eq(dev_net(dev), &init_net))
322e9dc86534051b78e41e5b746cccc291b57a3a311Eric W. Biederman		return NOTIFY_DONE;
323e9dc86534051b78e41e5b746cccc291b57a3a311Eric W. Biederman
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (event != NETDEV_DOWN && event != NETDEV_UP)
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i->if_dev == dev) {
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (event == NETDEV_UP)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipxitf_hold(i);
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				__ipxitf_put(i);
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NOTIFY_DONE;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __exit void ipxitf_cleanup(void)
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *i, *tmp;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
346981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__ipxitf_put(i);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock_queue_rcv_skb(sock, skb) < 0)
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree_skb(skb);
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * On input skb->sk is NULL. Nobody is charged for the memory.
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller must hold a reference to intrfc */
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_demux_socket(struct ipx_interface *intrfc,
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       struct sk_buff *skb, int copy)
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx = ipx_hdr(skb);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int is_broadcast = !memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   IPX_NODE_LEN);
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *s;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
375b67bfe0d42cac56c512dd5da4b1b347a23f4b70aSasha Levin	sk_for_each(s, &intrfc->if_sklist) {
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct ipx_sock *ipxs = ipx_sk(s);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipxs->port == ipx->ipx_dest.sock &&
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (is_broadcast || !memcmp(ipx->ipx_dest.node,
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					     ipxs->node, IPX_NODE_LEN))) {
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* We found a socket to which to send */
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct sk_buff *skb1;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (copy) {
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				skb1 = skb_clone(skb, GFP_ATOMIC);
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				rc = -ENOMEM;
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!skb1)
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto out;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				skb1 = skb;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				copy = 1; /* skb may only be used once */
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ipxitf_def_skb_handler(s, skb1);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* On an external interface, one socket can listen */
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (intrfc != ipx_internal_net)
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* skb was solely for us, and we did not make a copy, so free it. */
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!copy)
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree_skb(skb);
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sock *ncp_connection_hack(struct ipx_interface *intrfc,
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					struct ipxhdr *ipx)
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The packet's target is a NCP connection handler. We want to hand it
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * to the correct socket directly within the kernel, so that the
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * mars_nwe packet distribution process does not have to do it. Here we
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * only care about NCP and BURST packets.
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * You might call this a hack, but believe me, you do not want a
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * complete NCP layer in the kernel, and this is VERY fast as well. */
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = NULL;
422981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	int connection = 0;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *ncphdr = (u8 *)(ipx + 1);
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
425981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	if (*ncphdr == 0x22 && *(ncphdr + 1) == 0x22) /* NCP request */
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		connection = (((int) *(ncphdr + 5)) << 8) | (int) *(ncphdr + 3);
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (*ncphdr == 0x77 && *(ncphdr + 1) == 0x77) /* BURST packet */
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		connection = (((int) *(ncphdr + 9)) << 8) | (int) *(ncphdr + 8);
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (connection) {
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Now we have to look for a special NCP connection handling
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * socket. Only these sockets have ipx_ncp_conn != 0, set by
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * SIOCIPXNCPCONN. */
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_bh(&intrfc->if_sklist_lock);
435b67bfe0d42cac56c512dd5da4b1b347a23f4b70aSasha Levin		sk_for_each(sk, &intrfc->if_sklist)
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ipx_sk(sk)->ipx_ncp_conn == connection) {
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sock_hold(sk);
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto found;
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sk = NULL;
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	found:
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_bh(&intrfc->if_sklist_lock);
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sk;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_demux_socket(struct ipx_interface *intrfc,
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       struct sk_buff *skb, int copy)
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx = ipx_hdr(skb);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sock1 = NULL, *sock2 = NULL;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sk_buff *skb1 = NULL, *skb2 = NULL;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451)
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock1 = ncp_connection_hack(intrfc, ipx);
457981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	if (!sock1)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* No special socket found, forward the packet the normal way */
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We need to check if there is a primary net and if
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * this is addressed to one of the *SPECIAL* sockets because
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * these need to be propagated to the primary net.
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 0x456(Diagnostic).
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx_primary_net && intrfc != ipx_primary_net) {
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		const int dsock = ntohs(ipx->ipx_dest.sock);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dsock == 0x452 || dsock == 0x453 || dsock == 0x456)
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* The appropriate thing to do here is to dup the
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * packet and route to the primary net interface via
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * ipxitf_send; however, we'll cheat and just demux it
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * here. */
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sock2 = ipxitf_find_socket(ipx_primary_net,
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							ipx->ipx_dest.sock);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * If there is nothing to do return. The kfree will cancel any charging.
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!sock1 && !sock2) {
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!copy)
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree_skb(skb);
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * This next segment of code is a little awkward, but it sets it up
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * so that the appropriate number of copies of the SKB are made and
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * that skb1 and skb2 point to it (them) so that it (they) can be
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * demuxed to sock1 and/or sock2.  If we are unable to make enough
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * copies, we do as much as is possible.
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy)
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb1 = skb_clone(skb, GFP_ATOMIC);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb1 = skb;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENOMEM;
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!skb1)
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_put;
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Do we need 2 SKBs? */
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock1 && sock2)
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb2 = skb_clone(skb1, GFP_ATOMIC);
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb2 = skb1;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock1)
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_def_skb_handler(sock1, skb1);
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!skb2)
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_put;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock2)
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_def_skb_handler(sock2, skb2);
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_put:
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock1)
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock_put(sock1);
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock2)
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock_put(sock2);
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif	/* CONFIG_IPX_INTERN */
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sk_buff *ipxitf_adjust_skbuff(struct ipx_interface *intrfc,
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					    struct sk_buff *skb)
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sk_buff *skb2;
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int in_offset = (unsigned char *)ipx_hdr(skb) - skb->head;
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int out_offset = intrfc->if_ipx_offset;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len;
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Hopefully, most cases */
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (in_offset >= out_offset)
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return skb;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Need new SKB */
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len  = skb->len + out_offset;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb2 = alloc_skb(len, GFP_ATOMIC);
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (skb2) {
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb_reserve(skb2, out_offset);
5517e28ecc282574a7d72ace365fc9bc86e27ba880fArnaldo Carvalho de Melo		skb_reset_network_header(skb2);
552badff6d01a8589a1c828b0bf118903ca38627f4eArnaldo Carvalho de Melo		skb_reset_transport_header(skb2);
5537e28ecc282574a7d72ace365fc9bc86e27ba880fArnaldo Carvalho de Melo		skb_put(skb2, skb->len);
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(ipx_hdr(skb2), ipx_hdr(skb), skb->len);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(skb2->cb, skb->cb, sizeof(skb->cb));
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree_skb(skb);
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return skb2;
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller must hold a reference to intrfc and the skb has to be unshared */
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node)
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx = ipx_hdr(skb);
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = intrfc->if_dev;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct datalink_proto *dl = intrfc->if_dlink;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char dest_node[IPX_NODE_LEN];
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int send_to_wire = 1;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int addr_len;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx->ipx_tctrl = IPX_SKB_CB(skb)->ipx_tctrl;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx->ipx_dest.net = IPX_SKB_CB(skb)->ipx_dest_net;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx->ipx_source.net = IPX_SKB_CB(skb)->ipx_source_net;
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* see if we need to include the netnum in the route list */
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IPX_SKB_CB(skb)->last_hop.index >= 0) {
5774833ed094097323f5f219820f6ebdc8dd66f501fAl Viro		__be32 *last_hop = (__be32 *)(((u8 *) skb->data) +
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sizeof(struct ipxhdr) +
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				IPX_SKB_CB(skb)->last_hop.index *
5804833ed094097323f5f219820f6ebdc8dd66f501fAl Viro				sizeof(__be32));
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*last_hop = IPX_SKB_CB(skb)->last_hop.netnum;
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		IPX_SKB_CB(skb)->last_hop.index = -1;
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
584981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
585981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	/*
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We need to know how many skbuffs it will take to send out this
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * packet to avoid unnecessary copies.
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
589981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
590981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	if (!dl || !dev || dev->flags & IFF_LOOPBACK)
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		send_to_wire = 0;	/* No non looped */
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
594981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	 * See if this should be demuxed to sockets on this interface
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We want to ensure the original was eaten or that we only use
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * up clones.
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
599981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx->ipx_dest.net == intrfc->if_netnum) {
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * To our own node, loop and free the original.
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The internal net will receive on all node address.
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (intrfc == ipx_internal_net ||
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    !memcmp(intrfc->if_node, node, IPX_NODE_LEN)) {
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Don't charge sender */
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			skb_orphan(skb);
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Will charge receiver */
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return ipxitf_demux_socket(intrfc, skb, 0);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Broadcast, loop and possibly keep to send on. */
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN)) {
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!send_to_wire)
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				skb_orphan(skb);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ipxitf_demux_socket(intrfc, skb, send_to_wire);
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!send_to_wire)
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * If the originating net is not equal to our net; this is routed
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We are still charging the sender. Which is right - the driver
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * free will handle this fairly.
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx->ipx_source.net != intrfc->if_netnum) {
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Unshare the buffer before modifying the count in
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * case it's a flood or tcpdump
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb = skb_unshare(skb, GFP_ATOMIC);
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!skb)
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (++ipx->ipx_tctrl > ipxcfg_max_hops)
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			send_to_wire = 0;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!send_to_wire) {
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree_skb(skb);
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Determine the appropriate hardware address */
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	addr_len = dev->addr_len;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN))
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(dest_node, dev->broadcast, addr_len);
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Make any compensation for differing physical/data link size */
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb = ipxitf_adjust_skbuff(intrfc, skb);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!skb)
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set up data link and physical headers */
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb->dev	= dev;
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb->protocol	= htons(ETH_P_IPX);
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Send it out */
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dl->request(dl, skb, dest_node);
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_add_local_route(struct ipx_interface *intrfc)
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxitf_discover_netnum(struct ipx_interface *intrfc,
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   struct sk_buff *skb);
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_rcv(struct ipx_interface *intrfc, struct sk_buff *skb)
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx = ipx_hdr(skb);
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 0;
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_hold(intrfc);
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* See if we should update our network number */
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc->if_netnum) /* net number of intrfc not known yet */
686981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki		ipxitf_discover_netnum(intrfc, skb);
687981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IPX_SKB_CB(skb)->last_hop.index = -1;
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx->ipx_type == IPX_TYPE_PPROP) {
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = ipxitf_pprop(intrfc, skb);
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc)
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_free_skb;
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* local processing follows */
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!IPX_SKB_CB(skb)->ipx_dest_net)
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		IPX_SKB_CB(skb)->ipx_dest_net = intrfc->if_netnum;
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!IPX_SKB_CB(skb)->ipx_source_net)
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* it doesn't make sense to route a pprop packet, there's no meaning
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * in the ipx_dest_net for such packets */
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx->ipx_type != IPX_TYPE_PPROP &&
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    intrfc->if_netnum != IPX_SKB_CB(skb)->ipx_dest_net) {
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We only route point-to-point packets. */
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (skb->pkt_type == PACKET_HOST) {
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			skb = skb_unshare(skb, GFP_ATOMIC);
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (skb)
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				rc = ipxrtr_route_skb(skb);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_intrfc;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_free_skb;
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* see if we should keep it */
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) ||
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN)) {
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = ipxitf_demux_socket(intrfc, skb, 0);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_intrfc;
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* we couldn't pawn it off so unload it */
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_free_skb:
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree_skb(skb);
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_intrfc:
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxitf_discover_netnum(struct ipx_interface *intrfc,
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   struct sk_buff *skb)
733981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki{
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const struct ipx_cb *cb = IPX_SKB_CB(skb);
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* see if this is an intra packet: source_net == dest_net */
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cb->ipx_source_net == cb->ipx_dest_net && cb->ipx_source_net) {
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct ipx_interface *i =
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipxitf_find_using_net(cb->ipx_source_net);
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* NB: NetWare servers lie about their hop count so we
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * dropped the test based on it. This is the best way
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * to determine this is a 0 hop count packet. */
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!i) {
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			intrfc->if_netnum = cb->ipx_source_net;
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ipxitf_add_local_route(intrfc);
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "IPX: Network number collision "
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"%lx\n        %s %s and %s %s\n",
7494833ed094097323f5f219820f6ebdc8dd66f501fAl Viro				(unsigned long) ntohl(cb->ipx_source_net),
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipx_device_name(i),
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipx_frame_name(i->if_dlink_type),
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipx_device_name(intrfc),
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipx_frame_name(intrfc->if_dlink_type));
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ipxitf_put(i);
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ipxitf_pprop - Process packet propagation IPX packet type 0x14, used for
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 		  NetBIOS broadcasts
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @intrfc: IPX interface receiving this packet
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @skb: Received packet
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Checks if packet is valid: if its more than %IPX_MAX_PPROP_HOPS hops or if it
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * is smaller than a IPX header + the room for %IPX_MAX_PPROP_HOPS hops we drop
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it, not even processing it locally, if it has exact %IPX_MAX_PPROP_HOPS we
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * don't broadcast it, but process it locally. See chapter 5 of Novell's "IPX
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * RIP and SAP Router Specification", Part Number 107-000029-001.
770981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki *
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If it is valid, check if we have pprop broadcasting enabled by the user,
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * if not, just return zero for local processing.
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If it is enabled check the packet and don't broadcast it if we have already
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * seen this packet.
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Broadcast: send it to the interfaces that aren't on the packet visited nets
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * array, just after the IPX header.
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns -EINVAL for invalid packets, so that the calling function drops
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the packet without local processing. 0 if packet is to be locally processed.
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb)
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx = ipx_hdr(skb);
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, rc = -EINVAL;
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *ifcs;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *c;
7894833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	__be32 *l;
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Illegal packet - too many hops or too short */
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We decide to throw it away: no broadcasting, no local processing.
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * NetBIOS unaware implementations route them as normal packets -
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * tctrl <= 15, any data payload... */
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IPX_SKB_CB(skb)->ipx_tctrl > IPX_MAX_PPROP_HOPS ||
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    ntohs(ipx->ipx_pktsize) < sizeof(struct ipxhdr) +
797981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki					IPX_MAX_PPROP_HOPS * sizeof(u32))
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* are we broadcasting this damn thing? */
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!sysctl_ipx_pprop_broadcasting)
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We do broadcast packet on the IPX_MAX_PPROP_HOPS hop, but we
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * process it locally. All previous hops broadcasted it, and process it
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * locally. */
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IPX_SKB_CB(skb)->ipx_tctrl == IPX_MAX_PPROP_HOPS)
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
808981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	c = ((u8 *) ipx) + sizeof(struct ipxhdr);
8104833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	l = (__be32 *) c;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Don't broadcast packet if already seen this net */
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (*l++ == intrfc->if_netnum)
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* < IPX_MAX_PPROP_HOPS hops && input interface not in list. Save the
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * position where we will insert recvd netnum into list, later on,
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * in ipxitf_send */
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IPX_SKB_CB(skb)->last_hop.index = i;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IPX_SKB_CB(skb)->last_hop.netnum = intrfc->if_netnum;
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* xmit on all other interfaces... */
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry(ifcs, &ipx_interfaces, node) {
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Except unconfigured interfaces */
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ifcs->if_netnum)
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
828981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* That aren't in the list */
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ifcs == intrfc)
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
8324833ed094097323f5f219820f6ebdc8dd66f501fAl Viro		l = (__be32 *) c;
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* don't consider the last entry in the packet list,
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * it is our netnum, and it is not there yet */
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ifcs->if_netnum == *l++)
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i == IPX_SKB_CB(skb)->ipx_tctrl) {
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct sk_buff *s = skb_copy(skb, GFP_ATOMIC);
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (s) {
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				IPX_SKB_CB(s)->ipx_dest_net = ifcs->if_netnum;
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipxrtr_route_skb(s);
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ipxitf_insert(struct ipx_interface *intrfc)
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail(&intrfc->node, &ipx_interfaces);
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipxcfg_auto_select_primary && !ipx_primary_net)
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipx_primary_net = intrfc;
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8624833ed094097323f5f219820f6ebdc8dd66f501fAl Virostatic struct ipx_interface *ipxitf_alloc(struct net_device *dev, __be32 netnum,
8634833ed094097323f5f219820f6ebdc8dd66f501fAl Viro					  __be16 dlink_type,
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  struct datalink_proto *dlink,
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  unsigned char internal,
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  int ipx_offset)
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc = kmalloc(sizeof(*intrfc), GFP_ATOMIC);
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc) {
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_dev		= dev;
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_netnum	= netnum;
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_dlink_type 	= dlink_type;
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_dlink 	= dlink;
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_internal 	= internal;
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_ipx_offset 	= ipx_offset;
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_sknum 	= IPX_MIN_EPHEMERAL_SOCKET;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		INIT_HLIST_HEAD(&intrfc->if_sklist);
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		atomic_set(&intrfc->refcnt, 1);
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_init(&intrfc->if_sklist_lock);
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return intrfc;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_create_internal(struct ipx_interface_definition *idef)
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc;
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EEXIST;
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Only one primary network allowed */
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx_primary_net)
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Must have a valid network number */
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EADDRNOTAVAIL;
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!idef->ipx_network)
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_find_using_net(idef->ipx_network);
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EADDRINUSE;
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc) {
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_put(intrfc);
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_alloc(NULL, idef->ipx_network, 0, NULL, 1, 0);
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EAGAIN;
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc)
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN);
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_internal_net = ipx_primary_net = intrfc;
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_hold(intrfc);
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_insert(intrfc);
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = ipxitf_add_local_route(intrfc);
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9204ac396c0467993853d3d58c0975151515700c07bAlexey Dobriyanstatic __be16 ipx_map_frame_type(unsigned char type)
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9224ac396c0467993853d3d58c0975151515700c07bAlexey Dobriyan	__be16 rc = 0;
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (type) {
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_ETHERII:	rc = htons(ETH_P_IPX);		break;
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_8022:	rc = htons(ETH_P_802_2);	break;
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_SNAP:	rc = htons(ETH_P_SNAP);		break;
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_8023:	rc = htons(ETH_P_802_3);	break;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_create(struct ipx_interface_definition *idef)
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
9374833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	__be16 dlink_type = 0;
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct datalink_proto *datalink = NULL;
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc;
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (idef->ipx_special == IPX_INTERNAL) {
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = ipxitf_create_internal(idef);
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EEXIST;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (idef->ipx_special == IPX_PRIMARY && ipx_primary_net)
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_find_using_net(idef->ipx_network);
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EADDRINUSE;
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (idef->ipx_network && intrfc) {
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_put(intrfc);
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc)
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_put(intrfc);
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961881d966b48b035ab3f3aeaae0f3d3f9b584f45b2Eric W. Biederman	dev = dev_get_by_name(&init_net, idef->ipx_device);
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENODEV;
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (idef->ipx_dlink_type) {
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_8022:
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dlink_type 	= htons(ETH_P_802_2);
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		datalink 	= p8022_datalink;
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_ETHERII:
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev->type != ARPHRD_IEEE802) {
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dlink_type 	= htons(ETH_P_IPX);
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			datalink 	= pEII_datalink;
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
976211ed865108e24697b44bee5daac502ee6bdd4a4Paul Gortmaker		}
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* fall through */
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_SNAP:
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dlink_type 	= htons(ETH_P_SNAP);
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		datalink 	= pSNAP_datalink;
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_8023:
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dlink_type 	= htons(ETH_P_802_3);
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		datalink 	= p8023_datalink;
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IPX_FRAME_NONE:
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EPROTONOSUPPORT;
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_dev;
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENETDOWN;
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(dev->flags & IFF_UP))
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_dev;
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check addresses are suitable */
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EINVAL;
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->addr_len > IPX_NODE_LEN)
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_dev;
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_find_using_phys(dev, dlink_type);
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc) {
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Ok now create */
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc = ipxitf_alloc(dev, idef->ipx_network, dlink_type,
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      datalink, 0, dev->hard_header_len +
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					datalink->header_length);
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EAGAIN;
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!intrfc)
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_dev;
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Setup primary if necessary */
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (idef->ipx_special == IPX_PRIMARY)
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ipx_primary_net = intrfc;
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!memcmp(idef->ipx_node, "\000\000\000\000\000\000",
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    IPX_NODE_LEN)) {
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memset(intrfc->if_node, 0, IPX_NODE_LEN);
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(intrfc->if_node + IPX_NODE_LEN - dev->addr_len,
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->dev_addr, dev->addr_len);
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN);
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_hold(intrfc);
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_insert(intrfc);
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If the network number is known, add a route */
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc->if_netnum)
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_intrfc;
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = ipxitf_add_local_route(intrfc);
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_intrfc:
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_dev:
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_put(dev);
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_delete(struct ipx_interface_definition *idef)
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = NULL;
10434833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	__be16 dlink_type = 0;
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc;
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 0;
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&ipx_interfaces_lock);
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (idef->ipx_special == IPX_INTERNAL) {
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipx_internal_net) {
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__ipxitf_put(ipx_internal_net);
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -ENOENT;
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_type = ipx_map_frame_type(idef->ipx_dlink_type);
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EPROTONOSUPPORT;
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dlink_type)
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1062881d966b48b035ab3f3aeaae0f3d3f9b584f45b2Eric W. Biederman	dev = __dev_get_by_name(&init_net, idef->ipx_device);
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENODEV;
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = __ipxitf_find_using_phys(dev, dlink_type);
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EINVAL;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc)
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__ipxitf_put(intrfc);
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&ipx_interfaces_lock);
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ipx_interface *ipxitf_auto_create(struct net_device *dev,
10804833ed094097323f5f219820f6ebdc8dd66f501fAl Viro						__be16 dlink_type)
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc = NULL;
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct datalink_proto *datalink;
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check addresses are suitable */
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->addr_len > IPX_NODE_LEN)
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10924833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	switch (ntohs(dlink_type)) {
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_IPX:		datalink = pEII_datalink;	break;
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_802_2:	datalink = p8022_datalink;	break;
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_SNAP:	datalink = pSNAP_datalink;	break;
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_802_3:	datalink = p8023_datalink;	break;
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:		goto out;
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_alloc(dev, 0, dlink_type, datalink, 0,
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->hard_header_len + datalink->header_length);
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc) {
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memset(intrfc->if_node, 0, IPX_NODE_LEN);
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->dev_addr, dev->addr_len);
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_init(&intrfc->if_sklist_lock);
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		atomic_set(&intrfc->refcnt, 1);
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_insert(intrfc);
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_hold(dev);
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return intrfc;
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipxitf_ioctl(unsigned int cmd, void __user *arg)
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EINVAL;
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ifreq ifr;
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int val;
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCSIFADDR: {
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct sockaddr_ipx *sipx;
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct ipx_interface_definition f;
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EFAULT;
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_from_user(&ifr, arg, sizeof(ifr)))
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx = (struct sockaddr_ipx *)&ifr.ifr_addr;
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EINVAL;
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sipx->sipx_family != AF_IPX)
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		f.ipx_network = sipx->sipx_network;
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(f.ipx_device, ifr.ifr_name,
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sizeof(f.ipx_device));
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(f.ipx_node, sipx->sipx_node, IPX_NODE_LEN);
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		f.ipx_dlink_type = sipx->sipx_type;
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		f.ipx_special = sipx->sipx_special;
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sipx->sipx_action == IPX_DLTITF)
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = ipxitf_delete(&f);
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = ipxitf_create(&f);
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCGIFADDR: {
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct sockaddr_ipx *sipx;
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct ipx_interface *ipxif;
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev;
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EFAULT;
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_from_user(&ifr, arg, sizeof(ifr)))
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx = (struct sockaddr_ipx *)&ifr.ifr_addr;
1157881d966b48b035ab3f3aeaae0f3d3f9b584f45b2Eric W. Biederman		dev  = __dev_get_by_name(&init_net, ifr.ifr_name);
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc   = -ENODEV;
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!dev)
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxif = ipxitf_find_using_phys(dev,
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   ipx_map_frame_type(sipx->sipx_type));
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EADDRNOTAVAIL;
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ipxif)
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_family	= AF_IPX;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_network	= ipxif->if_netnum;
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(sipx->sipx_node, ipxif->if_node,
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sizeof(sipx->sipx_node));
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EFAULT;
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_to_user(arg, &ifr, sizeof(ifr)))
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxitf_put(ipxif);
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = 0;
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1178981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	case SIOCAIPXITFCRT:
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EFAULT;
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(val, (unsigned char __user *) arg))
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = 0;
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxcfg_auto_create_interfaces = val;
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1185981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	case SIOCAIPXPRISLT:
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EFAULT;
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(val, (unsigned char __user *) arg))
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = 0;
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxcfg_set_auto_select(val);
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Checksum routine for IPX
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1200981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This functions should *not* mess with packet contents */
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
120402e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro__be16 ipx_cksum(struct ipxhdr *packet, int length)
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1206981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	/*
1207981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	 *	NOTE: sum is a net byte order quantity, which optimizes the
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	loop. This only works on big and little endian machines. (I
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	don't know of a machine that isn't.)
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
121102e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	/* handle the first 3 words separately; checksum should be skipped
121202e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	 * and ipx_tctrl masked out */
121302e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	__u16 *p = (__u16 *)packet;
121402e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	__u32 sum = p[1] + (p[2] & (__force u16)htons(0x00ff));
121502e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	__u32 i = (length >> 1) - 3; /* Number of remaining complete words */
121602e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro
121702e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	/* Loop through them */
121802e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	p += 3;
121902e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	while (i--)
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sum += *p++;
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Add on the last part word if it exists */
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (packet->ipx_pktsize & htons(1))
122402e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro		sum += (__force u16)htons(0xff00) & *p;
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Do final fixup */
12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sum = (sum & 0xffff) + (sum >> 16);
12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* It's a pity there's no concept of carry in C */
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sum >= 0x10000)
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sum++;
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
123302e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	/*
123402e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	 * Leave 0 alone; we don't want 0xffff here.  Note that we can't get
123502e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	 * here with 0x10000, so this check is the same as ((__u16)sum)
123602e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	 */
123702e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	if (sum)
123802e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro		sum = ~sum;
123902e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro
124002e60370d4dac83f22d5ae75d5512bcb9a3f24b7Al Viro	return (__force __be16)sum;
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12434833ed094097323f5f219820f6ebdc8dd66f501fAl Viroconst char *ipx_frame_name(__be16 frame)
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char* rc = "None";
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (ntohs(frame)) {
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_IPX:		rc = "EtherII";	break;
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_802_2:	rc = "802.2";	break;
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_SNAP:	rc = "SNAP";	break;
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ETH_P_802_3:	rc = "802.3";	break;
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsconst char *ipx_device_name(struct ipx_interface *intrfc)
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return intrfc->if_internal ? "Internal" :
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intrfc->if_dev ? intrfc->if_dev->name : "Unknown";
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Handling for system calls applied via the various interfaces to an IPX
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * socket object. */
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_setsockopt(struct socket *sock, int level, int optname,
1267b7058842c940ad2c08dd829b21e5c92ebe3b8758David S. Miller			  char __user *optval, unsigned int optlen)
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int opt;
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EINVAL;
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1273b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (optlen != sizeof(int))
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EFAULT;
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (get_user(opt, (unsigned int __user *)optval))
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENOPROTOOPT;
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(level == SOL_IPX && optname == IPX_TYPE))
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_sk(sk)->type = opt;
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1288b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_getsockopt(struct socket *sock, int level, int optname,
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char __user *optval, int __user *optlen)
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int val = 0;
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len;
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -ENOPROTOOPT;
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1300b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(level == SOL_IPX && optname == IPX_TYPE))
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = ipx_sk(sk)->type;
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EFAULT;
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (get_user(len, optlen))
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len = min_t(unsigned int, len, sizeof(int));
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EINVAL;
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(len < 0)
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1314981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EFAULT;
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1321b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct proto ipx_proto = {
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name	  = "IPX",
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner	  = THIS_MODULE,
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.obj_size = sizeof(struct ipx_sock),
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13313f378b684453f2a028eda463ce383370545d9cc9Eric Parisstatic int ipx_create(struct net *net, struct socket *sock, int protocol,
13323f378b684453f2a028eda463ce383370545d9cc9Eric Paris		      int kern)
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -ESOCKTNOSUPPORT;
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk;
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
133709ad9bc752519cc167d0a573e1acf69b5c707c67Octavian Purdila	if (!net_eq(net, &init_net))
13381b8d7ae42d02e483ad94035cca851e4f7fbecb40Eric W. Biederman		return -EAFNOSUPPORT;
13391b8d7ae42d02e483ad94035cca851e4f7fbecb40Eric W. Biederman
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * SPX support is not anymore in the kernel sources. If you want to
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * ressurrect it, completing it and making it understand shared skbs,
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * be fully multithreaded, etc, grab the sources in an early 2.5 kernel
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * tree.
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock->type != SOCK_DGRAM)
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1349981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	rc = -ENOMEM;
13506257ff2177ff02d7f260a7a501876aa41cb9a9f6Pavel Emelyanov	sk = sk_alloc(net, PF_IPX, GFP_KERNEL, &ipx_proto);
13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!sk)
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1353c2b42336f4a733020360157ba629d37f1410923aPavel Emelyanov
1354c2b42336f4a733020360157ba629d37f1410923aPavel Emelyanov	sk_refcnt_debug_inc(sk);
13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock_init_data(sock, sk);
135628448b80456feafe07e2d05b6363b00f61f6171eTom Herbert	sk->sk_no_check_tx = 1;		/* Checksum off by default */
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock->ops = &ipx_dgram_ops;
13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_release(struct socket *sock)
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!sk)
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1370b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
13718815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	sk->sk_shutdown = SHUTDOWN_MASK;
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!sock_flag(sk, SOCK_DEAD))
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sk->sk_state_change(sk);
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock_set_flag(sk, SOCK_DEAD);
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock->sk = NULL;
1377c2b42336f4a733020360157ba629d37f1410923aPavel Emelyanov	sk_refcnt_debug_release(sk);
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_destroy_socket(sk);
1379b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
1380674f2115995b7b588cbf3540c9f9b2448a8c7ea8Eric Dumazet	sock_put(sk);
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller must hold a reference to intrfc */
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13874833ed094097323f5f219820f6ebdc8dd66f501fAl Virostatic __be16 ipx_first_free_socketnum(struct ipx_interface *intrfc)
13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short socketNum = intrfc->if_sknum;
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&intrfc->if_sklist_lock);
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (socketNum < IPX_MIN_EPHEMERAL_SOCKET)
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		socketNum = IPX_MIN_EPHEMERAL_SOCKET;
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13964833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	while (__ipxitf_find_socket(intrfc, htons(socketNum)))
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (socketNum > IPX_MAX_EPHEMERAL_SOCKET)
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			socketNum = IPX_MIN_EPHEMERAL_SOCKET;
13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			socketNum++;
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&intrfc->if_sklist_lock);
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc->if_sknum = socketNum;
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14054833ed094097323f5f219820f6ebdc8dd66f501fAl Viro	return htons(socketNum);
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
140883927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmannstatic int __ipx_bind(struct socket *sock,
140983927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann			struct sockaddr *uaddr, int addr_len)
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_sock *ipxs = ipx_sk(sk);
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc;
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sockaddr_ipx *addr = (struct sockaddr_ipx *)uaddr;
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EINVAL;
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!sock_flag(sk, SOCK_ZAPPED) || addr_len != sizeof(struct sockaddr_ipx))
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_find_using_net(addr->sipx_network);
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EADDRNOTAVAIL;
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc)
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!addr->sipx_port) {
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		addr->sipx_port = ipx_first_free_socketnum(intrfc);
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EINVAL;
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!addr->sipx_port)
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_put;
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* protect IPX system stuff like routing/sap */
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EACCES;
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ntohs(addr->sipx_port) < IPX_MIN_EPHEMERAL_SOCKET &&
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !capable(CAP_NET_ADMIN))
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_put;
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxs->port = addr->sipx_port;
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intrfc == ipx_internal_net) {
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* The source address is to be set explicitly if the
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * socket is to be bound on the internal network. If a
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * node number 0 was specified, the default is used.
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EINVAL;
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!memcmp(addr->sipx_node, ipx_broadcast_node, IPX_NODE_LEN))
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_put;
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN))
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN);
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(ipxs->node, addr->sipx_node, IPX_NODE_LEN);
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EADDRINUSE;
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipxitf_find_internal_socket(intrfc, ipxs->node,
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						ipxs->port)) {
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SOCK_DEBUG(sk,
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"IPX: bind failed because port %X in use.\n",
14604833ed094097323f5f219820f6ebdc8dd66f501fAl Viro				ntohs(addr->sipx_port));
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_put;
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Source addresses are easy. It must be our
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * network:node pair for an interface routed to IPX
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * with the ipx routing ioctl()
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN);
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EADDRINUSE;
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipxitf_find_socket(intrfc, addr->sipx_port)) {
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SOCK_DEBUG(sk,
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"IPX: bind failed because port %X in use.\n",
14754833ed094097323f5f219820f6ebdc8dd66f501fAl Viro				ntohs(addr->sipx_port));
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_put;
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else	/* !def CONFIG_IPX_INTERN */
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Source addresses are easy. It must be our network:node pair for
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   an interface routed to IPX with the ipx routing ioctl() */
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EADDRINUSE;
14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipxitf_find_socket(intrfc, addr->sipx_port)) {
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n",
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ntohs((int)addr->sipx_port));
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_put;
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif	/* CONFIG_IPX_INTERN */
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_insert_socket(intrfc, sk);
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock_reset_flag(sk, SOCK_ZAPPED);
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_put:
14991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
15011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
150483927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmannstatic int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
150583927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann{
1506b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	struct sock *sk = sock->sk;
150783927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann	int rc;
150883927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann
1509b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
151083927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann	rc = __ipx_bind(sock, uaddr, addr_len);
1511b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
151283927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann
151383927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann	return rc;
151483927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann}
151583927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann
15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int addr_len, int flags)
15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
15201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_sock *ipxs = ipx_sk(sk);
15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sockaddr_ipx *addr;
15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EINVAL;
15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_route *rt;
15241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sk->sk_state	= TCP_CLOSE;
15261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock->state 	= SS_UNCONNECTED;
15271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1528b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
15291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (addr_len != sizeof(*addr))
15301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
15311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	addr = (struct sockaddr_ipx *)uaddr;
15321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* put the autobinding in */
15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ipxs->port) {
15351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct sockaddr_ipx uaddr;
15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uaddr.sipx_port		= 0;
15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uaddr.sipx_network 	= 0;
15391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
15411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -ENETDOWN;
15421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ipxs->intrfc)
15431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out; /* Someone zonked the iface */
15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			IPX_NODE_LEN);
15461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif	/* CONFIG_IPX_INTERN */
15471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
154883927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann		rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      sizeof(struct sockaddr_ipx));
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc)
15511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
15521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1554981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	/* We can either connect to primary network or somewhere
15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * we can route to */
15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rt = ipxrtr_lookup(addr->sipx_network);
15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENETUNREACH;
15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!rt && !(!addr->sipx_network && ipx_primary_net))
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxs->dest_addr.net  = addr->sipx_network;
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxs->dest_addr.sock = addr->sipx_port;
15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(ipxs->dest_addr.node, addr->sipx_node, IPX_NODE_LEN);
15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxs->type = addr->sipx_type;
15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock->type == SOCK_DGRAM) {
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sock->state 	= SS_CONNECTED;
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sk->sk_state 	= TCP_ESTABLISHED;
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rt)
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ipxrtr_put(rt);
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1575b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int *uaddr_len, int peer)
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_address *addr;
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sockaddr_ipx sipx;
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_sock *ipxs = ipx_sk(sk);
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*uaddr_len = sizeof(struct sockaddr_ipx);
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (peer) {
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -ENOTCONN;
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sk->sk_state != TCP_ESTABLISHED)
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		addr = &ipxs->dest_addr;
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx.sipx_network	= addr->net;
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx.sipx_port		= addr->sock;
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(sipx.sipx_node, addr->node, IPX_NODE_LEN);
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipxs->intrfc) {
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sipx.sipx_network = ipxs->intrfc->if_netnum;
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(sipx.sipx_node, ipxs->node, IPX_NODE_LEN);
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(sipx.sipx_node, ipxs->intrfc->if_node,
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				IPX_NODE_LEN);
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif	/* CONFIG_IPX_INTERN */
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sipx.sipx_network = 0;
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx.sipx_port = ipxs->port;
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sipx.sipx_family = AF_IPX;
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sipx.sipx_type	 = ipxs->type;
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sipx.sipx_zero	 = 0;
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(uaddr, &sipx, sizeof(sipx));
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1626b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1630f2ccd8fa06c8e302116e71df372f5c1f83432e03David S. Millerstatic int ipx_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NULL here for pt means the packet was looped back */
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_interface *intrfc;
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx;
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 ipx_pktsize;
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 0;
1637981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
1638721499e8931c5732202481ae24f2dfbf9910f129YOSHIFUJI Hideaki	if (!net_eq(dev_net(dev), &init_net))
1639e730c15519d09ea528b4d2f1103681fa5937c0e6Eric W. Biederman		goto drop;
1640e730c15519d09ea528b4d2f1103681fa5937c0e6Eric W. Biederman
1641981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	/* Not ours */
1642981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki	if (skb->pkt_type == PACKET_OTHERHOST)
1643981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki		goto drop;
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16487b1ba8de569460894efa892457af7a37c0d574f9Stephen Hemminger	if (!pskb_may_pull(skb, sizeof(struct ipxhdr)))
16497b1ba8de569460894efa892457af7a37c0d574f9Stephen Hemminger		goto drop;
16507b1ba8de569460894efa892457af7a37c0d574f9Stephen Hemminger
1651fff642570dc47ab76491fe81ee6599269c4eb13eDavid S. Miller	ipx_pktsize = ntohs(ipx_hdr(skb)->ipx_pktsize);
1652981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Too small or invalid header? */
16548b5cc5ef40c83c6ea4c90b203bb2c8b17edfa11bStephen Hemminger	if (ipx_pktsize < sizeof(struct ipxhdr) ||
16558b5cc5ef40c83c6ea4c90b203bb2c8b17edfa11bStephen Hemminger	    !pskb_may_pull(skb, ipx_pktsize))
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto drop;
1657981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
16587b1ba8de569460894efa892457af7a37c0d574f9Stephen Hemminger	ipx = ipx_hdr(skb);
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ipx->ipx_checksum != IPX_NO_CHECKSUM &&
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   ipx->ipx_checksum != ipx_cksum(ipx, ipx_pktsize))
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto drop;
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IPX_SKB_CB(skb)->ipx_tctrl	= ipx->ipx_tctrl;
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IPX_SKB_CB(skb)->ipx_dest_net	= ipx->ipx_dest.net;
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IPX_SKB_CB(skb)->ipx_source_net = ipx->ipx_source.net;
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Determine what local ipx endpoint this is */
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	intrfc = ipxitf_find_using_phys(dev, pt->type);
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!intrfc) {
16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ipxcfg_auto_create_interfaces &&
16714833ed094097323f5f219820f6ebdc8dd66f501fAl Viro		   IPX_SKB_CB(skb)->ipx_dest_net) {
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			intrfc = ipxitf_auto_create(dev, pt->type);
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (intrfc)
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ipxitf_hold(intrfc);
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!intrfc)	/* Not one of ours */
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* or invalid packet for auto creation */
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto drop;
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = ipxitf_rcv(intrfc, skb);
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_put(intrfc);
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdrop:
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree_skb(skb);
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_sendmsg(struct kiocb *iocb, struct socket *sock,
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct msghdr *msg, size_t len)
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_sock *ipxs = ipx_sk(sk);
1696342dfc306fb32155314dad277f3c3686b83fb9f1Steffen Hurrle	DECLARE_SOCKADDR(struct sockaddr_ipx *, usipx, msg->msg_name);
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sockaddr_ipx local_sipx;
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EINVAL;
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int flags = msg->msg_flags;
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Socket gets bound below anyway */
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*	if (sk->sk_zapped)
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO; */	/* Socket not bound */
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Max possible packet size limited by 16 bit pktsize in header */
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len >= 65535 - sizeof(struct ipxhdr))
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usipx) {
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ipxs->port) {
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct sockaddr_ipx uaddr;
17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			uaddr.sipx_port		= 0;
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			uaddr.sipx_network	= 0;
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = -ENETDOWN;
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!ipxs->intrfc)
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out; /* Someone zonked the iface */
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				IPX_NODE_LEN);
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
172583927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann			rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					sizeof(struct sockaddr_ipx));
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (rc)
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out;
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EINVAL;
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (msg->msg_namelen < sizeof(*usipx) ||
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    usipx->sipx_family != AF_IPX)
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -ENOTCONN;
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sk->sk_state != TCP_ESTABLISHED)
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
17391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usipx = &local_sipx;
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usipx->sipx_family 	= AF_IPX;
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usipx->sipx_type 	= ipxs->type;
17431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usipx->sipx_port 	= ipxs->dest_addr.sock;
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usipx->sipx_network 	= ipxs->dest_addr.net;
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN);
17461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len,
17491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 flags & MSG_DONTWAIT);
17501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc >= 0)
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = len;
17521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1753b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
17541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
17551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
17591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct msghdr *msg, size_t size, int flags)
17601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
17621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipx_sock *ipxs = ipx_sk(sk);
1763342dfc306fb32155314dad277f3c3686b83fb9f1Steffen Hurrle	DECLARE_SOCKADDR(struct sockaddr_ipx *, sipx, msg->msg_name);
17641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ipxhdr *ipx = NULL;
17651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sk_buff *skb;
17661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int copied, rc;
176701462405f0c093b2f8dfddafcadcda6c9e4c5cdfJiri Bohac	bool locked = true;
17681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1769b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
17701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* put the autobinding in */
17711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ipxs->port) {
17721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct sockaddr_ipx uaddr;
17731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uaddr.sipx_port		= 0;
17751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uaddr.sipx_network 	= 0;
17761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPX_INTERN
17781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -ENETDOWN;
17791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ipxs->intrfc)
17801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out; /* Someone zonked the iface */
17811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN);
17821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif	/* CONFIG_IPX_INTERN */
17831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
178483927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmann		rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
17851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      sizeof(struct sockaddr_ipx));
17861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc)
17871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
17881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1789981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki
17901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -ENOTCONN;
17911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sock_flag(sk, SOCK_ZAPPED))
17921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
17931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
179401462405f0c093b2f8dfddafcadcda6c9e4c5cdfJiri Bohac	release_sock(sk);
179501462405f0c093b2f8dfddafcadcda6c9e4c5cdfJiri Bohac	locked = false;
17961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
17971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				flags & MSG_DONTWAIT, &rc);
17988815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	if (!skb) {
17998815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca		if (rc == -EAGAIN && (sk->sk_shutdown & RCV_SHUTDOWN))
18008815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca			rc = 0;
18011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
18028815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	}
18031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx 	= ipx_hdr(skb);
18051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	copied 	= ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
18061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copied > size) {
18071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copied = size;
18081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		msg->msg_flags |= MSG_TRUNC;
18091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
18101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = skb_copy_datagram_iovec(skb, sizeof(struct ipxhdr), msg->msg_iov,
18121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     copied);
18131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
18141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_free;
1815b7aa0bf70c4afb9e38be25f5c0922498d0f8684cEric Dumazet	if (skb->tstamp.tv64)
1816b7aa0bf70c4afb9e38be25f5c0922498d0f8684cEric Dumazet		sk->sk_stamp = skb->tstamp;
18171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sipx) {
18191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_family	= AF_IPX;
18201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_port		= ipx->ipx_source.sock;
18211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(sipx->sipx_node, ipx->ipx_source.node, IPX_NODE_LEN);
18221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_network	= IPX_SKB_CB(skb)->ipx_source_net;
18231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_type 	= ipx->ipx_type;
18241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sipx->sipx_zero		= 0;
1825f3d3342602f8bcbf37d7c46641cb9bca7618eb1cHannes Frederic Sowa		msg->msg_namelen	= sizeof(*sipx);
18261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
18271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = copied;
18281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_free:
18301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb_free_datagram(sk, skb);
18311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
183201462405f0c093b2f8dfddafcadcda6c9e4c5cdfJiri Bohac	if (locked)
183301462405f0c093b2f8dfddafcadcda6c9e4c5cdfJiri Bohac		release_sock(sk);
18341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
18351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipx_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
18391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
18401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 0;
18411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	long amount = 0;
18421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
18431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
18441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1845b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	lock_sock(sk);
18461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
18471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case TIOCOUTQ:
184831e6d363abcd0d05766c82f1a9c905a4c974a199Eric Dumazet		amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
18491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (amount < 0)
18501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			amount = 0;
18511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = put_user(amount, (int __user *)argp);
18521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case TIOCINQ: {
18541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
18551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* These two are safe on a single CPU system as only
18561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * user tasks fiddle here */
18571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (skb)
18581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			amount = skb->len - sizeof(struct ipxhdr);
18591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = put_user(amount, (int __user *)argp);
18601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
18621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCADDRT:
18631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCDELRT:
18641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EPERM;
18651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (capable(CAP_NET_ADMIN))
18661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = ipxrtr_ioctl(cmd, argp);
18671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCSIFADDR:
18691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCAIPXITFCRT:
18701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCAIPXPRISLT:
18711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EPERM;
18721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!capable(CAP_NET_ADMIN))
18731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
18741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCGIFADDR:
18751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = ipxitf_ioctl(cmd, argp);
18761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCIPXCFGDATA:
18781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = ipxcfg_get_config_data(argp);
18791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCIPXNCPCONN:
18811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
18821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * This socket wants to take care of the NCP connection
18831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * handed to us in arg.
18841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
1885981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki		rc = -EPERM;
1886981c0ff6900c981668a798fe9e0bc5ba32ee3fd4YOSHIFUJI Hideaki		if (!capable(CAP_NET_ADMIN))
18871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
18881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = get_user(ipx_sk(sk)->ipx_ncp_conn,
18891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      (const unsigned short __user *)argp);
18901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCGSTAMP:
189232e9072b92a1c556a303d8d0e0d64feb667e601dDavid S. Miller		rc = sock_get_timestamp(sk, argp);
18931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
18941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCGIFDSTADDR:
18951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCSIFDSTADDR:
18961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCGIFBRDADDR:
18971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCSIFBRDADDR:
18981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCGIFNETMASK:
18991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SIOCSIFNETMASK:
19001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EINVAL;
19011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
19021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
1903b5e5fa5e093e42cab4ee3d6dcbc4f450ad29a723Christoph Hellwig		rc = -ENOIOCTLCMD;
19041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
19051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1906b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	release_sock(sk);
19071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
19091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
19101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec
1912f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec#ifdef CONFIG_COMPAT
1913f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovecstatic int ipx_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
1914f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec{
1915f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	/*
1916f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	 * These 4 commands use same structure on 32bit and 64bit.  Rest of IPX
1917f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	 * commands is handled by generic ioctl code.  As these commands are
1918f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	 * SIOCPROTOPRIVATE..SIOCPROTOPRIVATE+3, they cannot be handled by generic
1919f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	 * code.
1920f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	 */
1921f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	switch (cmd) {
1922f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	case SIOCAIPXITFCRT:
1923f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	case SIOCAIPXPRISLT:
1924f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	case SIOCIPXCFGDATA:
1925f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	case SIOCIPXNCPCONN:
1926f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec		return ipx_ioctl(sock, cmd, arg);
1927f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	default:
1928f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec		return -ENOIOCTLCMD;
1929f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	}
1930f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec}
1931f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec#endif
1932f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec
19338815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubrocastatic int ipx_shutdown(struct socket *sock, int mode)
19348815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca{
19358815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	struct sock *sk = sock->sk;
19368815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca
19378815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	if (mode < SHUT_RD || mode > SHUT_RDWR)
19388815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca		return -EINVAL;
19398815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	/* This maps:
19408815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	 * SHUT_RD   (0) -> RCV_SHUTDOWN  (1)
19418815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	 * SHUT_WR   (1) -> SEND_SHUTDOWN (2)
19428815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	 * SHUT_RDWR (2) -> SHUTDOWN_MASK (3)
19438815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	 */
19448815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	++mode;
19458815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca
19468815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	lock_sock(sk);
19478815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	sk->sk_shutdown |= mode;
19488815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	release_sock(sk);
19498815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	sk->sk_state_change(sk);
19508815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca
19518815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	return 0;
19528815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca}
1953f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec
19541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
19551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Socket family declarations
19561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
19571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1958ec1b4cf74c81bfd0fbe5bf62bafc86c45917e72fStephen Hemmingerstatic const struct net_proto_family ipx_family_ops = {
19591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.family		= PF_IPX,
19601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.create		= ipx_create,
19611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
19621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
19631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
196483927ba069a65326f39991a02d6a49ba3b7cea44Arnd Bergmannstatic const struct proto_ops ipx_dgram_ops = {
19651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.family		= PF_IPX,
19661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
19671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release	= ipx_release,
19681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.bind		= ipx_bind,
19691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.connect	= ipx_connect,
19701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.socketpair	= sock_no_socketpair,
19711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.accept		= sock_no_accept,
19721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.getname	= ipx_getname,
1973b0d0d915d1d1a0fe486849f3e41333c66df620c4Arnd Bergmann	.poll		= datagram_poll,
19741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl		= ipx_ioctl,
1975f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec#ifdef CONFIG_COMPAT
1976f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec	.compat_ioctl	= ipx_compat_ioctl,
1977f6c90b71a355a0a4a22e1cfee5748617adc25a53Petr Vandrovec#endif
19781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.listen		= sock_no_listen,
19798815cbd9e38a7a02bfb79854aa7d33f1e1e50305Sabrina Dubroca	.shutdown	= ipx_shutdown,
19801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.setsockopt	= ipx_setsockopt,
19811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.getsockopt	= ipx_getsockopt,
19821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.sendmsg	= ipx_sendmsg,
19831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.recvmsg	= ipx_recvmsg,
19841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.mmap		= sock_no_mmap,
19851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.sendpage	= sock_no_sendpage,
19861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
19871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19887546dd97d27306d939c13e03318aae695badaa88Stephen Hemmingerstatic struct packet_type ipx_8023_packet_type __read_mostly = {
198909640e6365c679b5642b1c41b6d7078f51689ddfHarvey Harrison	.type		= cpu_to_be16(ETH_P_802_3),
19901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.func		= ipx_rcv,
19911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
19921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19937546dd97d27306d939c13e03318aae695badaa88Stephen Hemmingerstatic struct packet_type ipx_dix_packet_type __read_mostly = {
199409640e6365c679b5642b1c41b6d7078f51689ddfHarvey Harrison	.type		= cpu_to_be16(ETH_P_IPX),
19951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.func		= ipx_rcv,
19961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
19971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct notifier_block ipx_dev_notifier = {
19991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.notifier_call	= ipxitf_device_event,
20001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
20011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2002fa665ccf01440644a3956ed039e51e1088cd0f15Stephen Hemmingerstatic const unsigned char ipx_8022_type = 0xE0;
2003fa665ccf01440644a3956ed039e51e1088cd0f15Stephen Hemmingerstatic const unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
2004fa665ccf01440644a3956ed039e51e1088cd0f15Stephen Hemmingerstatic const char ipx_EII_err_msg[] __initconst =
20051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	KERN_CRIT "IPX: Unable to register with Ethernet II\n";
2006fa665ccf01440644a3956ed039e51e1088cd0f15Stephen Hemmingerstatic const char ipx_8023_err_msg[] __initconst =
20071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	KERN_CRIT "IPX: Unable to register with 802.3\n";
2008fa665ccf01440644a3956ed039e51e1088cd0f15Stephen Hemmingerstatic const char ipx_llc_err_msg[] __initconst =
20091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	KERN_CRIT "IPX: Unable to register with 802.2\n";
2010fa665ccf01440644a3956ed039e51e1088cd0f15Stephen Hemmingerstatic const char ipx_snap_err_msg[] __initconst =
20111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	KERN_CRIT "IPX: Unable to register with SNAP\n";
20121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ipx_init(void)
20141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
20151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = proto_register(&ipx_proto, 1);
20161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc != 0)
20181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
20191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock_register(&ipx_family_ops);
20211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pEII_datalink = make_EII_client();
20231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pEII_datalink)
20241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_add_pack(&ipx_dix_packet_type);
20251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
20261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(ipx_EII_err_msg);
20271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	p8023_datalink = make_8023_client();
20291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (p8023_datalink)
20301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_add_pack(&ipx_8023_packet_type);
20311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
20321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(ipx_8023_err_msg);
20331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	p8022_datalink = register_8022_client(ipx_8022_type, ipx_rcv);
20351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!p8022_datalink)
20361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(ipx_llc_err_msg);
20371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pSNAP_datalink = register_snap_client(ipx_snap_id, ipx_rcv);
20391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pSNAP_datalink)
20401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(ipx_snap_err_msg);
20411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	register_netdevice_notifier(&ipx_dev_notifier);
20431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_register_sysctl();
20441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_proc_init();
20451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
20461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
20471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ipx_proto_finito(void)
20501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
20511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_proc_exit();
20521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipx_unregister_sysctl();
20531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_netdevice_notifier(&ipx_dev_notifier);
20551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ipxitf_cleanup();
20571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20581539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	if (pSNAP_datalink) {
20591539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		unregister_snap_client(pSNAP_datalink);
20601539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		pSNAP_datalink = NULL;
20611539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	}
20621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20631539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	if (p8022_datalink) {
20641539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		unregister_8022_client(p8022_datalink);
20651539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		p8022_datalink = NULL;
20661539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	}
20671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_remove_pack(&ipx_8023_packet_type);
20691539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	if (p8023_datalink) {
20701539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		destroy_8023_client(p8023_datalink);
20711539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		p8023_datalink = NULL;
20721539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	}
20731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_remove_pack(&ipx_dix_packet_type);
20751539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	if (pEII_datalink) {
20761539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		destroy_EII_client(pEII_datalink);
20771539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac		pEII_datalink = NULL;
20781539b98b561754252dd520b98fa03a688a4f81b5Jiri Bohac	}
20791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	proto_unregister(&ipx_proto);
20811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock_unregister(ipx_family_ops.family);
20821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(ipx_init);
20851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(ipx_proto_finito);
20861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
20871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_NETPROTO(PF_IPX);
2088