af_ipx.c 49.4 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *	Implements an IPX socket layer.
 *
 *	This code is derived from work by
 *		Ross Biro	: 	Writing the original IP stack
 *		Fred Van Kempen :	Tidying up the TCP/IP
 *
 *	Many thanks go to Keith Baker, Institute For Industrial Information
 *	Technology Ltd, Swansea University for allowing me to work on this
 *	in my own time even though it was in some ways related to commercial
 *	work I am currently employed to do there.
 *
 *	All the material in this file is subject to the Gnu license version 2.
 *	Neither Alan Cox nor the Swansea University Computer Society admit 
 *	liability nor provide warranty for any of this software. This material
 *	is provided as is and at no charge.
 *
18
 *	Portions Copyright (c) 2000-2003 Conectiva, Inc. <acme@conectiva.com.br>
19 20 21
 *	Neither Arnaldo Carvalho de Melo nor Conectiva, Inc. admit liability nor
 *	provide warranty for any of this software. This material is provided
 *	"AS-IS" and at no charge.
Linus Torvalds's avatar
Linus Torvalds committed
22 23 24 25 26
 *
 * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
 *	Neither Greg Page nor Caldera, Inc. admit liability nor provide
 *	warranty for any of this software. This material is provided
 *	"AS-IS" and at no charge.
27 28
 *
 *	See net/ipx/ChangeLog.
Linus Torvalds's avatar
Linus Torvalds committed
29 30 31 32
 */

#include <linux/config.h>
#include <linux/errno.h>
33 34 35 36
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/init.h>
#include <linux/ipx.h>
Linus Torvalds's avatar
Linus Torvalds committed
37
#include <linux/kernel.h>
38 39
#include <linux/list.h>
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
40 41
#include <linux/net.h>
#include <linux/netdevice.h>
42 43 44 45 46
#include <linux/uio.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/string.h>
47
#include <linux/tcp.h>
48 49 50 51
#include <linux/types.h>
#include <linux/termios.h>

#include <net/ipx.h>
Linus Torvalds's avatar
Linus Torvalds committed
52 53
#include <net/p8022.h>
#include <net/psnap.h>
54 55 56
#include <net/sock.h>

#include <asm/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
57

Linus Torvalds's avatar
Linus Torvalds committed
58
#ifdef CONFIG_SYSCTL
Linus Torvalds's avatar
Linus Torvalds committed
59 60
extern void ipx_register_sysctl(void);
extern void ipx_unregister_sysctl(void);
Linus Torvalds's avatar
Linus Torvalds committed
61 62 63 64
#else
#define ipx_register_sysctl()
#define ipx_unregister_sysctl()
#endif
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69

/* Configuration Variables */
static unsigned char ipxcfg_max_hops = 16;
static char ipxcfg_auto_select_primary;
static char ipxcfg_auto_create_interfaces;
Linus Torvalds's avatar
Linus Torvalds committed
70
int sysctl_ipx_pprop_broadcasting = 1;
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75 76 77 78 79

/* Global Variables */
static struct datalink_proto *p8022_datalink;
static struct datalink_proto *pEII_datalink;
static struct datalink_proto *p8023_datalink;
static struct datalink_proto *pSNAP_datalink;

static struct proto_ops ipx_dgram_ops;

80
LIST_HEAD(ipx_interfaces);
81
spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED;
Linus Torvalds's avatar
Linus Torvalds committed
82

83
struct ipx_interface *ipx_primary_net;
84 85 86 87 88 89 90 91 92 93
struct ipx_interface *ipx_internal_net;

extern int ipxrtr_add_route(__u32 network, struct ipx_interface *intrfc,
			    unsigned char *node);
extern void ipxrtr_del_routes(struct ipx_interface *intrfc);
extern int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
			       struct iovec *iov, int len, int noblock);
extern int ipxrtr_route_skb(struct sk_buff *skb);
extern struct ipx_route *ipxrtr_lookup(__u32 net);
extern int ipxrtr_ioctl(unsigned int cmd, void *arg);
Linus Torvalds's avatar
Linus Torvalds committed
94 95 96 97 98 99

#undef IPX_REFCNT_DEBUG
#ifdef IPX_REFCNT_DEBUG
atomic_t ipx_sock_nr;
#endif

100 101 102 103 104 105 106 107 108 109
struct ipx_interface *ipx_interfaces_head(void)
{
	struct ipx_interface *rc = NULL;

	if (!list_empty(&ipx_interfaces))
		rc = list_entry(ipx_interfaces.next,
				struct ipx_interface, node);
	return rc;
}

Linus Torvalds's avatar
Linus Torvalds committed
110
static void ipxcfg_set_auto_select(char val)
Linus Torvalds's avatar
Linus Torvalds committed
111 112
{
	ipxcfg_auto_select_primary = val;
Linus Torvalds's avatar
Linus Torvalds committed
113
	if (val && !ipx_primary_net)
114
		ipx_primary_net = ipx_interfaces_head();
Linus Torvalds's avatar
Linus Torvalds committed
115 116
}

117
static int ipxcfg_get_config_data(struct ipx_config_data *arg)
Linus Torvalds's avatar
Linus Torvalds committed
118
{
119
	struct ipx_config_data vals;
Linus Torvalds's avatar
Linus Torvalds committed
120 121

	vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;
Linus Torvalds's avatar
Linus Torvalds committed
122
	vals.ipxcfg_auto_select_primary	   = ipxcfg_auto_select_primary;
Linus Torvalds's avatar
Linus Torvalds committed
123

Linus Torvalds's avatar
Linus Torvalds committed
124
	return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0;
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127 128 129 130 131 132
}

/*
 * Note: Sockets may not be removed _during_ an interrupt or inet_bh
 * handler using this technique. They can be added although we do not
 * use this facility.
 */

133
static void ipx_remove_socket(struct sock *sk)
Linus Torvalds's avatar
Linus Torvalds committed
134 135
{
	/* Determine interface with which socket is associated */
136
	struct ipx_interface *intrfc = ipx_sk(sk)->intrfc;
137

Linus Torvalds's avatar
Linus Torvalds committed
138
	if (!intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
139
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142

	ipxitf_hold(intrfc);
	spin_lock_bh(&intrfc->if_sklist_lock);
143
	sk_del_node_init(sk);
Linus Torvalds's avatar
Linus Torvalds committed
144
	spin_unlock_bh(&intrfc->if_sklist_lock);
Linus Torvalds's avatar
Linus Torvalds committed
145
	ipxitf_put(intrfc);
146 147
out:
	return;
Linus Torvalds's avatar
Linus Torvalds committed
148 149 150 151 152
}

static void ipx_destroy_socket(struct sock *sk)
{
	ipx_remove_socket(sk);
153
	skb_queue_purge(&sk->sk_receive_queue);
Linus Torvalds's avatar
Linus Torvalds committed
154 155 156 157
#ifdef IPX_REFCNT_DEBUG
        atomic_dec(&ipx_sock_nr);
        printk(KERN_DEBUG "IPX socket %p released, %d are still alive\n", sk,
			atomic_read(&ipx_sock_nr));
158
	if (atomic_read(&sk->sk_refcnt) != 1)
Linus Torvalds's avatar
Linus Torvalds committed
159
		printk(KERN_DEBUG "Destruction sock ipx %p delayed, cnt=%d\n",
160
				sk, atomic_read(&sk->sk_refcnt));
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163 164 165 166 167 168 169 170 171 172 173
#endif
	sock_put(sk);
}

/* 
 * The following code is used to support IPX Interfaces (IPXITF).  An
 * IPX interface is defined by a physical device and a frame type.
 */

/* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */

static void ipxitf_clear_primary_net(void)
{
174 175 176
	ipx_primary_net = NULL;
	if (ipxcfg_auto_select_primary)
		ipx_primary_net = ipx_interfaces_head();
Linus Torvalds's avatar
Linus Torvalds committed
177 178
}

179 180
static struct ipx_interface *__ipxitf_find_using_phys(struct net_device *dev,
						      unsigned short datalink)
Linus Torvalds's avatar
Linus Torvalds committed
181
{
182
	struct ipx_interface *i;
Linus Torvalds's avatar
Linus Torvalds committed
183

184 185 186 187 188
	list_for_each_entry(i, &ipx_interfaces, node)
		if (i->if_dev == dev && i->if_dlink_type == datalink)
			goto out;
	i = NULL;
out:
Linus Torvalds's avatar
Linus Torvalds committed
189
	return i;
Linus Torvalds's avatar
Linus Torvalds committed
190 191
}

192 193
static struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev,
						    unsigned short datalink)
Linus Torvalds's avatar
Linus Torvalds committed
194
{
195
	struct ipx_interface *i;
Linus Torvalds's avatar
Linus Torvalds committed
196 197 198 199 200 201 202 203 204

	spin_lock_bh(&ipx_interfaces_lock);
	i = __ipxitf_find_using_phys(dev, datalink);
	if (i)
		ipxitf_hold(i);
	spin_unlock_bh(&ipx_interfaces_lock);
	return i;
}

205
struct ipx_interface *ipxitf_find_using_net(__u32 net)
Linus Torvalds's avatar
Linus Torvalds committed
206
{
207
	struct ipx_interface *i;
Linus Torvalds's avatar
Linus Torvalds committed
208 209

	spin_lock_bh(&ipx_interfaces_lock);
210 211 212 213 214 215 216 217 218
	if (net) {
		list_for_each_entry(i, &ipx_interfaces, node)
			if (i->if_netnum == net)
				goto hold;
		i = NULL;
		goto unlock;
	}

	i = ipx_primary_net;
Linus Torvalds's avatar
Linus Torvalds committed
219
	if (i)
220
hold:
Linus Torvalds's avatar
Linus Torvalds committed
221
		ipxitf_hold(i);
222
unlock:
Linus Torvalds's avatar
Linus Torvalds committed
223
	spin_unlock_bh(&ipx_interfaces_lock);
Linus Torvalds's avatar
Linus Torvalds committed
224
	return i;
Linus Torvalds's avatar
Linus Torvalds committed
225 226 227
}

/* Sockets are bound to a particular IPX interface. */
228
static void ipxitf_insert_socket(struct ipx_interface *intrfc, struct sock *sk)
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231
{
	ipxitf_hold(intrfc);
	spin_lock_bh(&intrfc->if_sklist_lock);
232
	ipx_sk(sk)->intrfc = intrfc;
233
	sk_add_node(sk, &intrfc->if_sklist);
Linus Torvalds's avatar
Linus Torvalds committed
234 235 236 237 238
	spin_unlock_bh(&intrfc->if_sklist_lock);
	ipxitf_put(intrfc);
}

/* caller must hold intrfc->if_sklist_lock */
239
static struct sock *__ipxitf_find_socket(struct ipx_interface *intrfc,
Linus Torvalds's avatar
Linus Torvalds committed
240
					 unsigned short port)
Linus Torvalds's avatar
Linus Torvalds committed
241
{
242 243
	struct sock *s;
	struct hlist_node *node;
Linus Torvalds's avatar
Linus Torvalds committed
244

245 246 247 248 249
	sk_for_each(s, node, &intrfc->if_sklist)
		if (ipx_sk(s)->port == port)
			goto found;
	s = NULL;
found:
Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253
	return s;
}

/* caller must hold a reference to intrfc */
254
static struct sock *ipxitf_find_socket(struct ipx_interface *intrfc,
Linus Torvalds's avatar
Linus Torvalds committed
255
					unsigned short port)
Linus Torvalds's avatar
Linus Torvalds committed
256 257 258 259 260 261 262 263 264
{
	struct sock *s;

	spin_lock_bh(&intrfc->if_sklist_lock);
	s = __ipxitf_find_socket(intrfc, port);
	if (s)
		sock_hold(s);
	spin_unlock_bh(&intrfc->if_sklist_lock);

Linus Torvalds's avatar
Linus Torvalds committed
265
	return s;
Linus Torvalds's avatar
Linus Torvalds committed
266 267 268
}

#ifdef CONFIG_IPX_INTERN
269
static struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc,
270
						unsigned char *ipx_node,
271
						unsigned short port)
Linus Torvalds's avatar
Linus Torvalds committed
272 273
{
	struct sock *s;
274
	struct hlist_node *node;
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278

	ipxitf_hold(intrfc);
	spin_lock_bh(&intrfc->if_sklist_lock);

279
	sk_for_each(s, node, &intrfc->if_sklist) {
280 281 282
		struct ipx_opt *ipxs = ipx_sk(s);

		if (ipxs->port == port &&
283
		    !memcmp(ipx_node, ipxs->node, IPX_NODE_LEN))
284
			goto found;
Linus Torvalds's avatar
Linus Torvalds committed
285
	}
286 287
	s = NULL;
found:
Linus Torvalds's avatar
Linus Torvalds committed
288 289
	spin_unlock_bh(&intrfc->if_sklist_lock);
	ipxitf_put(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
290
	return s;
Linus Torvalds's avatar
Linus Torvalds committed
291 292 293
}
#endif

294
void __ipxitf_down(struct ipx_interface *intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
295
{
296 297
	struct sock *s;
	struct hlist_node *node, *t;
Linus Torvalds's avatar
Linus Torvalds committed
298 299 300 301 302 303

	/* Delete all routes associated with this interface */
	ipxrtr_del_routes(intrfc);

	spin_lock_bh(&intrfc->if_sklist_lock);
	/* error sockets */
304
	sk_for_each_safe(s, node, t, &intrfc->if_sklist) {
305 306
		struct ipx_opt *ipxs = ipx_sk(s);

307 308
		s->sk_err = ENOLINK;
		s->sk_error_report(s);
309 310
		ipxs->intrfc = NULL;
		ipxs->port   = 0;
311
		s->sk_zapped = 1;	/* Indicates it is no longer bound */
312
		sk_del_node_init(s);
Linus Torvalds's avatar
Linus Torvalds committed
313
	}
314
	INIT_HLIST_HEAD(&intrfc->if_sklist);
Linus Torvalds's avatar
Linus Torvalds committed
315 316 317
	spin_unlock_bh(&intrfc->if_sklist_lock);

	/* remove this interface from list */
318
	list_del(&intrfc->node);
Linus Torvalds's avatar
Linus Torvalds committed
319 320

	/* remove this interface from *special* networks */
Linus Torvalds's avatar
Linus Torvalds committed
321
	if (intrfc == ipx_primary_net)
Linus Torvalds's avatar
Linus Torvalds committed
322
		ipxitf_clear_primary_net();
Linus Torvalds's avatar
Linus Torvalds committed
323
	if (intrfc == ipx_internal_net)
Linus Torvalds's avatar
Linus Torvalds committed
324 325 326 327 328
		ipx_internal_net = NULL;

	if (intrfc->if_dev)
		dev_put(intrfc->if_dev);
	kfree(intrfc);
329
	module_put(THIS_MODULE);
Linus Torvalds's avatar
Linus Torvalds committed
330 331
}

332
void ipxitf_down(struct ipx_interface *intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
333 334 335 336 337 338
{
	spin_lock_bh(&ipx_interfaces_lock);
	__ipxitf_down(intrfc);
	spin_unlock_bh(&ipx_interfaces_lock);
}

Linus Torvalds's avatar
Linus Torvalds committed
339 340
static int ipxitf_device_event(struct notifier_block *notifier,
				unsigned long event, void *ptr)
Linus Torvalds's avatar
Linus Torvalds committed
341 342
{
	struct net_device *dev = ptr;
343
	struct ipx_interface *i, *tmp;
Linus Torvalds's avatar
Linus Torvalds committed
344

Linus Torvalds's avatar
Linus Torvalds committed
345 346
	if (event != NETDEV_DOWN && event != NETDEV_UP)
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
347 348

	spin_lock_bh(&ipx_interfaces_lock);
349
	list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)
Linus Torvalds's avatar
Linus Torvalds committed
350
		if (i->if_dev == dev) {
Linus Torvalds's avatar
Linus Torvalds committed
351 352 353 354
			if (event == NETDEV_UP)
				ipxitf_hold(i);
			else
				__ipxitf_put(i);
Linus Torvalds's avatar
Linus Torvalds committed
355
		}
Linus Torvalds's avatar
Linus Torvalds committed
356
	spin_unlock_bh(&ipx_interfaces_lock);
357 358
out:
	return NOTIFY_DONE;
Linus Torvalds's avatar
Linus Torvalds committed
359 360
}

Linus Torvalds's avatar
Linus Torvalds committed
361
static void ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
362
{
Linus Torvalds's avatar
Linus Torvalds committed
363
	if (sock_queue_rcv_skb(sock, skb) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
364 365 366 367 368 369 370 371 372 373
		kfree_skb(skb);
}

/*
 * On input skb->sk is NULL. Nobody is charged for the memory.
 */

/* caller must hold a reference to intrfc */

#ifdef CONFIG_IPX_INTERN
374 375
static int ipxitf_demux_socket(struct ipx_interface *intrfc,
			       struct sk_buff *skb, int copy)
Linus Torvalds's avatar
Linus Torvalds committed
376
{
377
	struct ipxhdr *ipx = ipx_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
378 379
	int is_broadcast = !memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
				   IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
380
	struct sock *s;
381
	struct hlist_node *node;
382
	int rc;
Linus Torvalds's avatar
Linus Torvalds committed
383 384 385

	spin_lock_bh(&intrfc->if_sklist_lock);

386
	sk_for_each(s, node, &intrfc->if_sklist) {
387 388 389
		struct ipx_opt *ipxs = ipx_sk(s);

		if (ipxs->port == ipx->ipx_dest.sock &&
Linus Torvalds's avatar
Linus Torvalds committed
390
		    (is_broadcast || !memcmp(ipx->ipx_dest.node,
391
					     ipxs->node, IPX_NODE_LEN))) {
Linus Torvalds's avatar
Linus Torvalds committed
392 393 394
			/* We found a socket to which to send */
			struct sk_buff *skb1;

Linus Torvalds's avatar
Linus Torvalds committed
395
			if (copy) {
Linus Torvalds's avatar
Linus Torvalds committed
396
				skb1 = skb_clone(skb, GFP_ATOMIC);
397
				rc = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
398
				if (!skb1)
Linus Torvalds's avatar
Linus Torvalds committed
399
					goto out;
Linus Torvalds's avatar
Linus Torvalds committed
400
			} else {
Linus Torvalds's avatar
Linus Torvalds committed
401 402 403 404 405 406
				skb1 = skb;
				copy = 1; /* skb may only be used once */
			}
			ipxitf_def_skb_handler(s, skb1);

			/* On an external interface, one socket can listen */
Linus Torvalds's avatar
Linus Torvalds committed
407
			if (intrfc != ipx_internal_net)
Linus Torvalds's avatar
Linus Torvalds committed
408 409 410 411 412
				break;
		}
	}

	/* skb was solely for us, and we did not make a copy, so free it. */
Linus Torvalds's avatar
Linus Torvalds committed
413
	if (!copy)
Linus Torvalds's avatar
Linus Torvalds committed
414 415
		kfree_skb(skb);

416
	rc = 0;
417 418
out:
	spin_unlock_bh(&intrfc->if_sklist_lock);
419
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
420 421
}
#else
422
static struct sock *ncp_connection_hack(struct ipx_interface *intrfc,
Linus Torvalds's avatar
Linus Torvalds committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
					struct ipxhdr *ipx)
{
	/* The packet's target is a NCP connection handler. We want to hand it
	 * to the correct socket directly within the kernel, so that the
	 * mars_nwe packet distribution process does not have to do it. Here we
	 * only care about NCP and BURST packets.
	 *
	 * You might call this a hack, but believe me, you do not want a
	 * complete NCP layer in the kernel, and this is VERY fast as well. */
	struct sock *sk = NULL;
 	int connection = 0;
	u8 *ncphdr = (u8 *)(ipx + 1);

 	if (*ncphdr == 0x22 && *(ncphdr + 1) == 0x22) /* NCP request */
		connection = (((int) *(ncphdr + 5)) << 8) | (int) *(ncphdr + 3);
	else if (*ncphdr == 0x77 && *(ncphdr + 1) == 0x77) /* BURST packet */
		connection = (((int) *(ncphdr + 9)) << 8) | (int) *(ncphdr + 8);

	if (connection) {
442
		struct hlist_node *node;
Linus Torvalds's avatar
Linus Torvalds committed
443 444 445 446
		/* Now we have to look for a special NCP connection handling
		 * socket. Only these sockets have ipx_ncp_conn != 0, set by
		 * SIOCIPXNCPCONN. */
		spin_lock_bh(&intrfc->if_sklist_lock);
447 448 449 450 451 452 453
		sk_for_each(sk, node, &intrfc->if_sklist)
			if (ipx_sk(sk)->ipx_ncp_conn == connection) {
				sock_hold(sk);
				goto found;
			}
		sk = NULL;
	found:
Linus Torvalds's avatar
Linus Torvalds committed
454 455 456 457 458
		spin_unlock_bh(&intrfc->if_sklist_lock);
	}
	return sk;
}

459 460
static int ipxitf_demux_socket(struct ipx_interface *intrfc,
			       struct sk_buff *skb, int copy)
Linus Torvalds's avatar
Linus Torvalds committed
461
{
462
	struct ipxhdr *ipx = ipx_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
463 464
	struct sock *sock1 = NULL, *sock2 = NULL;
	struct sk_buff *skb1 = NULL, *skb2 = NULL;
465
	int rc;
Linus Torvalds's avatar
Linus Torvalds committed
466

Linus Torvalds's avatar
Linus Torvalds committed
467 468
	if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451)
		sock1 = ncp_connection_hack(intrfc, ipx);
Linus Torvalds's avatar
Linus Torvalds committed
469 470
        if (!sock1)
		/* No special socket found, forward the packet the normal way */
Linus Torvalds's avatar
Linus Torvalds committed
471 472 473 474 475 476 477 478 479 480
		sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);

	/*
	 * We need to check if there is a primary net and if
	 * this is addressed to one of the *SPECIAL* sockets because
	 * these need to be propagated to the primary net.
	 * The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
	 * 0x456(Diagnostic).
	 */

Linus Torvalds's avatar
Linus Torvalds committed
481
	if (ipx_primary_net && intrfc != ipx_primary_net) {
Linus Torvalds's avatar
Linus Torvalds committed
482 483 484 485 486 487 488 489 490
		const int dsock = ntohs(ipx->ipx_dest.sock);

		if (dsock == 0x452 || dsock == 0x453 || dsock == 0x456)
			/* The appropriate thing to do here is to dup the
			 * packet and route to the primary net interface via
			 * ipxitf_send; however, we'll cheat and just demux it
			 * here. */
			sock2 = ipxitf_find_socket(ipx_primary_net,
							ipx->ipx_dest.sock);
Linus Torvalds's avatar
Linus Torvalds committed
491 492 493 494 495
	}

	/*
	 * If there is nothing to do return. The kfree will cancel any charging.
	 */
496
	rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
497 498
	if (!sock1 && !sock2) {
		if (!copy)
Linus Torvalds's avatar
Linus Torvalds committed
499
			kfree_skb(skb);
500
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
501 502 503 504 505 506 507 508 509 510
	}

	/*
	 * This next segment of code is a little awkward, but it sets it up
	 * so that the appropriate number of copies of the SKB are made and
	 * that skb1 and skb2 point to it (them) so that it (they) can be
	 * demuxed to sock1 and/or sock2.  If we are unable to make enough
	 * copies, we do as much as is possible.
	 */

Linus Torvalds's avatar
Linus Torvalds committed
511
	if (copy)
Linus Torvalds's avatar
Linus Torvalds committed
512 513 514 515
		skb1 = skb_clone(skb, GFP_ATOMIC);
	else
		skb1 = skb;

516
	rc = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
517
	if (!skb1)
518
		goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
519 520

	/* Do we need 2 SKBs? */
Linus Torvalds's avatar
Linus Torvalds committed
521
	if (sock1 && sock2)
Linus Torvalds's avatar
Linus Torvalds committed
522 523 524 525
		skb2 = skb_clone(skb1, GFP_ATOMIC);
	else
		skb2 = skb1;

Linus Torvalds's avatar
Linus Torvalds committed
526 527
	if (sock1)
		ipxitf_def_skb_handler(sock1, skb1);
Linus Torvalds's avatar
Linus Torvalds committed
528

Linus Torvalds's avatar
Linus Torvalds committed
529
	if (!skb2)
530
		goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
531

Linus Torvalds's avatar
Linus Torvalds committed
532 533
	if (sock2)
		ipxitf_def_skb_handler(sock2, skb2);
Linus Torvalds's avatar
Linus Torvalds committed
534

535 536
	rc = 0;
out_put:
537
	if (sock1)
Linus Torvalds's avatar
Linus Torvalds committed
538 539 540
		sock_put(sock1);
	if (sock2)
		sock_put(sock2);
541 542
out:
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
543 544 545
}
#endif	/* CONFIG_IPX_INTERN */

546
static struct sk_buff *ipxitf_adjust_skbuff(struct ipx_interface *intrfc,
Linus Torvalds's avatar
Linus Torvalds committed
547
					    struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
548 549
{
	struct sk_buff *skb2;
550
	int in_offset = (unsigned char *)ipx_hdr(skb) - skb->head;
Linus Torvalds's avatar
Linus Torvalds committed
551 552 553 554
	int out_offset = intrfc->if_ipx_offset;
	int len;

	/* Hopefully, most cases */
Linus Torvalds's avatar
Linus Torvalds committed
555 556
	if (in_offset >= out_offset)
		return skb;
Linus Torvalds's avatar
Linus Torvalds committed
557 558 559 560

	/* Need new SKB */
	len  = skb->len + out_offset;
	skb2 = alloc_skb(len, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
561
	if (skb2) {
Linus Torvalds's avatar
Linus Torvalds committed
562
		skb_reserve(skb2, out_offset);
Linus Torvalds's avatar
Linus Torvalds committed
563
		skb2->nh.raw = skb2->h.raw = skb_put(skb2, skb->len);
564
		memcpy(ipx_hdr(skb2), ipx_hdr(skb), skb->len);
Linus Torvalds's avatar
Linus Torvalds committed
565
		memcpy(skb2->cb, skb->cb, sizeof(skb->cb));
Linus Torvalds's avatar
Linus Torvalds committed
566 567
	}
	kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
568
	return skb2;
Linus Torvalds's avatar
Linus Torvalds committed
569 570
}

Linus Torvalds's avatar
Linus Torvalds committed
571
/* caller must hold a reference to intrfc and the skb has to be unshared */
572
int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node)
Linus Torvalds's avatar
Linus Torvalds committed
573
{
574
	struct ipxhdr *ipx = ipx_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
575 576 577 578 579
	struct net_device *dev = intrfc->if_dev;
	struct datalink_proto *dl = intrfc->if_dlink;
	char dest_node[IPX_NODE_LEN];
	int send_to_wire = 1;
	int addr_len;
Linus Torvalds's avatar
Linus Torvalds committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593

	ipx->ipx_tctrl = IPX_SKB_CB(skb)->ipx_tctrl;
	ipx->ipx_dest.net = IPX_SKB_CB(skb)->ipx_dest_net;
	ipx->ipx_source.net = IPX_SKB_CB(skb)->ipx_source_net;

	/* see if we need to include the netnum in the route list */
	if (IPX_SKB_CB(skb)->last_hop.index >= 0) {
		u32 *last_hop = (u32 *)(((u8 *) skb->data) +
				sizeof(struct ipxhdr) +
				IPX_SKB_CB(skb)->last_hop.index *
				sizeof(u32));
		*last_hop = IPX_SKB_CB(skb)->last_hop.netnum;
		IPX_SKB_CB(skb)->last_hop.index = -1;
	}
Linus Torvalds's avatar
Linus Torvalds committed
594 595 596 597 598 599
	
	/* 
	 * We need to know how many skbuffs it will take to send out this
	 * packet to avoid unnecessary copies.
	 */
	 
Linus Torvalds's avatar
Linus Torvalds committed
600
	if (!dl || !dev || dev->flags & IFF_LOOPBACK) 
Linus Torvalds's avatar
Linus Torvalds committed
601 602 603 604 605 606 607 608 609
		send_to_wire = 0;	/* No non looped */

	/*
	 * See if this should be demuxed to sockets on this interface 
	 *
	 * We want to ensure the original was eaten or that we only use
	 * up clones.
	 */
	 
Linus Torvalds's avatar
Linus Torvalds committed
610
	if (ipx->ipx_dest.net == intrfc->if_netnum) {
Linus Torvalds's avatar
Linus Torvalds committed
611 612 613 614
		/*
		 * To our own node, loop and free the original.
		 * The internal net will receive on all node address.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
615 616
		if (intrfc == ipx_internal_net ||
		    !memcmp(intrfc->if_node, node, IPX_NODE_LEN)) {
Linus Torvalds's avatar
Linus Torvalds committed
617 618 619 620
			/* Don't charge sender */
			skb_orphan(skb);

			/* Will charge receiver */
Linus Torvalds's avatar
Linus Torvalds committed
621
			return ipxitf_demux_socket(intrfc, skb, 0);
Linus Torvalds's avatar
Linus Torvalds committed
622 623 624
		}

		/* Broadcast, loop and possibly keep to send on. */
Linus Torvalds's avatar
Linus Torvalds committed
625 626
		if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN)) {
			if (!send_to_wire)
Linus Torvalds's avatar
Linus Torvalds committed
627 628
				skb_orphan(skb);
			ipxitf_demux_socket(intrfc, skb, send_to_wire);
Linus Torvalds's avatar
Linus Torvalds committed
629
			if (!send_to_wire)
Linus Torvalds's avatar
Linus Torvalds committed
630
				goto out;
Linus Torvalds's avatar
Linus Torvalds committed
631 632 633 634 635 636 637 638
		}
	}

	/*
	 * If the originating net is not equal to our net; this is routed
	 * We are still charging the sender. Which is right - the driver
	 * free will handle this fairly.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
639
	if (ipx->ipx_source.net != intrfc->if_netnum) {
Linus Torvalds's avatar
Linus Torvalds committed
640 641
		/*
		 * Unshare the buffer before modifying the count in
642
		 * case it's a flood or tcpdump
Linus Torvalds's avatar
Linus Torvalds committed
643 644
		 */
		skb = skb_unshare(skb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
645
		if (!skb)
Linus Torvalds's avatar
Linus Torvalds committed
646
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
647
		if (++ipx->ipx_tctrl > ipxcfg_max_hops)
Linus Torvalds's avatar
Linus Torvalds committed
648 649 650
			send_to_wire = 0;
	}

Linus Torvalds's avatar
Linus Torvalds committed
651
	if (!send_to_wire) {
Linus Torvalds's avatar
Linus Torvalds committed
652
		kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
653
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
654 655 656 657
	}

	/* Determine the appropriate hardware address */
	addr_len = dev->addr_len;
Linus Torvalds's avatar
Linus Torvalds committed
658
	if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN))
Linus Torvalds's avatar
Linus Torvalds committed
659 660 661 662 663 664
		memcpy(dest_node, dev->broadcast, addr_len);
	else
		memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);

	/* Make any compensation for differing physical/data link size */
	skb = ipxitf_adjust_skbuff(intrfc, skb);
Linus Torvalds's avatar
Linus Torvalds committed
665
	if (!skb)
Linus Torvalds's avatar
Linus Torvalds committed
666
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
667

Linus Torvalds's avatar
Linus Torvalds committed
668
	/* set up data link and physical headers */
Linus Torvalds's avatar
Linus Torvalds committed
669
	skb->dev	= dev;
670
	skb->protocol	= htons(ETH_P_IPX);
Linus Torvalds's avatar
Linus Torvalds committed
671 672

	/* Send it out */
673
	dl->request(dl, skb, dest_node);
674 675
out:
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
676 677
}

678
static int ipxitf_add_local_route(struct ipx_interface *intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
679
{
Linus Torvalds's avatar
Linus Torvalds committed
680
	return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
681 682
}

683 684 685
static void ipxitf_discover_netnum(struct ipx_interface *intrfc,
				   struct sk_buff *skb);
static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb);
Linus Torvalds's avatar
Linus Torvalds committed
686

687
static int ipxitf_rcv(struct ipx_interface *intrfc, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
688
{
689
	struct ipxhdr *ipx = ipx_hdr(skb);
690
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
691 692 693 694

	ipxitf_hold(intrfc);

	/* See if we should update our network number */
Linus Torvalds's avatar
Linus Torvalds committed
695 696
	if (!intrfc->if_netnum) /* net number of intrfc not known yet */
 		ipxitf_discover_netnum(intrfc, skb);
Linus Torvalds's avatar
Linus Torvalds committed
697
	
Linus Torvalds's avatar
Linus Torvalds committed
698 699
	IPX_SKB_CB(skb)->last_hop.index = -1;
	if (ipx->ipx_type == IPX_TYPE_PPROP) {
700 701
		rc = ipxitf_pprop(intrfc, skb);
		if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
702
			goto out_free_skb;
Linus Torvalds's avatar
Linus Torvalds committed
703 704
	}

Linus Torvalds's avatar
Linus Torvalds committed
705 706 707 708 709
	/* local processing follows */
	if (!IPX_SKB_CB(skb)->ipx_dest_net)
		IPX_SKB_CB(skb)->ipx_dest_net = intrfc->if_netnum;
	if (!IPX_SKB_CB(skb)->ipx_source_net)
		IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
Linus Torvalds's avatar
Linus Torvalds committed
710

Linus Torvalds's avatar
Linus Torvalds committed
711 712 713 714
	/* it doesn't make sense to route a pprop packet, there's no meaning
	 * in the ipx_dest_net for such packets */
	if (ipx->ipx_type != IPX_TYPE_PPROP &&
	    intrfc->if_netnum != IPX_SKB_CB(skb)->ipx_dest_net) {
Linus Torvalds's avatar
Linus Torvalds committed
715
		/* We only route point-to-point packets. */
Linus Torvalds's avatar
Linus Torvalds committed
716
		if (skb->pkt_type == PACKET_HOST) {
Linus Torvalds's avatar
Linus Torvalds committed
717
			skb = skb_unshare(skb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
718
			if (skb)
719
				rc = ipxrtr_route_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
720 721 722 723 724 725 726
			goto out_intrfc;
		}

		goto out_free_skb;
	}

	/* see if we should keep it */
Linus Torvalds's avatar
Linus Torvalds committed
727 728
	if (!memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) ||
	    !memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN)) {
729
		rc = ipxitf_demux_socket(intrfc, skb, 0);
Linus Torvalds's avatar
Linus Torvalds committed
730 731 732 733 734 735 736 737
		goto out_intrfc;
	}

	/* we couldn't pawn it off so unload it */
out_free_skb:
	kfree_skb(skb);
out_intrfc:
	ipxitf_put(intrfc);
738
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
739 740
}

741 742
static void ipxitf_discover_netnum(struct ipx_interface *intrfc,
				   struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
743 744 745 746 747
{ 
	const struct ipx_cb *cb = IPX_SKB_CB(skb);

	/* see if this is an intra packet: source_net == dest_net */
	if (cb->ipx_source_net == cb->ipx_dest_net && cb->ipx_source_net) {
748 749
		struct ipx_interface *i =
				ipxitf_find_using_net(cb->ipx_source_net);
Linus Torvalds's avatar
Linus Torvalds committed
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
		/* NB: NetWare servers lie about their hop count so we
		 * dropped the test based on it. This is the best way
		 * to determine this is a 0 hop count packet. */
		if (!i) {
			intrfc->if_netnum = cb->ipx_source_net;
			ipxitf_add_local_route(intrfc);
		} else {
			printk(KERN_WARNING "IPX: Network number collision "
				"%lx\n        %s %s and %s %s\n",
				(unsigned long) htonl(cb->ipx_source_net),
				ipx_device_name(i),
				ipx_frame_name(i->if_dlink_type),
				ipx_device_name(intrfc),
				ipx_frame_name(intrfc->if_dlink_type));
			ipxitf_put(i);
		}
	}
}

/**
 * ipxitf_pprop - Process packet propagation IPX packet type 0x14, used for
 * 		  NetBIOS broadcasts
 * @intrfc: IPX interface receiving this packet
 * @skb: Received packet
 *
 * Checks if packet is valid: if its more than %IPX_MAX_PPROP_HOPS hops or if it
 * is smaller than a IPX header + the room for %IPX_MAX_PPROP_HOPS hops we drop
 * it, not even processing it locally, if it has exact %IPX_MAX_PPROP_HOPS we
 * don't broadcast it, but process it locally. See chapter 5 of Novell's "IPX
 * RIP and SAP Router Specification", Part Number 107-000029-001.
 * 
 * If it is valid, check if we have pprop broadcasting enabled by the user,
 * if not, just return zero for local processing.
 *
 * If it is enabled check the packet and don't broadcast it if we have already
 * seen this packet.
 *
 * Broadcast: send it to the interfaces that aren't on the packet visited nets
 * array, just after the IPX header.
 *
 * Returns -EINVAL for invalid packets, so that the calling function drops
 * the packet without local processing. 0 if packet is to be locally processed.
 */
793
static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
794
{
795
	struct ipxhdr *ipx = ipx_hdr(skb);
796
	int i, rc = -EINVAL;
797
	struct ipx_interface *ifcs;
Linus Torvalds's avatar
Linus Torvalds committed
798 799 800 801 802 803 804 805 806 807 808 809
	char *c;
	u32 *l;

	/* Illegal packet - too many hops or too short */
	/* We decide to throw it away: no broadcasting, no local processing.
	 * NetBIOS unaware implementations route them as normal packets -
	 * tctrl <= 15, any data payload... */
	if (IPX_SKB_CB(skb)->ipx_tctrl > IPX_MAX_PPROP_HOPS ||
	    ntohs(ipx->ipx_pktsize) < sizeof(struct ipxhdr) +
	    				IPX_MAX_PPROP_HOPS * sizeof(u32))
		goto out;
	/* are we broadcasting this damn thing? */
810
	rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
	if (!sysctl_ipx_pprop_broadcasting)
		goto out;
	/* We do broadcast packet on the IPX_MAX_PPROP_HOPS hop, but we
	 * process it locally. All previous hops broadcasted it, and process it
	 * locally. */
	if (IPX_SKB_CB(skb)->ipx_tctrl == IPX_MAX_PPROP_HOPS)
		goto out;
	
	c = ((u8 *) ipx) + sizeof(struct ipxhdr);
	l = (u32 *) c;

	/* Don't broadcast packet if already seen this net */
	for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
		if (*l++ == intrfc->if_netnum)
			goto out;

	/* < IPX_MAX_PPROP_HOPS hops && input interface not in list. Save the
	 * position where we will insert recvd netnum into list, later on,
	 * in ipxitf_send */
	IPX_SKB_CB(skb)->last_hop.index = i;
	IPX_SKB_CB(skb)->last_hop.netnum = intrfc->if_netnum;
	/* xmit on all other interfaces... */
	spin_lock_bh(&ipx_interfaces_lock);
834
	list_for_each_entry(ifcs, &ipx_interfaces, node) {
Linus Torvalds's avatar
Linus Torvalds committed
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
		/* Except unconfigured interfaces */
		if (!ifcs->if_netnum)
			continue;
					
		/* That aren't in the list */
		if (ifcs == intrfc)
			continue;
		l = (__u32 *) c;
		/* don't consider the last entry in the packet list,
		 * it is our netnum, and it is not there yet */
		for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
			if (ifcs->if_netnum == *l++)
				break;
		if (i == IPX_SKB_CB(skb)->ipx_tctrl) {
			struct sk_buff *s = skb_copy(skb, GFP_ATOMIC);

			if (s) {
				IPX_SKB_CB(s)->ipx_dest_net = ifcs->if_netnum;
				ipxrtr_route_skb(s);
			}
		}
	}
	spin_unlock_bh(&ipx_interfaces_lock);
858
out:
859
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
860 861
}

862
static void ipxitf_insert(struct ipx_interface *intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
863 864
{
	spin_lock_bh(&ipx_interfaces_lock);
865
	list_add_tail(&intrfc->node, &ipx_interfaces);
Linus Torvalds's avatar
Linus Torvalds committed
866 867
	spin_unlock_bh(&ipx_interfaces_lock);

Linus Torvalds's avatar
Linus Torvalds committed
868
	if (ipxcfg_auto_select_primary && !ipx_primary_net)
Linus Torvalds's avatar
Linus Torvalds committed
869 870 871
		ipx_primary_net = intrfc;
}

872 873 874 875 876
static struct ipx_interface *ipxitf_alloc(struct net_device *dev, __u32 netnum,
					  unsigned short dlink_type,
					  struct datalink_proto *dlink,
					  unsigned char internal,
					  int ipx_offset)
Linus Torvalds's avatar
Linus Torvalds committed
877
{
878
	struct ipx_interface *intrfc = kmalloc(sizeof(*intrfc), GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
879 880 881 882 883 884 885 886 887

	if (intrfc) {
		intrfc->if_dev		= dev;
		intrfc->if_netnum	= netnum;
		intrfc->if_dlink_type 	= dlink_type;
		intrfc->if_dlink 	= dlink;
		intrfc->if_internal 	= internal;
		intrfc->if_ipx_offset 	= ipx_offset;
		intrfc->if_sknum 	= IPX_MIN_EPHEMERAL_SOCKET;
888
		INIT_HLIST_HEAD(&intrfc->if_sklist);
Linus Torvalds's avatar
Linus Torvalds committed
889 890
		atomic_set(&intrfc->refcnt, 1);
		spin_lock_init(&intrfc->if_sklist_lock);
891
		__module_get(THIS_MODULE);
Linus Torvalds's avatar
Linus Torvalds committed
892 893 894 895 896
	}

	return intrfc;
}

897
static int ipxitf_create_internal(struct ipx_interface_definition *idef)
Linus Torvalds's avatar
Linus Torvalds committed
898
{
899
	struct ipx_interface *intrfc;
900
	int rc = -EEXIST;
Linus Torvalds's avatar
Linus Torvalds committed
901 902

	/* Only one primary network allowed */
Linus Torvalds's avatar
Linus Torvalds committed
903
	if (ipx_primary_net)
Linus Torvalds's avatar
Linus Torvalds committed
904
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
905 906

	/* Must have a valid network number */
907
	rc = -EADDRNOTAVAIL;
Linus Torvalds's avatar
Linus Torvalds committed
908
	if (!idef->ipx_network)
Linus Torvalds's avatar
Linus Torvalds committed
909
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
910
	intrfc = ipxitf_find_using_net(idef->ipx_network);
911
	rc = -EADDRINUSE;
Linus Torvalds's avatar
Linus Torvalds committed
912
	if (intrfc) {
Linus Torvalds's avatar
Linus Torvalds committed
913
		ipxitf_put(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
914
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
915
	}
Linus Torvalds's avatar
Linus Torvalds committed
916
	intrfc = ipxitf_alloc(NULL, idef->ipx_network, 0, NULL, 1, 0);
917
	rc = -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
918
	if (!intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
919
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
920
	memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
921
	ipx_internal_net = ipx_primary_net = intrfc;
Linus Torvalds's avatar
Linus Torvalds committed
922 923 924
	ipxitf_hold(intrfc);
	ipxitf_insert(intrfc);

925
	rc = ipxitf_add_local_route(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
926
	ipxitf_put(intrfc);
927
out:
928
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
929 930 931 932
}

static int ipx_map_frame_type(unsigned char type)
{
933
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
934

Linus Torvalds's avatar
Linus Torvalds committed
935
	switch (type) {
936 937 938 939
	case IPX_FRAME_ETHERII:	rc = htons(ETH_P_IPX);		break;
	case IPX_FRAME_8022:	rc = htons(ETH_P_802_2);	break;
	case IPX_FRAME_SNAP:	rc = htons(ETH_P_SNAP);		break;
	case IPX_FRAME_8023:	rc = htons(ETH_P_802_3);	break;
Linus Torvalds's avatar
Linus Torvalds committed
940 941
	}

942
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
943 944
}

945
static int ipxitf_create(struct ipx_interface_definition *idef)
Linus Torvalds's avatar
Linus Torvalds committed
946 947 948 949
{
	struct net_device *dev;
	unsigned short dlink_type = 0;
	struct datalink_proto *datalink = NULL;
950
	struct ipx_interface *intrfc;
951
	int rc;
Linus Torvalds's avatar
Linus Torvalds committed
952

Linus Torvalds's avatar
Linus Torvalds committed
953
	if (idef->ipx_special == IPX_INTERNAL) {
954
		rc = ipxitf_create_internal(idef);
Linus Torvalds's avatar
Linus Torvalds committed
955 956
		goto out;
	}
Linus Torvalds's avatar
Linus Torvalds committed
957

958
	rc = -EEXIST;
Linus Torvalds's avatar
Linus Torvalds committed
959
	if (idef->ipx_special == IPX_PRIMARY && ipx_primary_net)
Linus Torvalds's avatar
Linus Torvalds committed
960
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
961 962

	intrfc = ipxitf_find_using_net(idef->ipx_network);
963
	rc = -EADDRINUSE;
Linus Torvalds's avatar
Linus Torvalds committed
964
	if (idef->ipx_network && intrfc) {
Linus Torvalds's avatar
Linus Torvalds committed
965
		ipxitf_put(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
966
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
967 968 969 970 971 972
	}

	if (intrfc)
		ipxitf_put(intrfc);

	dev = dev_get_by_name(idef->ipx_device);
973
	rc = -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
974
	if (!dev)
Linus Torvalds's avatar
Linus Torvalds committed
975
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
976

Linus Torvalds's avatar
Linus Torvalds committed
977
	switch (idef->ipx_dlink_type) {
978 979 980 981 982 983 984 985 986 987 988 989
	case IPX_FRAME_TR_8022:
		printk(KERN_WARNING "IPX frame type 802.2TR is "
			"obsolete Use 802.2 instead.\n");
		/* fall through */
	case IPX_FRAME_8022:
		dlink_type 	= htons(ETH_P_802_2);
		datalink 	= p8022_datalink;
		break;
	case IPX_FRAME_ETHERII:
		if (dev->type != ARPHRD_IEEE802) {
			dlink_type 	= htons(ETH_P_IPX);
			datalink 	= pEII_datalink;
Linus Torvalds's avatar
Linus Torvalds committed
990
			break;
991 992 993
		} else 
			printk(KERN_WARNING "IPX frame type EtherII over "
					"token-ring is obsolete. Use SNAP "
Linus Torvalds's avatar
Linus Torvalds committed
994
					"instead.\n");
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
		/* fall through */
	case IPX_FRAME_SNAP:
		dlink_type 	= htons(ETH_P_SNAP);
		datalink 	= pSNAP_datalink;
		break;
	case IPX_FRAME_8023:
		dlink_type 	= htons(ETH_P_802_3);
		datalink 	= p8023_datalink;
		break;
	case IPX_FRAME_NONE:
	default:
		rc = -EPROTONOSUPPORT;
		goto out_dev;
Linus Torvalds's avatar
Linus Torvalds committed
1008 1009
	}

1010
	rc = -ENETDOWN;
Linus Torvalds's avatar
Linus Torvalds committed
1011
	if (!(dev->flags & IFF_UP))
Linus Torvalds's avatar
Linus Torvalds committed
1012 1013 1014
		goto out_dev;

	/* Check addresses are suitable */
1015
	rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1016
	if (dev->addr_len > IPX_NODE_LEN)
Linus Torvalds's avatar
Linus Torvalds committed
1017 1018
		goto out_dev;

Linus Torvalds's avatar
Linus Torvalds committed
1019 1020
	intrfc = ipxitf_find_using_phys(dev, dlink_type);
	if (!intrfc) {
Linus Torvalds's avatar
Linus Torvalds committed
1021
		/* Ok now create */
Linus Torvalds's avatar
Linus Torvalds committed
1022 1023 1024
		intrfc = ipxitf_alloc(dev, idef->ipx_network, dlink_type,
				      datalink, 0, dev->hard_header_len +
					datalink->header_length);
1025
		rc = -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
1026
		if (!intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1027 1028
			goto out_dev;
		/* Setup primary if necessary */
Linus Torvalds's avatar
Linus Torvalds committed
1029
		if (idef->ipx_special == IPX_PRIMARY)
Linus Torvalds's avatar
Linus Torvalds committed
1030
			ipx_primary_net = intrfc;
Linus Torvalds's avatar
Linus Torvalds committed
1031 1032
		if (!memcmp(idef->ipx_node, "\000\000\000\000\000\000",
			    IPX_NODE_LEN)) {
Linus Torvalds's avatar
Linus Torvalds committed
1033
			memset(intrfc->if_node, 0, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1034
			memcpy(intrfc->if_node + IPX_NODE_LEN - dev->addr_len,
Linus Torvalds's avatar
Linus Torvalds committed
1035
				dev->dev_addr, dev->addr_len);
Linus Torvalds's avatar
Linus Torvalds committed
1036
		} else
Linus Torvalds's avatar
Linus Torvalds committed
1037 1038 1039 1040 1041 1042 1043
			memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN);
		ipxitf_hold(intrfc);
		ipxitf_insert(intrfc);
	}


	/* If the network number is known, add a route */
1044
	rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1045
	if (!intrfc->if_netnum)
Linus Torvalds's avatar
Linus Torvalds committed
1046 1047
		goto out_intrfc;

1048
	rc = ipxitf_add_local_route(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
1049 1050
out_intrfc:
	ipxitf_put(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
1051
	goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1052 1053
out_dev:
	dev_put(dev);
1054
out:
1055
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1056 1057
}

1058
static int ipxitf_delete(struct ipx_interface_definition *idef)
Linus Torvalds's avatar
Linus Torvalds committed
1059 1060 1061
{
	struct net_device *dev = NULL;
	unsigned short dlink_type = 0;
1062
	struct ipx_interface *intrfc;
1063
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1064 1065

	spin_lock_bh(&ipx_interfaces_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1066 1067
	if (idef->ipx_special == IPX_INTERNAL) {
		if (ipx_internal_net) {
Linus Torvalds's avatar
Linus Torvalds committed
1068 1069 1070
			__ipxitf_put(ipx_internal_net);
			goto out;
		}
1071
		rc = -ENOENT;
Linus Torvalds's avatar
Linus Torvalds committed
1072 1073 1074 1075
		goto out;
	}

	dlink_type = ipx_map_frame_type(idef->ipx_dlink_type);
1076
	rc = -EPROTONOSUPPORT;
Linus Torvalds's avatar
Linus Torvalds committed
1077
	if (!dlink_type)
Linus Torvalds's avatar
Linus Torvalds committed
1078 1079 1080
		goto out;

	dev = __dev_get_by_name(idef->ipx_device);
1081
	rc = -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
1082
	if (!dev)
Linus Torvalds's avatar
Linus Torvalds committed
1083 1084 1085
		goto out;

	intrfc = __ipxitf_find_using_phys(dev, dlink_type);
1086
	rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1087 1088 1089
	if (!intrfc)
		goto out;
	__ipxitf_put(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
1090

1091
	rc = 0;
1092 1093
out:
	spin_unlock_bh(&ipx_interfaces_lock);
1094
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1095 1096
}

1097 1098
static struct ipx_interface *ipxitf_auto_create(struct net_device *dev,
						unsigned short dlink_type)
Linus Torvalds's avatar
Linus Torvalds committed
1099
{
1100
	struct ipx_interface *intrfc = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1101 1102 1103 1104 1105 1106 1107 1108
	struct datalink_proto *datalink;

	if (!dev)
		goto out;

	/* Check addresses are suitable */
	if (dev->addr_len > IPX_NODE_LEN)
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1109

Linus Torvalds's avatar
Linus Torvalds committed
1110
	switch (htons(dlink_type)) {
1111 1112 1113 1114 1115
	case ETH_P_IPX:		datalink = pEII_datalink;	break;
	case ETH_P_802_2:	datalink = p8022_datalink;	break;
	case ETH_P_SNAP:	datalink = pSNAP_datalink;	break;
	case ETH_P_802_3:	datalink = p8023_datalink;	break;
	default:		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1116 1117
	}

Linus Torvalds's avatar
Linus Torvalds committed
1118 1119
	intrfc = ipxitf_alloc(dev, 0, dlink_type, datalink, 0,
				dev->hard_header_len + datalink->header_length);
Linus Torvalds's avatar
Linus Torvalds committed
1120

Linus Torvalds's avatar
Linus Torvalds committed
1121
	if (intrfc) {
Linus Torvalds's avatar
Linus Torvalds committed
1122 1123 1124 1125 1126 1127
		memset(intrfc->if_node, 0, IPX_NODE_LEN);
		memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
			dev->dev_addr, dev->addr_len);
		spin_lock_init(&intrfc->if_sklist_lock);
		atomic_set(&intrfc->refcnt, 1);
		ipxitf_insert(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
1128
		dev_hold(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1129 1130
	}

1131 1132
out:
	return intrfc;
Linus Torvalds's avatar
Linus Torvalds committed
1133 1134 1135 1136
}

static int ipxitf_ioctl(unsigned int cmd, void *arg)
{
1137
	int rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1138
	struct ifreq ifr;
Linus Torvalds's avatar
Linus Torvalds committed
1139
	int val;
Linus Torvalds's avatar
Linus Torvalds committed
1140

Linus Torvalds's avatar
Linus Torvalds committed
1141
	switch (cmd) {
1142 1143 1144
	case SIOCSIFADDR: {
		struct sockaddr_ipx *sipx;
		struct ipx_interface_definition f;
Linus Torvalds's avatar
Linus Torvalds committed
1145

1146 1147 1148 1149 1150 1151
		rc = -EFAULT;
		if (copy_from_user(&ifr, arg, sizeof(ifr)))
			break;
		sipx = (struct sockaddr_ipx *)&ifr.ifr_addr;
		rc = -EINVAL;
		if (sipx->sipx_family != AF_IPX)
Linus Torvalds's avatar
Linus Torvalds committed
1152
			break;
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
		f.ipx_network = sipx->sipx_network;
		memcpy(f.ipx_device, ifr.ifr_name,
			sizeof(f.ipx_device));
		memcpy(f.ipx_node, sipx->sipx_node, IPX_NODE_LEN);
		f.ipx_dlink_type = sipx->sipx_type;
		f.ipx_special = sipx->sipx_special;

		if (sipx->sipx_action == IPX_DLTITF)
			rc = ipxitf_delete(&f);
		else
			rc = ipxitf_create(&f);
		break;
	}
	case SIOCGIFADDR: {
		struct sockaddr_ipx *sipx;
		struct ipx_interface *ipxif;
		struct net_device *dev;
Linus Torvalds's avatar
Linus Torvalds committed
1170

1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
		rc = -EFAULT;
		if (copy_from_user(&ifr, arg, sizeof(ifr)))
			break;
		sipx = (struct sockaddr_ipx *)&ifr.ifr_addr;
		dev  = __dev_get_by_name(ifr.ifr_name);
		rc   = -ENODEV;
		if (!dev)
			break;
		ipxif = ipxitf_find_using_phys(dev,
					   ipx_map_frame_type(sipx->sipx_type));
		rc = -EADDRNOTAVAIL;
		if (!ipxif)
Linus Torvalds's avatar
Linus Torvalds committed
1183
			break;
Linus Torvalds's avatar
Linus Torvalds committed
1184

1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
		sipx->sipx_family	= AF_IPX;
		sipx->sipx_network	= ipxif->if_netnum;
		memcpy(sipx->sipx_node, ipxif->if_node,
			sizeof(sipx->sipx_node));
		rc = -EFAULT;
		if (copy_to_user(arg, &ifr, sizeof(ifr)))
			break;
		ipxitf_put(ipxif);
		rc = 0;
		break;
	}
	case SIOCAIPXITFCRT: 
		rc = -EFAULT;
		if (get_user(val, (unsigned char *) arg))
			break;
		rc = 0;
		ipxcfg_auto_create_interfaces = val;
		break;
	case SIOCAIPXPRISLT: 
		rc = -EFAULT;
		if (get_user(val, (unsigned char *) arg))
			break;
		rc = 0;
		ipxcfg_set_auto_select(val);
		break;
Linus Torvalds's avatar
Linus Torvalds committed
1210
	}
Linus Torvalds's avatar
Linus Torvalds committed
1211

1212
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1213 1214 1215 1216 1217 1218 1219
}

/*
 *	Checksum routine for IPX
 */
 
/* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
Linus Torvalds's avatar
Linus Torvalds committed
1220
/* This functions should *not* mess with packet contents */
Linus Torvalds's avatar
Linus Torvalds committed
1221

1222
__u16 ipx_cksum(struct ipxhdr *packet, int length) 
Linus Torvalds's avatar
Linus Torvalds committed
1223 1224 1225 1226 1227 1228
{
	/* 
	 *	NOTE: sum is a net byte order quantity, which optimizes the 
	 *	loop. This only works on big and little endian machines. (I
	 *	don't know of a machine that isn't.)
	 */
Linus Torvalds's avatar
Linus Torvalds committed
1229 1230 1231 1232 1233 1234 1235 1236 1237
	/* start at ipx_dest - We skip the checksum field and start with
	 * ipx_type before the loop, not considering ipx_tctrl in the calc */
	__u16 *p = (__u16 *)&packet->ipx_dest;
	__u32 i = (length >> 1) - 1; /* Number of complete words */
	__u32 sum = packet->ipx_type << sizeof(packet->ipx_tctrl); 

	/* Loop through all complete words except the checksum field,
	 * ipx_type (accounted above) and ipx_tctrl (not used in the cksum) */
	while (--i)
Linus Torvalds's avatar
Linus Torvalds committed
1238 1239 1240
		sum += *p++;

	/* Add on the last part word if it exists */
1241
	if (packet->ipx_pktsize & htons(1))
Linus Torvalds's avatar
Linus Torvalds committed
1242 1243 1244 1245 1246 1247
		sum += ntohs(0xff00) & *p;

	/* Do final fixup */
	sum = (sum & 0xffff) + (sum >> 16);

	/* It's a pity there's no concept of carry in C */
Linus Torvalds's avatar
Linus Torvalds committed
1248
	if (sum >= 0x10000)
Linus Torvalds's avatar
Linus Torvalds committed
1249 1250
		sum++;

Linus Torvalds's avatar
Linus Torvalds committed
1251
	return ~sum;
Linus Torvalds's avatar
Linus Torvalds committed
1252 1253
}

1254
const char *ipx_frame_name(unsigned short frame)
Linus Torvalds's avatar
Linus Torvalds committed
1255
{
1256
	char* rc = "None";
Linus Torvalds's avatar
Linus Torvalds committed
1257

Linus Torvalds's avatar
Linus Torvalds committed
1258
	switch (ntohs(frame)) {
1259 1260 1261 1262 1263
	case ETH_P_IPX:		rc = "EtherII";	break;
	case ETH_P_802_2:	rc = "802.2";	break;
	case ETH_P_SNAP:	rc = "SNAP";	break;
	case ETH_P_802_3:	rc = "802.3";	break;
	case ETH_P_TR_802_2:	rc = "802.2TR";	break;
Linus Torvalds's avatar
Linus Torvalds committed
1264
	}
Linus Torvalds's avatar
Linus Torvalds committed
1265

1266
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1267 1268
}

1269
const char *ipx_device_name(struct ipx_interface *intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1270
{
Linus Torvalds's avatar
Linus Torvalds committed
1271 1272
	return intrfc->if_internal ? "Internal" :
		intrfc->if_dev ? intrfc->if_dev->name : "Unknown";
Linus Torvalds's avatar
Linus Torvalds committed
1273 1274
}

Linus Torvalds's avatar
Linus Torvalds committed
1275 1276
/* Handling for system calls applied via the various interfaces to an IPX
 * socket object. */
Linus Torvalds's avatar
Linus Torvalds committed
1277

Linus Torvalds's avatar
Linus Torvalds committed
1278 1279
static int ipx_setsockopt(struct socket *sock, int level, int optname,
			  char *optval, int optlen)
Linus Torvalds's avatar
Linus Torvalds committed
1280
{
Linus Torvalds's avatar
Linus Torvalds committed
1281 1282
	struct sock *sk = sock->sk;
	int opt;
1283
	int rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1284

Linus Torvalds's avatar
Linus Torvalds committed
1285
	if (optlen != sizeof(int))
Linus Torvalds's avatar
Linus Torvalds committed
1286
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1287

1288
	rc = -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
1289
	if (get_user(opt, (unsigned int *)optval))
Linus Torvalds's avatar
Linus Torvalds committed
1290
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1291

1292
	rc = -ENOPROTOOPT;
Linus Torvalds's avatar
Linus Torvalds committed
1293 1294
	if (!(level == SOL_IPX && optname == IPX_TYPE))
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1295

1296
	ipx_sk(sk)->type = opt;
1297
	rc = 0;
1298
out:
1299
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1300 1301 1302 1303 1304
}

static int ipx_getsockopt(struct socket *sock, int level, int optname,
	char *optval, int *optlen)
{
Linus Torvalds's avatar
Linus Torvalds committed
1305 1306
	struct sock *sk = sock->sk;
	int val = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1307
	int len;
1308
	int rc = -ENOPROTOOPT;
Linus Torvalds's avatar
Linus Torvalds committed
1309

Linus Torvalds's avatar
Linus Torvalds committed
1310 1311
	if (!(level == SOL_IPX && optname == IPX_TYPE))
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1312

1313
	val = ipx_sk(sk)->type;
Linus Torvalds's avatar
Linus Torvalds committed
1314

1315
	rc = -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
1316
	if (get_user(len, optlen))
Linus Torvalds's avatar
Linus Torvalds committed
1317
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1318

Linus Torvalds's avatar
Linus Torvalds committed
1319
	len = min_t(unsigned int, len, sizeof(int));
1320
	rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1321
	if(len < 0)
Linus Torvalds's avatar
Linus Torvalds committed
1322
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1323
		
1324
	rc = -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
1325 1326
	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1327

1328
	rc = 0;
1329
out:
1330
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1331 1332 1333 1334
}

static int ipx_create(struct socket *sock, int protocol)
{
1335
	int rc = -ESOCKTNOSUPPORT;
1336
	struct ipx_opt *ipx = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1337 1338
	struct sock *sk;

Linus Torvalds's avatar
Linus Torvalds committed
1339
	switch (sock->type) {
1340 1341 1342 1343
	case SOCK_DGRAM:
		sk = sk_alloc(PF_IPX, GFP_KERNEL, 1, NULL);
        	rc = -ENOMEM;
		if (!sk)
1344
			goto out;
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
		ipx = ipx_sk(sk) = kmalloc(sizeof(*ipx), GFP_KERNEL);
		if (!ipx)
			goto outsk;
		memset(ipx, 0, sizeof(*ipx));
                sock->ops = &ipx_dgram_ops;
                break;
	case SOCK_SEQPACKET:
		/*
		 * SPX support is not anymore in the kernel sources. If
		 * you want to ressurrect it, completing it and making
		 * it understand shared skbs, be fully multithreaded,
		 * etc, grab the sources in an early 2.5 kernel tree.
		 */
	case SOCK_STREAM:       /* Allow higher levels to piggyback */
	default:
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1361 1362 1363 1364 1365 1366 1367
	}
#ifdef IPX_REFCNT_DEBUG
        atomic_inc(&ipx_sock_nr);
        printk(KERN_DEBUG "IPX socket %p created, now we have %d alive\n", sk,
			atomic_read(&ipx_sock_nr));
#endif
	sock_init_data(sock, sk);
1368
	sk_set_owner(sk, THIS_MODULE);
1369
	sk->sk_no_check = 1;		/* Checksum off by default */
1370
	rc = 0;
1371
out:
1372
	return rc;
1373 1374
outsk:
	sk_free(sk);
1375
	goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1376 1377 1378 1379 1380 1381
}

static int ipx_release(struct socket *sock)
{
	struct sock *sk = sock->sk;

Linus Torvalds's avatar
Linus Torvalds committed
1382
	if (!sk)
Linus Torvalds's avatar
Linus Torvalds committed
1383
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1384

1385
	if (!sock_flag(sk, SOCK_DEAD))
1386
		sk->sk_state_change(sk);
Linus Torvalds's avatar
Linus Torvalds committed
1387

1388
	sock_set_flag(sk, SOCK_DEAD);
Linus Torvalds's avatar
Linus Torvalds committed
1389 1390
	sock->sk = NULL;
	ipx_destroy_socket(sk);
1391 1392
out:
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1393 1394
}

Linus Torvalds's avatar
Linus Torvalds committed
1395
/* caller must hold a reference to intrfc */
Linus Torvalds's avatar
Linus Torvalds committed
1396

1397
static unsigned short ipx_first_free_socketnum(struct ipx_interface *intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1398 1399 1400 1401 1402
{
	unsigned short socketNum = intrfc->if_sknum;

	spin_lock_bh(&intrfc->if_sklist_lock);

Linus Torvalds's avatar
Linus Torvalds committed
1403
	if (socketNum < IPX_MIN_EPHEMERAL_SOCKET)
Linus Torvalds's avatar
Linus Torvalds committed
1404 1405
		socketNum = IPX_MIN_EPHEMERAL_SOCKET;

Linus Torvalds's avatar
Linus Torvalds committed
1406 1407
	while (__ipxitf_find_socket(intrfc, ntohs(socketNum)))
		if (socketNum > IPX_MAX_EPHEMERAL_SOCKET)
Linus Torvalds's avatar
Linus Torvalds committed
1408 1409 1410 1411 1412 1413 1414
			socketNum = IPX_MIN_EPHEMERAL_SOCKET;
		else
			socketNum++;

	spin_unlock_bh(&intrfc->if_sklist_lock);
	intrfc->if_sknum = socketNum;

Linus Torvalds's avatar
Linus Torvalds committed
1415
	return ntohs(socketNum);
Linus Torvalds's avatar
Linus Torvalds committed
1416 1417 1418 1419
}

static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
Linus Torvalds's avatar
Linus Torvalds committed
1420
	struct sock *sk = sock->sk;
1421
	struct ipx_opt *ipxs = ipx_sk(sk);
1422
	struct ipx_interface *intrfc;
Linus Torvalds's avatar
Linus Torvalds committed
1423
	struct sockaddr_ipx *addr = (struct sockaddr_ipx *)uaddr;
1424
	int rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1425

1426
	if (!sk->sk_zapped || addr_len != sizeof(struct sockaddr_ipx))
Linus Torvalds's avatar
Linus Torvalds committed
1427
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1428 1429

	intrfc = ipxitf_find_using_net(addr->sipx_network);
1430
	rc = -EADDRNOTAVAIL;
Linus Torvalds's avatar
Linus Torvalds committed
1431
	if (!intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1432
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1433

Linus Torvalds's avatar
Linus Torvalds committed
1434
	if (!addr->sipx_port) {
Linus Torvalds's avatar
Linus Torvalds committed
1435
		addr->sipx_port = ipx_first_free_socketnum(intrfc);
1436
		rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1437
		if (!addr->sipx_port)
Linus Torvalds's avatar
Linus Torvalds committed
1438
			goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
1439 1440 1441
	}

	/* protect IPX system stuff like routing/sap */
1442
	rc = -EACCES;
Linus Torvalds's avatar
Linus Torvalds committed
1443 1444
	if (ntohs(addr->sipx_port) < IPX_MIN_EPHEMERAL_SOCKET &&
	    !capable(CAP_NET_ADMIN))
Linus Torvalds's avatar
Linus Torvalds committed
1445
		goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
1446

1447
	ipxs->port = addr->sipx_port;
Linus Torvalds's avatar
Linus Torvalds committed
1448 1449

#ifdef CONFIG_IPX_INTERN
Linus Torvalds's avatar
Linus Torvalds committed
1450
	if (intrfc == ipx_internal_net) {
Linus Torvalds's avatar
Linus Torvalds committed
1451 1452 1453 1454 1455
		/* The source address is to be set explicitly if the
		 * socket is to be bound on the internal network. If a
		 * node number 0 was specified, the default is used.
		 */

1456
		rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1457 1458
		if (!memcmp(addr->sipx_node, ipx_broadcast_node, IPX_NODE_LEN))
			goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
1459
		if (!memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN))
1460
			memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1461
		else
1462
			memcpy(ipxs->node, addr->sipx_node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1463

1464
		rc = -EADDRINUSE;
1465 1466
		if (ipxitf_find_internal_socket(intrfc, ipxs->node,
						ipxs->port)) {
Linus Torvalds's avatar
Linus Torvalds committed
1467 1468 1469
			SOCK_DEBUG(sk,
				"IPX: bind failed because port %X in use.\n",
				ntohs((int)addr->sipx_port));
Linus Torvalds's avatar
Linus Torvalds committed
1470
			goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
1471
		}
Linus Torvalds's avatar
Linus Torvalds committed
1472
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
1473 1474 1475 1476 1477
		/* Source addresses are easy. It must be our
		 * network:node pair for an interface routed to IPX
		 * with the ipx routing ioctl()
		 */

1478
		memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1479

1480
		rc = -EADDRINUSE;
Linus Torvalds's avatar
Linus Torvalds committed
1481
		if (ipxitf_find_socket(intrfc, addr->sipx_port)) {
Linus Torvalds's avatar
Linus Torvalds committed
1482 1483 1484
			SOCK_DEBUG(sk,
				"IPX: bind failed because port %X in use.\n",
				ntohs((int)addr->sipx_port));
Linus Torvalds's avatar
Linus Torvalds committed
1485
			goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
1486 1487 1488 1489 1490 1491 1492 1493
		}
	}

#else	/* !def CONFIG_IPX_INTERN */

	/* Source addresses are easy. It must be our network:node pair for
	   an interface routed to IPX with the ipx routing ioctl() */

1494
	rc = -EADDRINUSE;
Linus Torvalds's avatar
Linus Torvalds committed
1495
	if (ipxitf_find_socket(intrfc, addr->sipx_port)) {
Linus Torvalds's avatar
Linus Torvalds committed
1496 1497
		SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n",
				ntohs((int)addr->sipx_port));
Linus Torvalds's avatar
Linus Torvalds committed
1498
		goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
1499 1500 1501 1502 1503
	}

#endif	/* CONFIG_IPX_INTERN */

	ipxitf_insert_socket(intrfc, sk);
1504
	sk->sk_zapped = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1505

1506
	rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1507 1508
out_put:
	ipxitf_put(intrfc);
1509
out:
1510
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1511 1512 1513 1514 1515 1516
}

static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
	int addr_len, int flags)
{
	struct sock *sk = sock->sk;
1517
	struct ipx_opt *ipxs = ipx_sk(sk);
Linus Torvalds's avatar
Linus Torvalds committed
1518
	struct sockaddr_ipx *addr;
1519
	int rc = -EINVAL;
1520
	struct ipx_route *rt;
Linus Torvalds's avatar
Linus Torvalds committed
1521

1522
	sk->sk_state	= TCP_CLOSE;
Linus Torvalds's avatar
Linus Torvalds committed
1523 1524
	sock->state 	= SS_UNCONNECTED;

Linus Torvalds's avatar
Linus Torvalds committed
1525
	if (addr_len != sizeof(*addr))
Linus Torvalds's avatar
Linus Torvalds committed
1526
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1527 1528 1529
	addr = (struct sockaddr_ipx *)uaddr;

	/* put the autobinding in */
1530
	if (!ipxs->port) {
Linus Torvalds's avatar
Linus Torvalds committed
1531 1532 1533 1534 1535 1536
		struct sockaddr_ipx uaddr;

		uaddr.sipx_port		= 0;
		uaddr.sipx_network 	= 0;

#ifdef CONFIG_IPX_INTERN
1537
		rc = -ENETDOWN;
1538
		if (!ipxs->intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1539
			goto out; /* Someone zonked the iface */
1540
		memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
Linus Torvalds's avatar
Linus Torvalds committed
1541
			IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1542 1543
#endif	/* CONFIG_IPX_INTERN */

1544 1545 1546
		rc = ipx_bind(sock, (struct sockaddr *)&uaddr,
			      sizeof(struct sockaddr_ipx));
		if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
1547
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1548 1549
	}

Linus Torvalds's avatar
Linus Torvalds committed
1550 1551
        /* We can either connect to primary network or somewhere
	 * we can route to */
Linus Torvalds's avatar
Linus Torvalds committed
1552
	rt = ipxrtr_lookup(addr->sipx_network);
1553
	rc = -ENETUNREACH;
Linus Torvalds's avatar
Linus Torvalds committed
1554
	if (!rt && !(!addr->sipx_network && ipx_primary_net))
Linus Torvalds's avatar
Linus Torvalds committed
1555
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1556

1557 1558 1559 1560
	ipxs->dest_addr.net  = addr->sipx_network;
	ipxs->dest_addr.sock = addr->sipx_port;
	memcpy(ipxs->dest_addr.node, addr->sipx_node, IPX_NODE_LEN);
	ipxs->type = addr->sipx_type;
Linus Torvalds's avatar
Linus Torvalds committed
1561

Linus Torvalds's avatar
Linus Torvalds committed
1562
	if (sock->type == SOCK_DGRAM) {
Linus Torvalds's avatar
Linus Torvalds committed
1563
		sock->state 	= SS_CONNECTED;
1564
		sk->sk_state 	= TCP_ESTABLISHED;
Linus Torvalds's avatar
Linus Torvalds committed
1565 1566
	}

Linus Torvalds's avatar
Linus Torvalds committed
1567 1568
	if (rt)
		ipxrtr_put(rt);
1569
	rc = 0;
1570
out:
1571
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1572 1573 1574 1575
}


static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
Linus Torvalds's avatar
Linus Torvalds committed
1576
			int *uaddr_len, int peer)
Linus Torvalds's avatar
Linus Torvalds committed
1577
{
1578
	struct ipx_address *addr;
Linus Torvalds's avatar
Linus Torvalds committed
1579
	struct sockaddr_ipx sipx;
Linus Torvalds's avatar
Linus Torvalds committed
1580
	struct sock *sk = sock->sk;
1581
	struct ipx_opt *ipxs = ipx_sk(sk);
1582
	int rc;
Linus Torvalds's avatar
Linus Torvalds committed
1583 1584 1585

	*uaddr_len = sizeof(struct sockaddr_ipx);

Linus Torvalds's avatar
Linus Torvalds committed
1586
	if (peer) {
1587
		rc = -ENOTCONN;
1588
		if (sk->sk_state != TCP_ESTABLISHED)
Linus Torvalds's avatar
Linus Torvalds committed
1589
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1590

1591
		addr = &ipxs->dest_addr;
Linus Torvalds's avatar
Linus Torvalds committed
1592 1593 1594
		sipx.sipx_network	= addr->net;
		sipx.sipx_port		= addr->sock;
		memcpy(sipx.sipx_node, addr->node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1595
	} else {
1596 1597
		if (ipxs->intrfc) {
			sipx.sipx_network = ipxs->intrfc->if_netnum;
Linus Torvalds's avatar
Linus Torvalds committed
1598
#ifdef CONFIG_IPX_INTERN
1599
			memcpy(sipx.sipx_node, ipxs->node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1600
#else
1601
			memcpy(sipx.sipx_node, ipxs->intrfc->if_node,
Linus Torvalds's avatar
Linus Torvalds committed
1602
				IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1603 1604
#endif	/* CONFIG_IPX_INTERN */

Linus Torvalds's avatar
Linus Torvalds committed
1605
		} else {
Linus Torvalds's avatar
Linus Torvalds committed
1606 1607 1608 1609
			sipx.sipx_network = 0;
			memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
		}

1610
		sipx.sipx_port = ipxs->port;
Linus Torvalds's avatar
Linus Torvalds committed
1611 1612 1613
	}

	sipx.sipx_family = AF_IPX;
1614
	sipx.sipx_type	 = ipxs->type;
Linus Torvalds's avatar
Linus Torvalds committed
1615
	memcpy(uaddr, &sipx, sizeof(sipx));
Linus Torvalds's avatar
Linus Torvalds committed
1616

1617
	rc = 0;
1618
out:
1619
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1620 1621 1622 1623 1624
}

int ipx_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
	/* NULL here for pt means the packet was looped back */
1625
	struct ipx_interface *intrfc;
Linus Torvalds's avatar
Linus Torvalds committed
1626
	struct ipxhdr *ipx;
Linus Torvalds's avatar
Linus Torvalds committed
1627
	u16 ipx_pktsize;
1628
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1629 1630 1631 1632
		
	/* Not ours */	
        if (skb->pkt_type == PACKET_OTHERHOST)
        	goto drop;
Linus Torvalds's avatar
Linus Torvalds committed
1633 1634 1635 1636

	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
		goto out;

1637
	ipx		= ipx_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
1638
	ipx_pktsize	= ntohs(ipx->ipx_pktsize);
Linus Torvalds's avatar
Linus Torvalds committed
1639 1640 1641 1642
	
	/* Too small or invalid header? */
	if (ipx_pktsize < sizeof(struct ipxhdr) || ipx_pktsize > skb->len)
		goto drop;
Linus Torvalds's avatar
Linus Torvalds committed
1643
                        
Linus Torvalds's avatar
Linus Torvalds committed
1644 1645 1646 1647
	if (ipx->ipx_checksum != IPX_NO_CHECKSUM &&
	   ipx->ipx_checksum != ipx_cksum(ipx, ipx_pktsize))
		goto drop;

Linus Torvalds's avatar
Linus Torvalds committed
1648 1649
	IPX_SKB_CB(skb)->ipx_tctrl	= ipx->ipx_tctrl;
	IPX_SKB_CB(skb)->ipx_dest_net	= ipx->ipx_dest.net;
Linus Torvalds's avatar
Linus Torvalds committed
1650
	IPX_SKB_CB(skb)->ipx_source_net = ipx->ipx_source.net;
Linus Torvalds's avatar
Linus Torvalds committed
1651 1652 1653

	/* Determine what local ipx endpoint this is */
	intrfc = ipxitf_find_using_phys(dev, pt->type);
Linus Torvalds's avatar
Linus Torvalds committed
1654 1655
	if (!intrfc) {
		if (ipxcfg_auto_create_interfaces &&
Linus Torvalds's avatar
Linus Torvalds committed
1656
		   ntohl(IPX_SKB_CB(skb)->ipx_dest_net)) {
Linus Torvalds's avatar
Linus Torvalds committed
1657
			intrfc = ipxitf_auto_create(dev, pt->type);
Linus Torvalds's avatar
Linus Torvalds committed
1658 1659
			if (intrfc)
				ipxitf_hold(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
1660 1661
		}

Linus Torvalds's avatar
Linus Torvalds committed
1662
		if (!intrfc)	/* Not one of ours */
Linus Torvalds's avatar
Linus Torvalds committed
1663
				/* or invalid packet for auto creation */
Linus Torvalds's avatar
Linus Torvalds committed
1664 1665 1666
			goto drop;
	}

1667
	rc = ipxitf_rcv(intrfc, skb);
Linus Torvalds's avatar
Linus Torvalds committed
1668
	ipxitf_put(intrfc);
Linus Torvalds's avatar
Linus Torvalds committed
1669
	goto out;
1670 1671 1672
drop:
	kfree_skb(skb);
out:
1673
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1674 1675
}

Benjamin LaHaise's avatar
Benjamin LaHaise committed
1676
static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock,
1677
	struct msghdr *msg, int len)
Linus Torvalds's avatar
Linus Torvalds committed
1678 1679
{
	struct sock *sk = sock->sk;
1680
	struct ipx_opt *ipxs = ipx_sk(sk);
Linus Torvalds's avatar
Linus Torvalds committed
1681
	struct sockaddr_ipx *usipx = (struct sockaddr_ipx *)msg->msg_name;
Linus Torvalds's avatar
Linus Torvalds committed
1682
	struct sockaddr_ipx local_sipx;
1683
	int rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1684 1685 1686
	int flags = msg->msg_flags;

	/* Socket gets bound below anyway */
1687
/*	if (sk->sk_zapped)
Linus Torvalds's avatar
Linus Torvalds committed
1688 1689
		return -EIO; */	/* Socket not bound */
	if (flags & ~MSG_DONTWAIT)
Linus Torvalds's avatar
Linus Torvalds committed
1690
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1691 1692

	if (usipx) {
1693
		if (!ipxs->port) {
Linus Torvalds's avatar
Linus Torvalds committed
1694 1695
			struct sockaddr_ipx uaddr;

Linus Torvalds's avatar
Linus Torvalds committed
1696 1697
			uaddr.sipx_port		= 0;
			uaddr.sipx_network	= 0;
Linus Torvalds's avatar
Linus Torvalds committed
1698
#ifdef CONFIG_IPX_INTERN
1699
			rc = -ENETDOWN;
1700
			if (!ipxs->intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1701
				goto out; /* Someone zonked the iface */
1702
			memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
Linus Torvalds's avatar
Linus Torvalds committed
1703
				IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1704
#endif
1705
			rc = ipx_bind(sock, (struct sockaddr *)&uaddr,
Linus Torvalds's avatar
Linus Torvalds committed
1706
					sizeof(struct sockaddr_ipx));
1707
			if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
1708
				goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1709 1710
		}

1711
		rc = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1712 1713 1714
		if (msg->msg_namelen < sizeof(*usipx) ||
		    usipx->sipx_family != AF_IPX)
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1715
	} else {
1716
		rc = -ENOTCONN;
1717
		if (sk->sk_state != TCP_ESTABLISHED)
Linus Torvalds's avatar
Linus Torvalds committed
1718
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1719

Linus Torvalds's avatar
Linus Torvalds committed
1720
		usipx = &local_sipx;
Linus Torvalds's avatar
Linus Torvalds committed
1721
		usipx->sipx_family 	= AF_IPX;
1722 1723 1724 1725
		usipx->sipx_type 	= ipxs->type;
		usipx->sipx_port 	= ipxs->dest_addr.sock;
		usipx->sipx_network 	= ipxs->dest_addr.net;
		memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1726 1727
	}

1728 1729 1730 1731
	rc = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len,
				 flags & MSG_DONTWAIT);
	if (rc >= 0)
		rc = len;
1732
out:
1733
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1734 1735 1736
}


Benjamin LaHaise's avatar
Benjamin LaHaise committed
1737
static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
1738
		struct msghdr *msg, int size, int flags)
Linus Torvalds's avatar
Linus Torvalds committed
1739 1740
{
	struct sock *sk = sock->sk;
1741
	struct ipx_opt *ipxs = ipx_sk(sk);
Linus Torvalds's avatar
Linus Torvalds committed
1742 1743 1744
	struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)msg->msg_name;
	struct ipxhdr *ipx = NULL;
	struct sk_buff *skb;
1745
	int copied, rc;
Linus Torvalds's avatar
Linus Torvalds committed
1746 1747

	/* put the autobinding in */
1748
	if (!ipxs->port) {
Linus Torvalds's avatar
Linus Torvalds committed
1749 1750 1751 1752 1753 1754
		struct sockaddr_ipx uaddr;

		uaddr.sipx_port		= 0;
		uaddr.sipx_network 	= 0;

#ifdef CONFIG_IPX_INTERN
1755
		rc = -ENETDOWN;
1756
		if (!ipxs->intrfc)
Linus Torvalds's avatar
Linus Torvalds committed
1757
			goto out; /* Someone zonked the iface */
1758
		memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1759 1760
#endif	/* CONFIG_IPX_INTERN */

1761 1762 1763
		rc = ipx_bind(sock, (struct sockaddr *)&uaddr,
			      sizeof(struct sockaddr_ipx));
		if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
1764
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1765 1766
	}
	
1767
	rc = -ENOTCONN;
1768
	if (sk->sk_zapped)
Linus Torvalds's avatar
Linus Torvalds committed
1769
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1770

Linus Torvalds's avatar
Linus Torvalds committed
1771
	skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
1772
				flags & MSG_DONTWAIT, &rc);
Linus Torvalds's avatar
Linus Torvalds committed
1773
	if (!skb)
Linus Torvalds's avatar
Linus Torvalds committed
1774 1775
		goto out;

1776
	ipx 	= ipx_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
1777
	copied 	= ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
Linus Torvalds's avatar
Linus Torvalds committed
1778
	if (copied > size) {
Linus Torvalds's avatar
Linus Torvalds committed
1779
		copied = size;
Linus Torvalds's avatar
Linus Torvalds committed
1780 1781 1782
		msg->msg_flags |= MSG_TRUNC;
	}

1783 1784 1785
	rc = skb_copy_datagram_iovec(skb, sizeof(struct ipxhdr), msg->msg_iov,
				     copied);
	if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
1786
		goto out_free;
1787
	sk->sk_stamp = skb->stamp;
Linus Torvalds's avatar
Linus Torvalds committed
1788 1789 1790

	msg->msg_namelen = sizeof(*sipx);

Linus Torvalds's avatar
Linus Torvalds committed
1791
	if (sipx) {
Linus Torvalds's avatar
Linus Torvalds committed
1792 1793
		sipx->sipx_family	= AF_IPX;
		sipx->sipx_port		= ipx->ipx_source.sock;
Linus Torvalds's avatar
Linus Torvalds committed
1794
		memcpy(sipx->sipx_node, ipx->ipx_source.node, IPX_NODE_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1795
		sipx->sipx_network	= IPX_SKB_CB(skb)->ipx_source_net;
Linus Torvalds's avatar
Linus Torvalds committed
1796 1797
		sipx->sipx_type 	= ipx->ipx_type;
	}
1798
	rc = copied;
Linus Torvalds's avatar
Linus Torvalds committed
1799 1800 1801

out_free:
	skb_free_datagram(sk, skb);
1802
out:
1803
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1804 1805 1806
}


Linus Torvalds's avatar
Linus Torvalds committed
1807
static int ipx_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
Linus Torvalds's avatar
Linus Torvalds committed
1808
{
1809
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1810 1811 1812
	long amount = 0;
	struct sock *sk = sock->sk;

Linus Torvalds's avatar
Linus Torvalds committed
1813
	switch (cmd) {
1814
	case TIOCOUTQ:
1815
		amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
1816 1817 1818 1819 1820
		if (amount < 0)
			amount = 0;
		rc = put_user(amount, (int *)arg);
		break;
	case TIOCINQ: {
1821
		struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861
		/* These two are safe on a single CPU system as only
		 * user tasks fiddle here */
		if (skb)
			amount = skb->len - sizeof(struct ipxhdr);
		rc = put_user(amount, (int *)arg);
		break;
	}
	case SIOCADDRT:
	case SIOCDELRT:
		rc = -EPERM;
		if (capable(CAP_NET_ADMIN))
			rc = ipxrtr_ioctl(cmd, (void *)arg);
		break;
	case SIOCSIFADDR:
	case SIOCAIPXITFCRT:
	case SIOCAIPXPRISLT:
		rc = -EPERM;
		if (!capable(CAP_NET_ADMIN))
			break;
	case SIOCGIFADDR:
		rc = ipxitf_ioctl(cmd, (void *)arg);
		break;
	case SIOCIPXCFGDATA:
		rc = ipxcfg_get_config_data((void *)arg);
		break;
	case SIOCIPXNCPCONN:
		/*
		 * This socket wants to take care of the NCP connection
		 * handed to us in arg.
		 */
        	rc = -EPERM;
        	if (!capable(CAP_NET_ADMIN))
			break;
		rc = get_user(ipx_sk(sk)->ipx_ncp_conn,
			      (const unsigned short *)(arg));
		break;
	case SIOCGSTAMP:
		rc = -EINVAL;
		if (sk) {
			rc = -ENOENT;
1862
			if (!sk->sk_stamp.tv_sec)
1863 1864
				break;
			rc = -EFAULT;
1865
			if (!copy_to_user((void *)arg, &sk->sk_stamp,
1866 1867
					  sizeof(struct timeval)))
				rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1868
		}
1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880
		break;
	case SIOCGIFDSTADDR:
	case SIOCSIFDSTADDR:
	case SIOCGIFBRDADDR:
	case SIOCSIFBRDADDR:
	case SIOCGIFNETMASK:
	case SIOCSIFNETMASK:
		rc = -EINVAL;
		break;
	default:
		rc = dev_ioctl(cmd,(void *) arg);
		break;
Linus Torvalds's avatar
Linus Torvalds committed
1881 1882
	}

1883
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1884 1885 1886 1887 1888 1889 1890
}

/*
 * Socket family declarations
 */

static struct net_proto_family ipx_family_ops = {
1891 1892
	.family		= PF_IPX,
	.create		= ipx_create,
1893
	.owner		= THIS_MODULE,
Linus Torvalds's avatar
Linus Torvalds committed
1894 1895 1896
};

static struct proto_ops SOCKOPS_WRAPPED(ipx_dgram_ops) = {
1897
	.family		= PF_IPX,
1898
	.owner		= THIS_MODULE,
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914
	.release	= ipx_release,
	.bind		= ipx_bind,
	.connect	= ipx_connect,
	.socketpair	= sock_no_socketpair,
	.accept		= sock_no_accept,
	.getname	= ipx_getname,
	.poll		= datagram_poll,
	.ioctl		= ipx_ioctl,
	.listen		= sock_no_listen,
	.shutdown	= sock_no_shutdown, /* FIXME: support shutdown */
	.setsockopt	= ipx_setsockopt,
	.getsockopt	= ipx_getsockopt,
	.sendmsg	= ipx_sendmsg,
	.recvmsg	= ipx_recvmsg,
	.mmap		= sock_no_mmap,
	.sendpage	= sock_no_sendpage,
Linus Torvalds's avatar
Linus Torvalds committed
1915 1916 1917 1918 1919
};

#include <linux/smp_lock.h>
SOCKOPS_WRAP(ipx_dgram, PF_IPX);

Linus Torvalds's avatar
Linus Torvalds committed
1920
static struct packet_type ipx_8023_packet_type = {
1921 1922 1923
	.type		= __constant_htons(ETH_P_802_3),
	.func		= ipx_rcv,
	.data		= (void *)1,	/* yap, I understand shared skbs :-) */
Linus Torvalds's avatar
Linus Torvalds committed
1924 1925
};

Linus Torvalds's avatar
Linus Torvalds committed
1926
static struct packet_type ipx_dix_packet_type = {
1927 1928 1929
	.type		= __constant_htons(ETH_P_IPX),
	.func		= ipx_rcv,
	.data		= (void *)1,	/* yap, I understand shared skbs :-) */
Linus Torvalds's avatar
Linus Torvalds committed
1930 1931
};

Linus Torvalds's avatar
Linus Torvalds committed
1932
static struct notifier_block ipx_dev_notifier = {
1933
	.notifier_call	= ipxitf_device_event,
Linus Torvalds's avatar
Linus Torvalds committed
1934 1935 1936 1937 1938 1939 1940 1941 1942
};

extern struct datalink_proto *make_EII_client(void);
extern struct datalink_proto *make_8023_client(void);
extern void destroy_EII_client(struct datalink_proto *);
extern void destroy_8023_client(struct datalink_proto *);

static unsigned char ipx_8022_type = 0xE0;
static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
1943 1944 1945 1946
static char ipx_EII_err_msg[] __initdata =
	KERN_CRIT "IPX: Unable to register with Ethernet II\n";
static char ipx_8023_err_msg[] __initdata =
	KERN_CRIT "IPX: Unable to register with 802.3\n";
1947 1948 1949 1950
static char ipx_llc_err_msg[] __initdata =
	KERN_CRIT "IPX: Unable to register with 802.2\n";
static char ipx_snap_err_msg[] __initdata =
	KERN_CRIT "IPX: Unable to register with SNAP\n";
Linus Torvalds's avatar
Linus Torvalds committed
1951 1952 1953

static int __init ipx_init(void)
{
Linus Torvalds's avatar
Linus Torvalds committed
1954
	sock_register(&ipx_family_ops);
Linus Torvalds's avatar
Linus Torvalds committed
1955 1956

	pEII_datalink = make_EII_client();
1957 1958 1959 1960
	if (pEII_datalink)
		dev_add_pack(&ipx_dix_packet_type);
	else
		printk(ipx_EII_err_msg);
Linus Torvalds's avatar
Linus Torvalds committed
1961 1962

	p8023_datalink = make_8023_client();
1963 1964 1965 1966
	if (p8023_datalink)
		dev_add_pack(&ipx_8023_packet_type);
	else
		printk(ipx_8023_err_msg);
Linus Torvalds's avatar
Linus Torvalds committed
1967

1968
	p8022_datalink = register_8022_client(ipx_8022_type, ipx_rcv);
Linus Torvalds's avatar
Linus Torvalds committed
1969
	if (!p8022_datalink)
1970
		printk(ipx_llc_err_msg);
Linus Torvalds's avatar
Linus Torvalds committed
1971

Linus Torvalds's avatar
Linus Torvalds committed
1972
	pSNAP_datalink = register_snap_client(ipx_snap_id, ipx_rcv);
Linus Torvalds's avatar
Linus Torvalds committed
1973
	if (!pSNAP_datalink)
1974
		printk(ipx_snap_err_msg);
Linus Torvalds's avatar
Linus Torvalds committed
1975 1976

	register_netdevice_notifier(&ipx_dev_notifier);
Linus Torvalds's avatar
Linus Torvalds committed
1977
	ipx_register_sysctl();
1978
	ipx_proc_init();
Linus Torvalds's avatar
Linus Torvalds committed
1979 1980
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
1981

Linus Torvalds's avatar
Linus Torvalds committed
1982
static void __exit ipx_proto_finito(void)
Linus Torvalds's avatar
Linus Torvalds committed
1983
{
1984 1985 1986 1987 1988
	/*
	 * No need to worry about having anything on the ipx_interfaces list,
	 * when a interface is created we increment the module usage count, so
	 * the module will only be unloaded when there are no more interfaces
	 */
1989 1990 1991 1992
	if (unlikely(!list_empty(&ipx_interfaces)))
		BUG();
	if (unlikely(!list_empty(&ipx_routes)))
		BUG();
1993 1994

	ipx_proc_exit();
Linus Torvalds's avatar
Linus Torvalds committed
1995
	ipx_unregister_sysctl();
Linus Torvalds's avatar
Linus Torvalds committed
1996 1997 1998

	unregister_netdevice_notifier(&ipx_dev_notifier);

1999
	unregister_snap_client(pSNAP_datalink);
Linus Torvalds's avatar
Linus Torvalds committed
2000
	pSNAP_datalink = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2001

2002
	unregister_8022_client(p8022_datalink);
Linus Torvalds's avatar
Linus Torvalds committed
2003
	p8022_datalink = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2004 2005 2006

	dev_remove_pack(&ipx_8023_packet_type);
	destroy_8023_client(p8023_datalink);
Linus Torvalds's avatar
Linus Torvalds committed
2007
	p8023_datalink = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2008 2009 2010

	dev_remove_pack(&ipx_dix_packet_type);
	destroy_EII_client(pEII_datalink);
Linus Torvalds's avatar
Linus Torvalds committed
2011
	pEII_datalink = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2012

Linus Torvalds's avatar
Linus Torvalds committed
2013
	sock_unregister(ipx_family_ops.family);
Linus Torvalds's avatar
Linus Torvalds committed
2014
}
Linus Torvalds's avatar
Linus Torvalds committed
2015

2016
module_init(ipx_init);
Linus Torvalds's avatar
Linus Torvalds committed
2017
module_exit(ipx_proto_finito);
Linus Torvalds's avatar
Linus Torvalds committed
2018
MODULE_LICENSE("GPL");
2019
MODULE_ALIAS_NETPROTO(PF_IPX);