162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	DDP:	An implementation of the AppleTalk DDP protocol for
462306a36Sopenharmony_ci *		Ethernet 'ELAP'.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *		Alan Cox  <alan@lxorguk.ukuu.org.uk>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *		With more than a little assistance from
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *		Wesley Craig <netatalk@umich.edu>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *	Fixes:
1362306a36Sopenharmony_ci *		Neil Horman		:	Added missing device ioctls
1462306a36Sopenharmony_ci *		Michael Callahan	:	Made routing work
1562306a36Sopenharmony_ci *		Wesley Craig		:	Fix probing to listen to a
1662306a36Sopenharmony_ci *						passed node id.
1762306a36Sopenharmony_ci *		Alan Cox		:	Added send/recvmsg support
1862306a36Sopenharmony_ci *		Alan Cox		:	Moved at. to protinfo in
1962306a36Sopenharmony_ci *						socket.
2062306a36Sopenharmony_ci *		Alan Cox		:	Added firewall hooks.
2162306a36Sopenharmony_ci *		Alan Cox		:	Supports new ARPHRD_LOOPBACK
2262306a36Sopenharmony_ci *		Christer Weinigel	: 	Routing and /proc fixes.
2362306a36Sopenharmony_ci *		Bradford Johnson	:	LocalTalk.
2462306a36Sopenharmony_ci *		Tom Dyas		:	Module support.
2562306a36Sopenharmony_ci *		Alan Cox		:	Hooks for PPP (based on the
2662306a36Sopenharmony_ci *						LocalTalk hook).
2762306a36Sopenharmony_ci *		Alan Cox		:	Posix bits
2862306a36Sopenharmony_ci *		Alan Cox/Mike Freeman	:	Possible fix to NBP problems
2962306a36Sopenharmony_ci *		Bradford Johnson	:	IP-over-DDP (experimental)
3062306a36Sopenharmony_ci *		Jay Schulist		:	Moved IP-over-DDP to its own
3162306a36Sopenharmony_ci *						driver file. (ipddp.c & ipddp.h)
3262306a36Sopenharmony_ci *		Jay Schulist		:	Made work as module with
3362306a36Sopenharmony_ci *						AppleTalk drivers, cleaned it.
3462306a36Sopenharmony_ci *		Rob Newberry		:	Added proxy AARP and AARP
3562306a36Sopenharmony_ci *						procfs, moved probing to AARP
3662306a36Sopenharmony_ci *						module.
3762306a36Sopenharmony_ci *              Adrian Sun/
3862306a36Sopenharmony_ci *              Michael Zuelsdorff      :       fix for net.0 packets. don't
3962306a36Sopenharmony_ci *                                              allow illegal ether/tokentalk
4062306a36Sopenharmony_ci *                                              port assignment. we lose a
4162306a36Sopenharmony_ci *                                              valid localtalk port as a
4262306a36Sopenharmony_ci *                                              result.
4362306a36Sopenharmony_ci *		Arnaldo C. de Melo	:	Cleanup, in preparation for
4462306a36Sopenharmony_ci *						shared skb support 8)
4562306a36Sopenharmony_ci *		Arnaldo C. de Melo	:	Move proc stuff to atalk_proc.c,
4662306a36Sopenharmony_ci *						use seq_file
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include <linux/capability.h>
5062306a36Sopenharmony_ci#include <linux/module.h>
5162306a36Sopenharmony_ci#include <linux/if_arp.h>
5262306a36Sopenharmony_ci#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
5362306a36Sopenharmony_ci#include <linux/compat.h>
5462306a36Sopenharmony_ci#include <linux/slab.h>
5562306a36Sopenharmony_ci#include <net/datalink.h>
5662306a36Sopenharmony_ci#include <net/psnap.h>
5762306a36Sopenharmony_ci#include <net/sock.h>
5862306a36Sopenharmony_ci#include <net/tcp_states.h>
5962306a36Sopenharmony_ci#include <net/route.h>
6062306a36Sopenharmony_ci#include <net/compat.h>
6162306a36Sopenharmony_ci#include <linux/atalk.h>
6262306a36Sopenharmony_ci#include <linux/highmem.h>
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct datalink_proto *ddp_dl, *aarp_dl;
6562306a36Sopenharmony_cistatic const struct proto_ops atalk_dgram_ops;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**************************************************************************\
6862306a36Sopenharmony_ci*                                                                          *
6962306a36Sopenharmony_ci* Handlers for the socket list.                                            *
7062306a36Sopenharmony_ci*                                                                          *
7162306a36Sopenharmony_ci\**************************************************************************/
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciHLIST_HEAD(atalk_sockets);
7462306a36Sopenharmony_ciDEFINE_RWLOCK(atalk_sockets_lock);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic inline void __atalk_insert_socket(struct sock *sk)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	sk_add_node(sk, &atalk_sockets);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic inline void atalk_remove_socket(struct sock *sk)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	write_lock_bh(&atalk_sockets_lock);
8462306a36Sopenharmony_ci	sk_del_node_init(sk);
8562306a36Sopenharmony_ci	write_unlock_bh(&atalk_sockets_lock);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct sock *atalk_search_socket(struct sockaddr_at *to,
8962306a36Sopenharmony_ci					struct atalk_iface *atif)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct sock *s;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	read_lock_bh(&atalk_sockets_lock);
9462306a36Sopenharmony_ci	sk_for_each(s, &atalk_sockets) {
9562306a36Sopenharmony_ci		struct atalk_sock *at = at_sk(s);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		if (to->sat_port != at->src_port)
9862306a36Sopenharmony_ci			continue;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		if (to->sat_addr.s_net == ATADDR_ANYNET &&
10162306a36Sopenharmony_ci		    to->sat_addr.s_node == ATADDR_BCAST)
10262306a36Sopenharmony_ci			goto found;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		if (to->sat_addr.s_net == at->src_net &&
10562306a36Sopenharmony_ci		    (to->sat_addr.s_node == at->src_node ||
10662306a36Sopenharmony_ci		     to->sat_addr.s_node == ATADDR_BCAST ||
10762306a36Sopenharmony_ci		     to->sat_addr.s_node == ATADDR_ANYNODE))
10862306a36Sopenharmony_ci			goto found;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		/* XXXX.0 -- we got a request for this router. make sure
11162306a36Sopenharmony_ci		 * that the node is appropriately set. */
11262306a36Sopenharmony_ci		if (to->sat_addr.s_node == ATADDR_ANYNODE &&
11362306a36Sopenharmony_ci		    to->sat_addr.s_net != ATADDR_ANYNET &&
11462306a36Sopenharmony_ci		    atif->address.s_node == at->src_node) {
11562306a36Sopenharmony_ci			to->sat_addr.s_node = atif->address.s_node;
11662306a36Sopenharmony_ci			goto found;
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	s = NULL;
12062306a36Sopenharmony_cifound:
12162306a36Sopenharmony_ci	read_unlock_bh(&atalk_sockets_lock);
12262306a36Sopenharmony_ci	return s;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/**
12662306a36Sopenharmony_ci * atalk_find_or_insert_socket - Try to find a socket matching ADDR
12762306a36Sopenharmony_ci * @sk: socket to insert in the list if it is not there already
12862306a36Sopenharmony_ci * @sat: address to search for
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * Try to find a socket matching ADDR in the socket list, if found then return
13162306a36Sopenharmony_ci * it. If not, insert SK into the socket list.
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * This entire operation must execute atomically.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_cistatic struct sock *atalk_find_or_insert_socket(struct sock *sk,
13662306a36Sopenharmony_ci						struct sockaddr_at *sat)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct sock *s;
13962306a36Sopenharmony_ci	struct atalk_sock *at;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	write_lock_bh(&atalk_sockets_lock);
14262306a36Sopenharmony_ci	sk_for_each(s, &atalk_sockets) {
14362306a36Sopenharmony_ci		at = at_sk(s);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		if (at->src_net == sat->sat_addr.s_net &&
14662306a36Sopenharmony_ci		    at->src_node == sat->sat_addr.s_node &&
14762306a36Sopenharmony_ci		    at->src_port == sat->sat_port)
14862306a36Sopenharmony_ci			goto found;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	s = NULL;
15162306a36Sopenharmony_ci	__atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */
15262306a36Sopenharmony_cifound:
15362306a36Sopenharmony_ci	write_unlock_bh(&atalk_sockets_lock);
15462306a36Sopenharmony_ci	return s;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void atalk_destroy_timer(struct timer_list *t)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct sock *sk = from_timer(sk, t, sk_timer);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (sk_has_allocations(sk)) {
16262306a36Sopenharmony_ci		sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME;
16362306a36Sopenharmony_ci		add_timer(&sk->sk_timer);
16462306a36Sopenharmony_ci	} else
16562306a36Sopenharmony_ci		sock_put(sk);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic inline void atalk_destroy_socket(struct sock *sk)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	atalk_remove_socket(sk);
17162306a36Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (sk_has_allocations(sk)) {
17462306a36Sopenharmony_ci		timer_setup(&sk->sk_timer, atalk_destroy_timer, 0);
17562306a36Sopenharmony_ci		sk->sk_timer.expires	= jiffies + SOCK_DESTROY_TIME;
17662306a36Sopenharmony_ci		add_timer(&sk->sk_timer);
17762306a36Sopenharmony_ci	} else
17862306a36Sopenharmony_ci		sock_put(sk);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**************************************************************************\
18262306a36Sopenharmony_ci*                                                                          *
18362306a36Sopenharmony_ci* Routing tables for the AppleTalk socket layer.                           *
18462306a36Sopenharmony_ci*                                                                          *
18562306a36Sopenharmony_ci\**************************************************************************/
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */
18862306a36Sopenharmony_cistruct atalk_route *atalk_routes;
18962306a36Sopenharmony_ciDEFINE_RWLOCK(atalk_routes_lock);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistruct atalk_iface *atalk_interfaces;
19262306a36Sopenharmony_ciDEFINE_RWLOCK(atalk_interfaces_lock);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* For probing devices or in a routerless network */
19562306a36Sopenharmony_cistruct atalk_route atrtr_default;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/* AppleTalk interface control */
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * Drop a device. Doesn't drop any of its routes - that is the caller's
20062306a36Sopenharmony_ci * problem. Called when we down the interface or delete the address.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic void atif_drop_device(struct net_device *dev)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct atalk_iface **iface = &atalk_interfaces;
20562306a36Sopenharmony_ci	struct atalk_iface *tmp;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	write_lock_bh(&atalk_interfaces_lock);
20862306a36Sopenharmony_ci	while ((tmp = *iface) != NULL) {
20962306a36Sopenharmony_ci		if (tmp->dev == dev) {
21062306a36Sopenharmony_ci			*iface = tmp->next;
21162306a36Sopenharmony_ci			dev_put(dev);
21262306a36Sopenharmony_ci			kfree(tmp);
21362306a36Sopenharmony_ci			dev->atalk_ptr = NULL;
21462306a36Sopenharmony_ci		} else
21562306a36Sopenharmony_ci			iface = &tmp->next;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	write_unlock_bh(&atalk_interfaces_lock);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic struct atalk_iface *atif_add_device(struct net_device *dev,
22162306a36Sopenharmony_ci					   struct atalk_addr *sa)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (!iface)
22662306a36Sopenharmony_ci		goto out;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	dev_hold(dev);
22962306a36Sopenharmony_ci	iface->dev = dev;
23062306a36Sopenharmony_ci	dev->atalk_ptr = iface;
23162306a36Sopenharmony_ci	iface->address = *sa;
23262306a36Sopenharmony_ci	iface->status = 0;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	write_lock_bh(&atalk_interfaces_lock);
23562306a36Sopenharmony_ci	iface->next = atalk_interfaces;
23662306a36Sopenharmony_ci	atalk_interfaces = iface;
23762306a36Sopenharmony_ci	write_unlock_bh(&atalk_interfaces_lock);
23862306a36Sopenharmony_ciout:
23962306a36Sopenharmony_ci	return iface;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/* Perform phase 2 AARP probing on our tentative address */
24362306a36Sopenharmony_cistatic int atif_probe_device(struct atalk_iface *atif)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int netrange = ntohs(atif->nets.nr_lastnet) -
24662306a36Sopenharmony_ci			ntohs(atif->nets.nr_firstnet) + 1;
24762306a36Sopenharmony_ci	int probe_net = ntohs(atif->address.s_net);
24862306a36Sopenharmony_ci	int probe_node = atif->address.s_node;
24962306a36Sopenharmony_ci	int netct, nodect;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Offset the network we start probing with */
25262306a36Sopenharmony_ci	if (probe_net == ATADDR_ANYNET) {
25362306a36Sopenharmony_ci		probe_net = ntohs(atif->nets.nr_firstnet);
25462306a36Sopenharmony_ci		if (netrange)
25562306a36Sopenharmony_ci			probe_net += jiffies % netrange;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	if (probe_node == ATADDR_ANYNODE)
25862306a36Sopenharmony_ci		probe_node = jiffies & 0xFF;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Scan the networks */
26162306a36Sopenharmony_ci	atif->status |= ATIF_PROBE;
26262306a36Sopenharmony_ci	for (netct = 0; netct <= netrange; netct++) {
26362306a36Sopenharmony_ci		/* Sweep the available nodes from a given start */
26462306a36Sopenharmony_ci		atif->address.s_net = htons(probe_net);
26562306a36Sopenharmony_ci		for (nodect = 0; nodect < 256; nodect++) {
26662306a36Sopenharmony_ci			atif->address.s_node = (nodect + probe_node) & 0xFF;
26762306a36Sopenharmony_ci			if (atif->address.s_node > 0 &&
26862306a36Sopenharmony_ci			    atif->address.s_node < 254) {
26962306a36Sopenharmony_ci				/* Probe a proposed address */
27062306a36Sopenharmony_ci				aarp_probe_network(atif);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci				if (!(atif->status & ATIF_PROBE_FAIL)) {
27362306a36Sopenharmony_ci					atif->status &= ~ATIF_PROBE;
27462306a36Sopenharmony_ci					return 0;
27562306a36Sopenharmony_ci				}
27662306a36Sopenharmony_ci			}
27762306a36Sopenharmony_ci			atif->status &= ~ATIF_PROBE_FAIL;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci		probe_net++;
28062306a36Sopenharmony_ci		if (probe_net > ntohs(atif->nets.nr_lastnet))
28162306a36Sopenharmony_ci			probe_net = ntohs(atif->nets.nr_firstnet);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	atif->status &= ~ATIF_PROBE;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return -EADDRINUSE;	/* Network is full... */
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/* Perform AARP probing for a proxy address */
29062306a36Sopenharmony_cistatic int atif_proxy_probe_device(struct atalk_iface *atif,
29162306a36Sopenharmony_ci				   struct atalk_addr *proxy_addr)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	int netrange = ntohs(atif->nets.nr_lastnet) -
29462306a36Sopenharmony_ci			ntohs(atif->nets.nr_firstnet) + 1;
29562306a36Sopenharmony_ci	/* we probe the interface's network */
29662306a36Sopenharmony_ci	int probe_net = ntohs(atif->address.s_net);
29762306a36Sopenharmony_ci	int probe_node = ATADDR_ANYNODE;	    /* we'll take anything */
29862306a36Sopenharmony_ci	int netct, nodect;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* Offset the network we start probing with */
30162306a36Sopenharmony_ci	if (probe_net == ATADDR_ANYNET) {
30262306a36Sopenharmony_ci		probe_net = ntohs(atif->nets.nr_firstnet);
30362306a36Sopenharmony_ci		if (netrange)
30462306a36Sopenharmony_ci			probe_net += jiffies % netrange;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (probe_node == ATADDR_ANYNODE)
30862306a36Sopenharmony_ci		probe_node = jiffies & 0xFF;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Scan the networks */
31162306a36Sopenharmony_ci	for (netct = 0; netct <= netrange; netct++) {
31262306a36Sopenharmony_ci		/* Sweep the available nodes from a given start */
31362306a36Sopenharmony_ci		proxy_addr->s_net = htons(probe_net);
31462306a36Sopenharmony_ci		for (nodect = 0; nodect < 256; nodect++) {
31562306a36Sopenharmony_ci			proxy_addr->s_node = (nodect + probe_node) & 0xFF;
31662306a36Sopenharmony_ci			if (proxy_addr->s_node > 0 &&
31762306a36Sopenharmony_ci			    proxy_addr->s_node < 254) {
31862306a36Sopenharmony_ci				/* Tell AARP to probe a proposed address */
31962306a36Sopenharmony_ci				int ret = aarp_proxy_probe_network(atif,
32062306a36Sopenharmony_ci								    proxy_addr);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci				if (ret != -EADDRINUSE)
32362306a36Sopenharmony_ci					return ret;
32462306a36Sopenharmony_ci			}
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		probe_net++;
32762306a36Sopenharmony_ci		if (probe_net > ntohs(atif->nets.nr_lastnet))
32862306a36Sopenharmony_ci			probe_net = ntohs(atif->nets.nr_firstnet);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return -EADDRINUSE;	/* Network is full... */
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistruct atalk_addr *atalk_find_dev_addr(struct net_device *dev)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct atalk_iface *iface = dev->atalk_ptr;
33862306a36Sopenharmony_ci	return iface ? &iface->address : NULL;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic struct atalk_addr *atalk_find_primary(void)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct atalk_iface *fiface = NULL;
34462306a36Sopenharmony_ci	struct atalk_addr *retval;
34562306a36Sopenharmony_ci	struct atalk_iface *iface;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/*
34862306a36Sopenharmony_ci	 * Return a point-to-point interface only if
34962306a36Sopenharmony_ci	 * there is no non-ptp interface available.
35062306a36Sopenharmony_ci	 */
35162306a36Sopenharmony_ci	read_lock_bh(&atalk_interfaces_lock);
35262306a36Sopenharmony_ci	for (iface = atalk_interfaces; iface; iface = iface->next) {
35362306a36Sopenharmony_ci		if (!fiface && !(iface->dev->flags & IFF_LOOPBACK))
35462306a36Sopenharmony_ci			fiface = iface;
35562306a36Sopenharmony_ci		if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) {
35662306a36Sopenharmony_ci			retval = &iface->address;
35762306a36Sopenharmony_ci			goto out;
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (fiface)
36262306a36Sopenharmony_ci		retval = &fiface->address;
36362306a36Sopenharmony_ci	else if (atalk_interfaces)
36462306a36Sopenharmony_ci		retval = &atalk_interfaces->address;
36562306a36Sopenharmony_ci	else
36662306a36Sopenharmony_ci		retval = NULL;
36762306a36Sopenharmony_ciout:
36862306a36Sopenharmony_ci	read_unlock_bh(&atalk_interfaces_lock);
36962306a36Sopenharmony_ci	return retval;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/*
37362306a36Sopenharmony_ci * Find a match for 'any network' - ie any of our interfaces with that
37462306a36Sopenharmony_ci * node number will do just nicely.
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistatic struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct atalk_iface *iface = dev->atalk_ptr;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!iface || iface->status & ATIF_PROBE)
38162306a36Sopenharmony_ci		goto out_err;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (node != ATADDR_BCAST &&
38462306a36Sopenharmony_ci	    iface->address.s_node != node &&
38562306a36Sopenharmony_ci	    node != ATADDR_ANYNODE)
38662306a36Sopenharmony_ci		goto out_err;
38762306a36Sopenharmony_ciout:
38862306a36Sopenharmony_ci	return iface;
38962306a36Sopenharmony_ciout_err:
39062306a36Sopenharmony_ci	iface = NULL;
39162306a36Sopenharmony_ci	goto out;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/* Find a match for a specific network:node pair */
39562306a36Sopenharmony_cistatic struct atalk_iface *atalk_find_interface(__be16 net, int node)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct atalk_iface *iface;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	read_lock_bh(&atalk_interfaces_lock);
40062306a36Sopenharmony_ci	for (iface = atalk_interfaces; iface; iface = iface->next) {
40162306a36Sopenharmony_ci		if ((node == ATADDR_BCAST ||
40262306a36Sopenharmony_ci		     node == ATADDR_ANYNODE ||
40362306a36Sopenharmony_ci		     iface->address.s_node == node) &&
40462306a36Sopenharmony_ci		    iface->address.s_net == net &&
40562306a36Sopenharmony_ci		    !(iface->status & ATIF_PROBE))
40662306a36Sopenharmony_ci			break;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* XXXX.0 -- net.0 returns the iface associated with net */
40962306a36Sopenharmony_ci		if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET &&
41062306a36Sopenharmony_ci		    ntohs(iface->nets.nr_firstnet) <= ntohs(net) &&
41162306a36Sopenharmony_ci		    ntohs(net) <= ntohs(iface->nets.nr_lastnet))
41262306a36Sopenharmony_ci			break;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci	read_unlock_bh(&atalk_interfaces_lock);
41562306a36Sopenharmony_ci	return iface;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/*
42062306a36Sopenharmony_ci * Find a route for an AppleTalk packet. This ought to get cached in
42162306a36Sopenharmony_ci * the socket (later on...). We know about host routes and the fact
42262306a36Sopenharmony_ci * that a route must be direct to broadcast.
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistatic struct atalk_route *atrtr_find(struct atalk_addr *target)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * we must search through all routes unless we find a
42862306a36Sopenharmony_ci	 * host route, because some host routes might overlap
42962306a36Sopenharmony_ci	 * network routes
43062306a36Sopenharmony_ci	 */
43162306a36Sopenharmony_ci	struct atalk_route *net_route = NULL;
43262306a36Sopenharmony_ci	struct atalk_route *r;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	read_lock_bh(&atalk_routes_lock);
43562306a36Sopenharmony_ci	for (r = atalk_routes; r; r = r->next) {
43662306a36Sopenharmony_ci		if (!(r->flags & RTF_UP))
43762306a36Sopenharmony_ci			continue;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		if (r->target.s_net == target->s_net) {
44062306a36Sopenharmony_ci			if (r->flags & RTF_HOST) {
44162306a36Sopenharmony_ci				/*
44262306a36Sopenharmony_ci				 * if this host route is for the target,
44362306a36Sopenharmony_ci				 * the we're done
44462306a36Sopenharmony_ci				 */
44562306a36Sopenharmony_ci				if (r->target.s_node == target->s_node)
44662306a36Sopenharmony_ci					goto out;
44762306a36Sopenharmony_ci			} else
44862306a36Sopenharmony_ci				/*
44962306a36Sopenharmony_ci				 * this route will work if there isn't a
45062306a36Sopenharmony_ci				 * direct host route, so cache it
45162306a36Sopenharmony_ci				 */
45262306a36Sopenharmony_ci				net_route = r;
45362306a36Sopenharmony_ci		}
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/*
45762306a36Sopenharmony_ci	 * if we found a network route but not a direct host
45862306a36Sopenharmony_ci	 * route, then return it
45962306a36Sopenharmony_ci	 */
46062306a36Sopenharmony_ci	if (net_route)
46162306a36Sopenharmony_ci		r = net_route;
46262306a36Sopenharmony_ci	else if (atrtr_default.dev)
46362306a36Sopenharmony_ci		r = &atrtr_default;
46462306a36Sopenharmony_ci	else /* No route can be found */
46562306a36Sopenharmony_ci		r = NULL;
46662306a36Sopenharmony_ciout:
46762306a36Sopenharmony_ci	read_unlock_bh(&atalk_routes_lock);
46862306a36Sopenharmony_ci	return r;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/*
47362306a36Sopenharmony_ci * Given an AppleTalk network, find the device to use. This can be
47462306a36Sopenharmony_ci * a simple lookup.
47562306a36Sopenharmony_ci */
47662306a36Sopenharmony_cistruct net_device *atrtr_get_dev(struct atalk_addr *sa)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct atalk_route *atr = atrtr_find(sa);
47962306a36Sopenharmony_ci	return atr ? atr->dev : NULL;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/* Set up a default router */
48362306a36Sopenharmony_cistatic void atrtr_set_default(struct net_device *dev)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	atrtr_default.dev	     = dev;
48662306a36Sopenharmony_ci	atrtr_default.flags	     = RTF_UP;
48762306a36Sopenharmony_ci	atrtr_default.gateway.s_net  = htons(0);
48862306a36Sopenharmony_ci	atrtr_default.gateway.s_node = 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/*
49262306a36Sopenharmony_ci * Add a router. Basically make sure it looks valid and stuff the
49362306a36Sopenharmony_ci * entry in the list. While it uses netranges we always set them to one
49462306a36Sopenharmony_ci * entry to work like netatalk.
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_cistatic int atrtr_create(struct rtentry *r, struct net_device *devhint)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst;
49962306a36Sopenharmony_ci	struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway;
50062306a36Sopenharmony_ci	struct atalk_route *rt;
50162306a36Sopenharmony_ci	struct atalk_iface *iface, *riface;
50262306a36Sopenharmony_ci	int retval = -EINVAL;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/*
50562306a36Sopenharmony_ci	 * Fixme: Raise/Lower a routing change semaphore for these
50662306a36Sopenharmony_ci	 * operations.
50762306a36Sopenharmony_ci	 */
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Validate the request */
51062306a36Sopenharmony_ci	if (ta->sat_family != AF_APPLETALK ||
51162306a36Sopenharmony_ci	    (!devhint && ga->sat_family != AF_APPLETALK))
51262306a36Sopenharmony_ci		goto out;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Now walk the routing table and make our decisions */
51562306a36Sopenharmony_ci	write_lock_bh(&atalk_routes_lock);
51662306a36Sopenharmony_ci	for (rt = atalk_routes; rt; rt = rt->next) {
51762306a36Sopenharmony_ci		if (r->rt_flags != rt->flags)
51862306a36Sopenharmony_ci			continue;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (ta->sat_addr.s_net == rt->target.s_net) {
52162306a36Sopenharmony_ci			if (!(rt->flags & RTF_HOST))
52262306a36Sopenharmony_ci				break;
52362306a36Sopenharmony_ci			if (ta->sat_addr.s_node == rt->target.s_node)
52462306a36Sopenharmony_ci				break;
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (!devhint) {
52962306a36Sopenharmony_ci		riface = NULL;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		read_lock_bh(&atalk_interfaces_lock);
53262306a36Sopenharmony_ci		for (iface = atalk_interfaces; iface; iface = iface->next) {
53362306a36Sopenharmony_ci			if (!riface &&
53462306a36Sopenharmony_ci			    ntohs(ga->sat_addr.s_net) >=
53562306a36Sopenharmony_ci					ntohs(iface->nets.nr_firstnet) &&
53662306a36Sopenharmony_ci			    ntohs(ga->sat_addr.s_net) <=
53762306a36Sopenharmony_ci					ntohs(iface->nets.nr_lastnet))
53862306a36Sopenharmony_ci				riface = iface;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci			if (ga->sat_addr.s_net == iface->address.s_net &&
54162306a36Sopenharmony_ci			    ga->sat_addr.s_node == iface->address.s_node)
54262306a36Sopenharmony_ci				riface = iface;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci		read_unlock_bh(&atalk_interfaces_lock);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		retval = -ENETUNREACH;
54762306a36Sopenharmony_ci		if (!riface)
54862306a36Sopenharmony_ci			goto out_unlock;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		devhint = riface->dev;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (!rt) {
55462306a36Sopenharmony_ci		rt = kzalloc(sizeof(*rt), GFP_ATOMIC);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		retval = -ENOBUFS;
55762306a36Sopenharmony_ci		if (!rt)
55862306a36Sopenharmony_ci			goto out_unlock;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		rt->next = atalk_routes;
56162306a36Sopenharmony_ci		atalk_routes = rt;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Fill in the routing entry */
56562306a36Sopenharmony_ci	rt->target  = ta->sat_addr;
56662306a36Sopenharmony_ci	dev_hold(devhint);
56762306a36Sopenharmony_ci	rt->dev     = devhint;
56862306a36Sopenharmony_ci	rt->flags   = r->rt_flags;
56962306a36Sopenharmony_ci	rt->gateway = ga->sat_addr;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	retval = 0;
57262306a36Sopenharmony_ciout_unlock:
57362306a36Sopenharmony_ci	write_unlock_bh(&atalk_routes_lock);
57462306a36Sopenharmony_ciout:
57562306a36Sopenharmony_ci	return retval;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/* Delete a route. Find it and discard it */
57962306a36Sopenharmony_cistatic int atrtr_delete(struct atalk_addr *addr)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	struct atalk_route **r = &atalk_routes;
58262306a36Sopenharmony_ci	int retval = 0;
58362306a36Sopenharmony_ci	struct atalk_route *tmp;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	write_lock_bh(&atalk_routes_lock);
58662306a36Sopenharmony_ci	while ((tmp = *r) != NULL) {
58762306a36Sopenharmony_ci		if (tmp->target.s_net == addr->s_net &&
58862306a36Sopenharmony_ci		    (!(tmp->flags&RTF_GATEWAY) ||
58962306a36Sopenharmony_ci		     tmp->target.s_node == addr->s_node)) {
59062306a36Sopenharmony_ci			*r = tmp->next;
59162306a36Sopenharmony_ci			dev_put(tmp->dev);
59262306a36Sopenharmony_ci			kfree(tmp);
59362306a36Sopenharmony_ci			goto out;
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci		r = &tmp->next;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci	retval = -ENOENT;
59862306a36Sopenharmony_ciout:
59962306a36Sopenharmony_ci	write_unlock_bh(&atalk_routes_lock);
60062306a36Sopenharmony_ci	return retval;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci/*
60462306a36Sopenharmony_ci * Called when a device is downed. Just throw away any routes
60562306a36Sopenharmony_ci * via it.
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_cistatic void atrtr_device_down(struct net_device *dev)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct atalk_route **r = &atalk_routes;
61062306a36Sopenharmony_ci	struct atalk_route *tmp;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	write_lock_bh(&atalk_routes_lock);
61362306a36Sopenharmony_ci	while ((tmp = *r) != NULL) {
61462306a36Sopenharmony_ci		if (tmp->dev == dev) {
61562306a36Sopenharmony_ci			*r = tmp->next;
61662306a36Sopenharmony_ci			dev_put(dev);
61762306a36Sopenharmony_ci			kfree(tmp);
61862306a36Sopenharmony_ci		} else
61962306a36Sopenharmony_ci			r = &tmp->next;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	write_unlock_bh(&atalk_routes_lock);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (atrtr_default.dev == dev)
62462306a36Sopenharmony_ci		atrtr_set_default(NULL);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/* Actually down the interface */
62862306a36Sopenharmony_cistatic inline void atalk_dev_down(struct net_device *dev)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	atrtr_device_down(dev);	/* Remove all routes for the device */
63162306a36Sopenharmony_ci	aarp_device_down(dev);	/* Remove AARP entries for the device */
63262306a36Sopenharmony_ci	atif_drop_device(dev);	/* Remove the device */
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * A device event has occurred. Watch for devices going down and
63762306a36Sopenharmony_ci * delete our use of them (iface and route).
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_cistatic int ddp_device_event(struct notifier_block *this, unsigned long event,
64062306a36Sopenharmony_ci			    void *ptr)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
64562306a36Sopenharmony_ci		return NOTIFY_DONE;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (event == NETDEV_DOWN)
64862306a36Sopenharmony_ci		/* Discard any use of this */
64962306a36Sopenharmony_ci		atalk_dev_down(dev);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return NOTIFY_DONE;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci/* ioctl calls. Shouldn't even need touching */
65562306a36Sopenharmony_ci/* Device configuration ioctl calls */
65662306a36Sopenharmony_cistatic int atif_ioctl(int cmd, void __user *arg)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF };
65962306a36Sopenharmony_ci	struct ifreq atreq;
66062306a36Sopenharmony_ci	struct atalk_netrange *nr;
66162306a36Sopenharmony_ci	struct sockaddr_at *sa;
66262306a36Sopenharmony_ci	struct net_device *dev;
66362306a36Sopenharmony_ci	struct atalk_iface *atif;
66462306a36Sopenharmony_ci	int ct;
66562306a36Sopenharmony_ci	int limit;
66662306a36Sopenharmony_ci	struct rtentry rtdef;
66762306a36Sopenharmony_ci	int add_route;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (get_user_ifreq(&atreq, NULL, arg))
67062306a36Sopenharmony_ci		return -EFAULT;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	dev = __dev_get_by_name(&init_net, atreq.ifr_name);
67362306a36Sopenharmony_ci	if (!dev)
67462306a36Sopenharmony_ci		return -ENODEV;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	sa = (struct sockaddr_at *)&atreq.ifr_addr;
67762306a36Sopenharmony_ci	atif = atalk_find_dev(dev);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	switch (cmd) {
68062306a36Sopenharmony_ci	case SIOCSIFADDR:
68162306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
68262306a36Sopenharmony_ci			return -EPERM;
68362306a36Sopenharmony_ci		if (sa->sat_family != AF_APPLETALK)
68462306a36Sopenharmony_ci			return -EINVAL;
68562306a36Sopenharmony_ci		if (dev->type != ARPHRD_ETHER &&
68662306a36Sopenharmony_ci		    dev->type != ARPHRD_LOOPBACK &&
68762306a36Sopenharmony_ci		    dev->type != ARPHRD_LOCALTLK &&
68862306a36Sopenharmony_ci		    dev->type != ARPHRD_PPP)
68962306a36Sopenharmony_ci			return -EPROTONOSUPPORT;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		nr = (struct atalk_netrange *)&sa->sat_zero[0];
69262306a36Sopenharmony_ci		add_route = 1;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		/*
69562306a36Sopenharmony_ci		 * if this is a point-to-point iface, and we already
69662306a36Sopenharmony_ci		 * have an iface for this AppleTalk address, then we
69762306a36Sopenharmony_ci		 * should not add a route
69862306a36Sopenharmony_ci		 */
69962306a36Sopenharmony_ci		if ((dev->flags & IFF_POINTOPOINT) &&
70062306a36Sopenharmony_ci		    atalk_find_interface(sa->sat_addr.s_net,
70162306a36Sopenharmony_ci					 sa->sat_addr.s_node)) {
70262306a36Sopenharmony_ci			printk(KERN_DEBUG "AppleTalk: point-to-point "
70362306a36Sopenharmony_ci			       "interface added with "
70462306a36Sopenharmony_ci			       "existing address\n");
70562306a36Sopenharmony_ci			add_route = 0;
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		/*
70962306a36Sopenharmony_ci		 * Phase 1 is fine on LocalTalk but we don't do
71062306a36Sopenharmony_ci		 * EtherTalk phase 1. Anyone wanting to add it, go ahead.
71162306a36Sopenharmony_ci		 */
71262306a36Sopenharmony_ci		if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2)
71362306a36Sopenharmony_ci			return -EPROTONOSUPPORT;
71462306a36Sopenharmony_ci		if (sa->sat_addr.s_node == ATADDR_BCAST ||
71562306a36Sopenharmony_ci		    sa->sat_addr.s_node == 254)
71662306a36Sopenharmony_ci			return -EINVAL;
71762306a36Sopenharmony_ci		if (atif) {
71862306a36Sopenharmony_ci			/* Already setting address */
71962306a36Sopenharmony_ci			if (atif->status & ATIF_PROBE)
72062306a36Sopenharmony_ci				return -EBUSY;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci			atif->address.s_net  = sa->sat_addr.s_net;
72362306a36Sopenharmony_ci			atif->address.s_node = sa->sat_addr.s_node;
72462306a36Sopenharmony_ci			atrtr_device_down(dev);	/* Flush old routes */
72562306a36Sopenharmony_ci		} else {
72662306a36Sopenharmony_ci			atif = atif_add_device(dev, &sa->sat_addr);
72762306a36Sopenharmony_ci			if (!atif)
72862306a36Sopenharmony_ci				return -ENOMEM;
72962306a36Sopenharmony_ci		}
73062306a36Sopenharmony_ci		atif->nets = *nr;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		/*
73362306a36Sopenharmony_ci		 * Check if the chosen address is used. If so we
73462306a36Sopenharmony_ci		 * error and atalkd will try another.
73562306a36Sopenharmony_ci		 */
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		if (!(dev->flags & IFF_LOOPBACK) &&
73862306a36Sopenharmony_ci		    !(dev->flags & IFF_POINTOPOINT) &&
73962306a36Sopenharmony_ci		    atif_probe_device(atif) < 0) {
74062306a36Sopenharmony_ci			atif_drop_device(dev);
74162306a36Sopenharmony_ci			return -EADDRINUSE;
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		/* Hey it worked - add the direct routes */
74562306a36Sopenharmony_ci		sa = (struct sockaddr_at *)&rtdef.rt_gateway;
74662306a36Sopenharmony_ci		sa->sat_family = AF_APPLETALK;
74762306a36Sopenharmony_ci		sa->sat_addr.s_net  = atif->address.s_net;
74862306a36Sopenharmony_ci		sa->sat_addr.s_node = atif->address.s_node;
74962306a36Sopenharmony_ci		sa = (struct sockaddr_at *)&rtdef.rt_dst;
75062306a36Sopenharmony_ci		rtdef.rt_flags = RTF_UP;
75162306a36Sopenharmony_ci		sa->sat_family = AF_APPLETALK;
75262306a36Sopenharmony_ci		sa->sat_addr.s_node = ATADDR_ANYNODE;
75362306a36Sopenharmony_ci		if (dev->flags & IFF_LOOPBACK ||
75462306a36Sopenharmony_ci		    dev->flags & IFF_POINTOPOINT)
75562306a36Sopenharmony_ci			rtdef.rt_flags |= RTF_HOST;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		/* Routerless initial state */
75862306a36Sopenharmony_ci		if (nr->nr_firstnet == htons(0) &&
75962306a36Sopenharmony_ci		    nr->nr_lastnet == htons(0xFFFE)) {
76062306a36Sopenharmony_ci			sa->sat_addr.s_net = atif->address.s_net;
76162306a36Sopenharmony_ci			atrtr_create(&rtdef, dev);
76262306a36Sopenharmony_ci			atrtr_set_default(dev);
76362306a36Sopenharmony_ci		} else {
76462306a36Sopenharmony_ci			limit = ntohs(nr->nr_lastnet);
76562306a36Sopenharmony_ci			if (limit - ntohs(nr->nr_firstnet) > 4096) {
76662306a36Sopenharmony_ci				printk(KERN_WARNING "Too many routes/"
76762306a36Sopenharmony_ci				       "iface.\n");
76862306a36Sopenharmony_ci				return -EINVAL;
76962306a36Sopenharmony_ci			}
77062306a36Sopenharmony_ci			if (add_route)
77162306a36Sopenharmony_ci				for (ct = ntohs(nr->nr_firstnet);
77262306a36Sopenharmony_ci				     ct <= limit; ct++) {
77362306a36Sopenharmony_ci					sa->sat_addr.s_net = htons(ct);
77462306a36Sopenharmony_ci					atrtr_create(&rtdef, dev);
77562306a36Sopenharmony_ci				}
77662306a36Sopenharmony_ci		}
77762306a36Sopenharmony_ci		dev_mc_add_global(dev, aarp_mcast);
77862306a36Sopenharmony_ci		return 0;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	case SIOCGIFADDR:
78162306a36Sopenharmony_ci		if (!atif)
78262306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		sa->sat_family = AF_APPLETALK;
78562306a36Sopenharmony_ci		sa->sat_addr = atif->address;
78662306a36Sopenharmony_ci		break;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	case SIOCGIFBRDADDR:
78962306a36Sopenharmony_ci		if (!atif)
79062306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		sa->sat_family = AF_APPLETALK;
79362306a36Sopenharmony_ci		sa->sat_addr.s_net = atif->address.s_net;
79462306a36Sopenharmony_ci		sa->sat_addr.s_node = ATADDR_BCAST;
79562306a36Sopenharmony_ci		break;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	case SIOCATALKDIFADDR:
79862306a36Sopenharmony_ci	case SIOCDIFADDR:
79962306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
80062306a36Sopenharmony_ci			return -EPERM;
80162306a36Sopenharmony_ci		if (sa->sat_family != AF_APPLETALK)
80262306a36Sopenharmony_ci			return -EINVAL;
80362306a36Sopenharmony_ci		atalk_dev_down(dev);
80462306a36Sopenharmony_ci		break;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	case SIOCSARP:
80762306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
80862306a36Sopenharmony_ci			return -EPERM;
80962306a36Sopenharmony_ci		if (sa->sat_family != AF_APPLETALK)
81062306a36Sopenharmony_ci			return -EINVAL;
81162306a36Sopenharmony_ci		/*
81262306a36Sopenharmony_ci		 * for now, we only support proxy AARP on ELAP;
81362306a36Sopenharmony_ci		 * we should be able to do it for LocalTalk, too.
81462306a36Sopenharmony_ci		 */
81562306a36Sopenharmony_ci		if (dev->type != ARPHRD_ETHER)
81662306a36Sopenharmony_ci			return -EPROTONOSUPPORT;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		/*
81962306a36Sopenharmony_ci		 * atif points to the current interface on this network;
82062306a36Sopenharmony_ci		 * we aren't concerned about its current status (at
82162306a36Sopenharmony_ci		 * least for now), but it has all the settings about
82262306a36Sopenharmony_ci		 * the network we're going to probe. Consequently, it
82362306a36Sopenharmony_ci		 * must exist.
82462306a36Sopenharmony_ci		 */
82562306a36Sopenharmony_ci		if (!atif)
82662306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		nr = (struct atalk_netrange *)&(atif->nets);
82962306a36Sopenharmony_ci		/*
83062306a36Sopenharmony_ci		 * Phase 1 is fine on Localtalk but we don't do
83162306a36Sopenharmony_ci		 * Ethertalk phase 1. Anyone wanting to add it, go ahead.
83262306a36Sopenharmony_ci		 */
83362306a36Sopenharmony_ci		if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2)
83462306a36Sopenharmony_ci			return -EPROTONOSUPPORT;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci		if (sa->sat_addr.s_node == ATADDR_BCAST ||
83762306a36Sopenharmony_ci		    sa->sat_addr.s_node == 254)
83862306a36Sopenharmony_ci			return -EINVAL;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		/*
84162306a36Sopenharmony_ci		 * Check if the chosen address is used. If so we
84262306a36Sopenharmony_ci		 * error and ATCP will try another.
84362306a36Sopenharmony_ci		 */
84462306a36Sopenharmony_ci		if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0)
84562306a36Sopenharmony_ci			return -EADDRINUSE;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		/*
84862306a36Sopenharmony_ci		 * We now have an address on the local network, and
84962306a36Sopenharmony_ci		 * the AARP code will defend it for us until we take it
85062306a36Sopenharmony_ci		 * down. We don't set up any routes right now, because
85162306a36Sopenharmony_ci		 * ATCP will install them manually via SIOCADDRT.
85262306a36Sopenharmony_ci		 */
85362306a36Sopenharmony_ci		break;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	case SIOCDARP:
85662306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
85762306a36Sopenharmony_ci			return -EPERM;
85862306a36Sopenharmony_ci		if (sa->sat_family != AF_APPLETALK)
85962306a36Sopenharmony_ci			return -EINVAL;
86062306a36Sopenharmony_ci		if (!atif)
86162306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		/* give to aarp module to remove proxy entry */
86462306a36Sopenharmony_ci		aarp_proxy_remove(atif->dev, &(sa->sat_addr));
86562306a36Sopenharmony_ci		return 0;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	return put_user_ifreq(&atreq, arg);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int atrtr_ioctl_addrt(struct rtentry *rt)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct net_device *dev = NULL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (rt->rt_dev) {
87662306a36Sopenharmony_ci		char name[IFNAMSIZ];
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		if (copy_from_user(name, rt->rt_dev, IFNAMSIZ-1))
87962306a36Sopenharmony_ci			return -EFAULT;
88062306a36Sopenharmony_ci		name[IFNAMSIZ-1] = '\0';
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		dev = __dev_get_by_name(&init_net, name);
88362306a36Sopenharmony_ci		if (!dev)
88462306a36Sopenharmony_ci			return -ENODEV;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci	return atrtr_create(rt, dev);
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci/* Routing ioctl() calls */
89062306a36Sopenharmony_cistatic int atrtr_ioctl(unsigned int cmd, void __user *arg)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct rtentry rt;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (copy_from_user(&rt, arg, sizeof(rt)))
89562306a36Sopenharmony_ci		return -EFAULT;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	switch (cmd) {
89862306a36Sopenharmony_ci	case SIOCDELRT:
89962306a36Sopenharmony_ci		if (rt.rt_dst.sa_family != AF_APPLETALK)
90062306a36Sopenharmony_ci			return -EINVAL;
90162306a36Sopenharmony_ci		return atrtr_delete(&((struct sockaddr_at *)
90262306a36Sopenharmony_ci				      &rt.rt_dst)->sat_addr);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	case SIOCADDRT:
90562306a36Sopenharmony_ci		return atrtr_ioctl_addrt(&rt);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	return -EINVAL;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/**************************************************************************\
91162306a36Sopenharmony_ci*                                                                          *
91262306a36Sopenharmony_ci* Handling for system calls applied via the various interfaces to an       *
91362306a36Sopenharmony_ci* AppleTalk socket object.                                                 *
91462306a36Sopenharmony_ci*                                                                          *
91562306a36Sopenharmony_ci\**************************************************************************/
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/*
91862306a36Sopenharmony_ci * Checksum: This is 'optional'. It's quite likely also a good
91962306a36Sopenharmony_ci * candidate for assembler hackery 8)
92062306a36Sopenharmony_ci */
92162306a36Sopenharmony_cistatic unsigned long atalk_sum_partial(const unsigned char *data,
92262306a36Sopenharmony_ci				       int len, unsigned long sum)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	/* This ought to be unwrapped neatly. I'll trust gcc for now */
92562306a36Sopenharmony_ci	while (len--) {
92662306a36Sopenharmony_ci		sum += *data++;
92762306a36Sopenharmony_ci		sum = rol16(sum, 1);
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci	return sum;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci/*  Checksum skb data --  similar to skb_checksum  */
93362306a36Sopenharmony_cistatic unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
93462306a36Sopenharmony_ci				   int len, unsigned long sum)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	int start = skb_headlen(skb);
93762306a36Sopenharmony_ci	struct sk_buff *frag_iter;
93862306a36Sopenharmony_ci	int i, copy;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* checksum stuff in header space */
94162306a36Sopenharmony_ci	if ((copy = start - offset) > 0) {
94262306a36Sopenharmony_ci		if (copy > len)
94362306a36Sopenharmony_ci			copy = len;
94462306a36Sopenharmony_ci		sum = atalk_sum_partial(skb->data + offset, copy, sum);
94562306a36Sopenharmony_ci		if ((len -= copy) == 0)
94662306a36Sopenharmony_ci			return sum;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		offset += copy;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/* checksum stuff in frags */
95262306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
95362306a36Sopenharmony_ci		int end;
95462306a36Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
95562306a36Sopenharmony_ci		WARN_ON(start > offset + len);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		end = start + skb_frag_size(frag);
95862306a36Sopenharmony_ci		if ((copy = end - offset) > 0) {
95962306a36Sopenharmony_ci			u8 *vaddr;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci			if (copy > len)
96262306a36Sopenharmony_ci				copy = len;
96362306a36Sopenharmony_ci			vaddr = kmap_atomic(skb_frag_page(frag));
96462306a36Sopenharmony_ci			sum = atalk_sum_partial(vaddr + skb_frag_off(frag) +
96562306a36Sopenharmony_ci						offset - start, copy, sum);
96662306a36Sopenharmony_ci			kunmap_atomic(vaddr);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci			if (!(len -= copy))
96962306a36Sopenharmony_ci				return sum;
97062306a36Sopenharmony_ci			offset += copy;
97162306a36Sopenharmony_ci		}
97262306a36Sopenharmony_ci		start = end;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	skb_walk_frags(skb, frag_iter) {
97662306a36Sopenharmony_ci		int end;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		WARN_ON(start > offset + len);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		end = start + frag_iter->len;
98162306a36Sopenharmony_ci		if ((copy = end - offset) > 0) {
98262306a36Sopenharmony_ci			if (copy > len)
98362306a36Sopenharmony_ci				copy = len;
98462306a36Sopenharmony_ci			sum = atalk_sum_skb(frag_iter, offset - start,
98562306a36Sopenharmony_ci					    copy, sum);
98662306a36Sopenharmony_ci			if ((len -= copy) == 0)
98762306a36Sopenharmony_ci				return sum;
98862306a36Sopenharmony_ci			offset += copy;
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci		start = end;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	BUG_ON(len > 0);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return sum;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic __be16 atalk_checksum(const struct sk_buff *skb, int len)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	unsigned long sum;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* skip header 4 bytes */
100362306a36Sopenharmony_ci	sum = atalk_sum_skb(skb, 4, len-4, 0);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	/* Use 0xFFFF for 0. 0 itself means none */
100662306a36Sopenharmony_ci	return sum ? htons((unsigned short)sum) : htons(0xFFFF);
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic struct proto ddp_proto = {
101062306a36Sopenharmony_ci	.name	  = "DDP",
101162306a36Sopenharmony_ci	.owner	  = THIS_MODULE,
101262306a36Sopenharmony_ci	.obj_size = sizeof(struct atalk_sock),
101362306a36Sopenharmony_ci};
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci/*
101662306a36Sopenharmony_ci * Create a socket. Initialise the socket, blank the addresses
101762306a36Sopenharmony_ci * set the state.
101862306a36Sopenharmony_ci */
101962306a36Sopenharmony_cistatic int atalk_create(struct net *net, struct socket *sock, int protocol,
102062306a36Sopenharmony_ci			int kern)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct sock *sk;
102362306a36Sopenharmony_ci	int rc = -ESOCKTNOSUPPORT;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (!net_eq(net, &init_net))
102662306a36Sopenharmony_ci		return -EAFNOSUPPORT;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/*
102962306a36Sopenharmony_ci	 * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do
103062306a36Sopenharmony_ci	 * and gives you the full ELAP frame. Should be handy for CAP 8)
103162306a36Sopenharmony_ci	 */
103262306a36Sopenharmony_ci	if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
103362306a36Sopenharmony_ci		goto out;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	rc = -EPERM;
103662306a36Sopenharmony_ci	if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
103762306a36Sopenharmony_ci		goto out;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	rc = -ENOMEM;
104062306a36Sopenharmony_ci	sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto, kern);
104162306a36Sopenharmony_ci	if (!sk)
104262306a36Sopenharmony_ci		goto out;
104362306a36Sopenharmony_ci	rc = 0;
104462306a36Sopenharmony_ci	sock->ops = &atalk_dgram_ops;
104562306a36Sopenharmony_ci	sock_init_data(sock, sk);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* Checksums on by default */
104862306a36Sopenharmony_ci	sock_set_flag(sk, SOCK_ZAPPED);
104962306a36Sopenharmony_ciout:
105062306a36Sopenharmony_ci	return rc;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci/* Free a socket. No work needed */
105462306a36Sopenharmony_cistatic int atalk_release(struct socket *sock)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct sock *sk = sock->sk;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (sk) {
105962306a36Sopenharmony_ci		sock_hold(sk);
106062306a36Sopenharmony_ci		lock_sock(sk);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		sock_orphan(sk);
106362306a36Sopenharmony_ci		sock->sk = NULL;
106462306a36Sopenharmony_ci		atalk_destroy_socket(sk);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci		release_sock(sk);
106762306a36Sopenharmony_ci		sock_put(sk);
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci	return 0;
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci/**
107362306a36Sopenharmony_ci * atalk_pick_and_bind_port - Pick a source port when one is not given
107462306a36Sopenharmony_ci * @sk: socket to insert into the tables
107562306a36Sopenharmony_ci * @sat: address to search for
107662306a36Sopenharmony_ci *
107762306a36Sopenharmony_ci * Pick a source port when one is not given. If we can find a suitable free
107862306a36Sopenharmony_ci * one, we insert the socket into the tables using it.
107962306a36Sopenharmony_ci *
108062306a36Sopenharmony_ci * This whole operation must be atomic.
108162306a36Sopenharmony_ci */
108262306a36Sopenharmony_cistatic int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	int retval;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	write_lock_bh(&atalk_sockets_lock);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	for (sat->sat_port = ATPORT_RESERVED;
108962306a36Sopenharmony_ci	     sat->sat_port < ATPORT_LAST;
109062306a36Sopenharmony_ci	     sat->sat_port++) {
109162306a36Sopenharmony_ci		struct sock *s;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		sk_for_each(s, &atalk_sockets) {
109462306a36Sopenharmony_ci			struct atalk_sock *at = at_sk(s);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci			if (at->src_net == sat->sat_addr.s_net &&
109762306a36Sopenharmony_ci			    at->src_node == sat->sat_addr.s_node &&
109862306a36Sopenharmony_ci			    at->src_port == sat->sat_port)
109962306a36Sopenharmony_ci				goto try_next_port;
110062306a36Sopenharmony_ci		}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci		/* Wheee, it's free, assign and insert. */
110362306a36Sopenharmony_ci		__atalk_insert_socket(sk);
110462306a36Sopenharmony_ci		at_sk(sk)->src_port = sat->sat_port;
110562306a36Sopenharmony_ci		retval = 0;
110662306a36Sopenharmony_ci		goto out;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_citry_next_port:;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	retval = -EBUSY;
111262306a36Sopenharmony_ciout:
111362306a36Sopenharmony_ci	write_unlock_bh(&atalk_sockets_lock);
111462306a36Sopenharmony_ci	return retval;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic int atalk_autobind(struct sock *sk)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct atalk_sock *at = at_sk(sk);
112062306a36Sopenharmony_ci	struct sockaddr_at sat;
112162306a36Sopenharmony_ci	struct atalk_addr *ap = atalk_find_primary();
112262306a36Sopenharmony_ci	int n = -EADDRNOTAVAIL;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (!ap || ap->s_net == htons(ATADDR_ANYNET))
112562306a36Sopenharmony_ci		goto out;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	at->src_net  = sat.sat_addr.s_net  = ap->s_net;
112862306a36Sopenharmony_ci	at->src_node = sat.sat_addr.s_node = ap->s_node;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	n = atalk_pick_and_bind_port(sk, &sat);
113162306a36Sopenharmony_ci	if (!n)
113262306a36Sopenharmony_ci		sock_reset_flag(sk, SOCK_ZAPPED);
113362306a36Sopenharmony_ciout:
113462306a36Sopenharmony_ci	return n;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/* Set the address 'our end' of the connection */
113862306a36Sopenharmony_cistatic int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	struct sockaddr_at *addr = (struct sockaddr_at *)uaddr;
114162306a36Sopenharmony_ci	struct sock *sk = sock->sk;
114262306a36Sopenharmony_ci	struct atalk_sock *at = at_sk(sk);
114362306a36Sopenharmony_ci	int err;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (!sock_flag(sk, SOCK_ZAPPED) ||
114662306a36Sopenharmony_ci	    addr_len != sizeof(struct sockaddr_at))
114762306a36Sopenharmony_ci		return -EINVAL;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (addr->sat_family != AF_APPLETALK)
115062306a36Sopenharmony_ci		return -EAFNOSUPPORT;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	lock_sock(sk);
115362306a36Sopenharmony_ci	if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) {
115462306a36Sopenharmony_ci		struct atalk_addr *ap = atalk_find_primary();
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		err = -EADDRNOTAVAIL;
115762306a36Sopenharmony_ci		if (!ap)
115862306a36Sopenharmony_ci			goto out;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci		at->src_net  = addr->sat_addr.s_net = ap->s_net;
116162306a36Sopenharmony_ci		at->src_node = addr->sat_addr.s_node = ap->s_node;
116262306a36Sopenharmony_ci	} else {
116362306a36Sopenharmony_ci		err = -EADDRNOTAVAIL;
116462306a36Sopenharmony_ci		if (!atalk_find_interface(addr->sat_addr.s_net,
116562306a36Sopenharmony_ci					  addr->sat_addr.s_node))
116662306a36Sopenharmony_ci			goto out;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		at->src_net  = addr->sat_addr.s_net;
116962306a36Sopenharmony_ci		at->src_node = addr->sat_addr.s_node;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (addr->sat_port == ATADDR_ANYPORT) {
117362306a36Sopenharmony_ci		err = atalk_pick_and_bind_port(sk, addr);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci		if (err < 0)
117662306a36Sopenharmony_ci			goto out;
117762306a36Sopenharmony_ci	} else {
117862306a36Sopenharmony_ci		at->src_port = addr->sat_port;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci		err = -EADDRINUSE;
118162306a36Sopenharmony_ci		if (atalk_find_or_insert_socket(sk, addr))
118262306a36Sopenharmony_ci			goto out;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	sock_reset_flag(sk, SOCK_ZAPPED);
118662306a36Sopenharmony_ci	err = 0;
118762306a36Sopenharmony_ciout:
118862306a36Sopenharmony_ci	release_sock(sk);
118962306a36Sopenharmony_ci	return err;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci/* Set the address we talk to */
119362306a36Sopenharmony_cistatic int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
119462306a36Sopenharmony_ci			 int addr_len, int flags)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct sock *sk = sock->sk;
119762306a36Sopenharmony_ci	struct atalk_sock *at = at_sk(sk);
119862306a36Sopenharmony_ci	struct sockaddr_at *addr;
119962306a36Sopenharmony_ci	int err;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	sk->sk_state   = TCP_CLOSE;
120262306a36Sopenharmony_ci	sock->state = SS_UNCONNECTED;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (addr_len != sizeof(*addr))
120562306a36Sopenharmony_ci		return -EINVAL;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	addr = (struct sockaddr_at *)uaddr;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	if (addr->sat_family != AF_APPLETALK)
121062306a36Sopenharmony_ci		return -EAFNOSUPPORT;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (addr->sat_addr.s_node == ATADDR_BCAST &&
121362306a36Sopenharmony_ci	    !sock_flag(sk, SOCK_BROADCAST)) {
121462306a36Sopenharmony_ci#if 1
121562306a36Sopenharmony_ci		pr_warn("atalk_connect: %s is broken and did not set SO_BROADCAST.\n",
121662306a36Sopenharmony_ci			current->comm);
121762306a36Sopenharmony_ci#else
121862306a36Sopenharmony_ci		return -EACCES;
121962306a36Sopenharmony_ci#endif
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	lock_sock(sk);
122362306a36Sopenharmony_ci	err = -EBUSY;
122462306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_ZAPPED))
122562306a36Sopenharmony_ci		if (atalk_autobind(sk) < 0)
122662306a36Sopenharmony_ci			goto out;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	err = -ENETUNREACH;
122962306a36Sopenharmony_ci	if (!atrtr_get_dev(&addr->sat_addr))
123062306a36Sopenharmony_ci		goto out;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	at->dest_port = addr->sat_port;
123362306a36Sopenharmony_ci	at->dest_net  = addr->sat_addr.s_net;
123462306a36Sopenharmony_ci	at->dest_node = addr->sat_addr.s_node;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	sock->state  = SS_CONNECTED;
123762306a36Sopenharmony_ci	sk->sk_state = TCP_ESTABLISHED;
123862306a36Sopenharmony_ci	err = 0;
123962306a36Sopenharmony_ciout:
124062306a36Sopenharmony_ci	release_sock(sk);
124162306a36Sopenharmony_ci	return err;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci/*
124562306a36Sopenharmony_ci * Find the name of an AppleTalk socket. Just copy the right
124662306a36Sopenharmony_ci * fields into the sockaddr.
124762306a36Sopenharmony_ci */
124862306a36Sopenharmony_cistatic int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
124962306a36Sopenharmony_ci			 int peer)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	struct sockaddr_at sat;
125262306a36Sopenharmony_ci	struct sock *sk = sock->sk;
125362306a36Sopenharmony_ci	struct atalk_sock *at = at_sk(sk);
125462306a36Sopenharmony_ci	int err;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	lock_sock(sk);
125762306a36Sopenharmony_ci	err = -ENOBUFS;
125862306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_ZAPPED))
125962306a36Sopenharmony_ci		if (atalk_autobind(sk) < 0)
126062306a36Sopenharmony_ci			goto out;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	memset(&sat, 0, sizeof(sat));
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (peer) {
126562306a36Sopenharmony_ci		err = -ENOTCONN;
126662306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED)
126762306a36Sopenharmony_ci			goto out;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		sat.sat_addr.s_net  = at->dest_net;
127062306a36Sopenharmony_ci		sat.sat_addr.s_node = at->dest_node;
127162306a36Sopenharmony_ci		sat.sat_port	    = at->dest_port;
127262306a36Sopenharmony_ci	} else {
127362306a36Sopenharmony_ci		sat.sat_addr.s_net  = at->src_net;
127462306a36Sopenharmony_ci		sat.sat_addr.s_node = at->src_node;
127562306a36Sopenharmony_ci		sat.sat_port	    = at->src_port;
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	sat.sat_family = AF_APPLETALK;
127962306a36Sopenharmony_ci	memcpy(uaddr, &sat, sizeof(sat));
128062306a36Sopenharmony_ci	err = sizeof(struct sockaddr_at);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ciout:
128362306a36Sopenharmony_ci	release_sock(sk);
128462306a36Sopenharmony_ci	return err;
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPDDP)
128862306a36Sopenharmony_cistatic __inline__ int is_ip_over_ddp(struct sk_buff *skb)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	return skb->data[12] == 22;
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_cistatic int handle_ip_over_ddp(struct sk_buff *skb)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0");
129662306a36Sopenharmony_ci	struct net_device_stats *stats;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* This needs to be able to handle ipddp"N" devices */
129962306a36Sopenharmony_ci	if (!dev) {
130062306a36Sopenharmony_ci		kfree_skb(skb);
130162306a36Sopenharmony_ci		return NET_RX_DROP;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
130562306a36Sopenharmony_ci	skb_pull(skb, 13);
130662306a36Sopenharmony_ci	skb->dev   = dev;
130762306a36Sopenharmony_ci	skb_reset_transport_header(skb);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	stats = netdev_priv(dev);
131062306a36Sopenharmony_ci	stats->rx_packets++;
131162306a36Sopenharmony_ci	stats->rx_bytes += skb->len + 13;
131262306a36Sopenharmony_ci	return netif_rx(skb);  /* Send the SKB up to a higher place. */
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci#else
131562306a36Sopenharmony_ci/* make it easy for gcc to optimize this test out, i.e. kill the code */
131662306a36Sopenharmony_ci#define is_ip_over_ddp(skb) 0
131762306a36Sopenharmony_ci#define handle_ip_over_ddp(skb) 0
131862306a36Sopenharmony_ci#endif
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic int atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
132162306a36Sopenharmony_ci			      struct ddpehdr *ddp, __u16 len_hops, int origlen)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct atalk_route *rt;
132462306a36Sopenharmony_ci	struct atalk_addr ta;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/*
132762306a36Sopenharmony_ci	 * Don't route multicast, etc., packets, or packets sent to "this
132862306a36Sopenharmony_ci	 * network"
132962306a36Sopenharmony_ci	 */
133062306a36Sopenharmony_ci	if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) {
133162306a36Sopenharmony_ci		/*
133262306a36Sopenharmony_ci		 * FIXME:
133362306a36Sopenharmony_ci		 *
133462306a36Sopenharmony_ci		 * Can it ever happen that a packet is from a PPP iface and
133562306a36Sopenharmony_ci		 * needs to be broadcast onto the default network?
133662306a36Sopenharmony_ci		 */
133762306a36Sopenharmony_ci		if (dev->type == ARPHRD_PPP)
133862306a36Sopenharmony_ci			printk(KERN_DEBUG "AppleTalk: didn't forward broadcast "
133962306a36Sopenharmony_ci					  "packet received from PPP iface\n");
134062306a36Sopenharmony_ci		goto free_it;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	ta.s_net  = ddp->deh_dnet;
134462306a36Sopenharmony_ci	ta.s_node = ddp->deh_dnode;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/* Route the packet */
134762306a36Sopenharmony_ci	rt = atrtr_find(&ta);
134862306a36Sopenharmony_ci	/* increment hops count */
134962306a36Sopenharmony_ci	len_hops += 1 << 10;
135062306a36Sopenharmony_ci	if (!rt || !(len_hops & (15 << 10)))
135162306a36Sopenharmony_ci		goto free_it;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	/* FIXME: use skb->cb to be able to use shared skbs */
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/*
135662306a36Sopenharmony_ci	 * Route goes through another gateway, so set the target to the
135762306a36Sopenharmony_ci	 * gateway instead.
135862306a36Sopenharmony_ci	 */
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (rt->flags & RTF_GATEWAY) {
136162306a36Sopenharmony_ci		ta.s_net  = rt->gateway.s_net;
136262306a36Sopenharmony_ci		ta.s_node = rt->gateway.s_node;
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	/* Fix up skb->len field */
136662306a36Sopenharmony_ci	skb_trim(skb, min_t(unsigned int, origlen,
136762306a36Sopenharmony_ci			    (rt->dev->hard_header_len +
136862306a36Sopenharmony_ci			     ddp_dl->header_length + (len_hops & 1023))));
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	/* FIXME: use skb->cb to be able to use shared skbs */
137162306a36Sopenharmony_ci	ddp->deh_len_hops = htons(len_hops);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	/*
137462306a36Sopenharmony_ci	 * Send the buffer onwards
137562306a36Sopenharmony_ci	 *
137662306a36Sopenharmony_ci	 * Now we must always be careful. If it's come from LocalTalk to
137762306a36Sopenharmony_ci	 * EtherTalk it might not fit
137862306a36Sopenharmony_ci	 *
137962306a36Sopenharmony_ci	 * Order matters here: If a packet has to be copied to make a new
138062306a36Sopenharmony_ci	 * headroom (rare hopefully) then it won't need unsharing.
138162306a36Sopenharmony_ci	 *
138262306a36Sopenharmony_ci	 * Note. ddp-> becomes invalid at the realloc.
138362306a36Sopenharmony_ci	 */
138462306a36Sopenharmony_ci	if (skb_headroom(skb) < 22) {
138562306a36Sopenharmony_ci		/* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */
138662306a36Sopenharmony_ci		struct sk_buff *nskb = skb_realloc_headroom(skb, 32);
138762306a36Sopenharmony_ci		kfree_skb(skb);
138862306a36Sopenharmony_ci		skb = nskb;
138962306a36Sopenharmony_ci	} else
139062306a36Sopenharmony_ci		skb = skb_unshare(skb, GFP_ATOMIC);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/*
139362306a36Sopenharmony_ci	 * If the buffer didn't vanish into the lack of space bitbucket we can
139462306a36Sopenharmony_ci	 * send it.
139562306a36Sopenharmony_ci	 */
139662306a36Sopenharmony_ci	if (skb == NULL)
139762306a36Sopenharmony_ci		goto drop;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP)
140062306a36Sopenharmony_ci		return NET_RX_DROP;
140162306a36Sopenharmony_ci	return NET_RX_SUCCESS;
140262306a36Sopenharmony_cifree_it:
140362306a36Sopenharmony_ci	kfree_skb(skb);
140462306a36Sopenharmony_cidrop:
140562306a36Sopenharmony_ci	return NET_RX_DROP;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci/**
140962306a36Sopenharmony_ci *	atalk_rcv - Receive a packet (in skb) from device dev
141062306a36Sopenharmony_ci *	@skb: packet received
141162306a36Sopenharmony_ci *	@dev: network device where the packet comes from
141262306a36Sopenharmony_ci *	@pt: packet type
141362306a36Sopenharmony_ci *	@orig_dev: the original receive net device
141462306a36Sopenharmony_ci *
141562306a36Sopenharmony_ci *	Receive a packet (in skb) from device dev. This has come from the SNAP
141662306a36Sopenharmony_ci *	decoder, and on entry skb->transport_header is the DDP header, skb->len
141762306a36Sopenharmony_ci *	is the DDP header, skb->len is the DDP length. The physical headers
141862306a36Sopenharmony_ci *	have been extracted. PPP should probably pass frames marked as for this
141962306a36Sopenharmony_ci *	layer.  [ie ARPHRD_ETHERTALK]
142062306a36Sopenharmony_ci */
142162306a36Sopenharmony_cistatic int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
142262306a36Sopenharmony_ci		     struct packet_type *pt, struct net_device *orig_dev)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	struct ddpehdr *ddp;
142562306a36Sopenharmony_ci	struct sock *sock;
142662306a36Sopenharmony_ci	struct atalk_iface *atif;
142762306a36Sopenharmony_ci	struct sockaddr_at tosat;
142862306a36Sopenharmony_ci	int origlen;
142962306a36Sopenharmony_ci	__u16 len_hops;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
143262306a36Sopenharmony_ci		goto drop;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	/* Don't mangle buffer if shared */
143562306a36Sopenharmony_ci	if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
143662306a36Sopenharmony_ci		goto out;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* Size check and make sure header is contiguous */
143962306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*ddp)))
144062306a36Sopenharmony_ci		goto drop;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	ddp = ddp_hdr(skb);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	len_hops = ntohs(ddp->deh_len_hops);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* Trim buffer in case of stray trailing data */
144762306a36Sopenharmony_ci	origlen = skb->len;
144862306a36Sopenharmony_ci	skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023));
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/*
145162306a36Sopenharmony_ci	 * Size check to see if ddp->deh_len was crap
145262306a36Sopenharmony_ci	 * (Otherwise we'll detonate most spectacularly
145362306a36Sopenharmony_ci	 * in the middle of atalk_checksum() or recvmsg()).
145462306a36Sopenharmony_ci	 */
145562306a36Sopenharmony_ci	if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) {
145662306a36Sopenharmony_ci		pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, "
145762306a36Sopenharmony_ci			 "skb->len=%u)\n", len_hops & 1023, skb->len);
145862306a36Sopenharmony_ci		goto drop;
145962306a36Sopenharmony_ci	}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	/*
146262306a36Sopenharmony_ci	 * Any checksums. Note we don't do htons() on this == is assumed to be
146362306a36Sopenharmony_ci	 * valid for net byte orders all over the networking code...
146462306a36Sopenharmony_ci	 */
146562306a36Sopenharmony_ci	if (ddp->deh_sum &&
146662306a36Sopenharmony_ci	    atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum)
146762306a36Sopenharmony_ci		/* Not a valid AppleTalk frame - dustbin time */
146862306a36Sopenharmony_ci		goto drop;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/* Check the packet is aimed at us */
147162306a36Sopenharmony_ci	if (!ddp->deh_dnet)	/* Net 0 is 'this network' */
147262306a36Sopenharmony_ci		atif = atalk_find_anynet(ddp->deh_dnode, dev);
147362306a36Sopenharmony_ci	else
147462306a36Sopenharmony_ci		atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	if (!atif) {
147762306a36Sopenharmony_ci		/* Not ours, so we route the packet via the correct
147862306a36Sopenharmony_ci		 * AppleTalk iface
147962306a36Sopenharmony_ci		 */
148062306a36Sopenharmony_ci		return atalk_route_packet(skb, dev, ddp, len_hops, origlen);
148162306a36Sopenharmony_ci	}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	/* if IP over DDP is not selected this code will be optimized out */
148462306a36Sopenharmony_ci	if (is_ip_over_ddp(skb))
148562306a36Sopenharmony_ci		return handle_ip_over_ddp(skb);
148662306a36Sopenharmony_ci	/*
148762306a36Sopenharmony_ci	 * Which socket - atalk_search_socket() looks for a *full match*
148862306a36Sopenharmony_ci	 * of the <net, node, port> tuple.
148962306a36Sopenharmony_ci	 */
149062306a36Sopenharmony_ci	tosat.sat_addr.s_net  = ddp->deh_dnet;
149162306a36Sopenharmony_ci	tosat.sat_addr.s_node = ddp->deh_dnode;
149262306a36Sopenharmony_ci	tosat.sat_port	      = ddp->deh_dport;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	sock = atalk_search_socket(&tosat, atif);
149562306a36Sopenharmony_ci	if (!sock) /* But not one of our sockets */
149662306a36Sopenharmony_ci		goto drop;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	/* Queue packet (standard) */
149962306a36Sopenharmony_ci	if (sock_queue_rcv_skb(sock, skb) < 0)
150062306a36Sopenharmony_ci		goto drop;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	return NET_RX_SUCCESS;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cidrop:
150562306a36Sopenharmony_ci	kfree_skb(skb);
150662306a36Sopenharmony_ciout:
150762306a36Sopenharmony_ci	return NET_RX_DROP;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci/*
151262306a36Sopenharmony_ci * Receive a LocalTalk frame. We make some demands on the caller here.
151362306a36Sopenharmony_ci * Caller must provide enough headroom on the packet to pull the short
151462306a36Sopenharmony_ci * header and append a long one.
151562306a36Sopenharmony_ci */
151662306a36Sopenharmony_cistatic int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
151762306a36Sopenharmony_ci		     struct packet_type *pt, struct net_device *orig_dev)
151862306a36Sopenharmony_ci{
151962306a36Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
152062306a36Sopenharmony_ci		goto freeit;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/* Expand any short form frames */
152362306a36Sopenharmony_ci	if (skb_mac_header(skb)[2] == 1) {
152462306a36Sopenharmony_ci		struct ddpehdr *ddp;
152562306a36Sopenharmony_ci		/* Find our address */
152662306a36Sopenharmony_ci		struct atalk_addr *ap = atalk_find_dev_addr(dev);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci		if (!ap || skb->len < sizeof(__be16) || skb->len > 1023)
152962306a36Sopenharmony_ci			goto freeit;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci		/* Don't mangle buffer if shared */
153262306a36Sopenharmony_ci		if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
153362306a36Sopenharmony_ci			return 0;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci		/*
153662306a36Sopenharmony_ci		 * The push leaves us with a ddephdr not an shdr, and
153762306a36Sopenharmony_ci		 * handily the port bytes in the right place preset.
153862306a36Sopenharmony_ci		 */
153962306a36Sopenharmony_ci		ddp = skb_push(skb, sizeof(*ddp) - 4);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci		/* Now fill in the long header */
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci		/*
154462306a36Sopenharmony_ci		 * These two first. The mac overlays the new source/dest
154562306a36Sopenharmony_ci		 * network information so we MUST copy these before
154662306a36Sopenharmony_ci		 * we write the network numbers !
154762306a36Sopenharmony_ci		 */
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci		ddp->deh_dnode = skb_mac_header(skb)[0];     /* From physical header */
155062306a36Sopenharmony_ci		ddp->deh_snode = skb_mac_header(skb)[1];     /* From physical header */
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci		ddp->deh_dnet  = ap->s_net;	/* Network number */
155362306a36Sopenharmony_ci		ddp->deh_snet  = ap->s_net;
155462306a36Sopenharmony_ci		ddp->deh_sum   = 0;		/* No checksum */
155562306a36Sopenharmony_ci		/*
155662306a36Sopenharmony_ci		 * Not sure about this bit...
155762306a36Sopenharmony_ci		 */
155862306a36Sopenharmony_ci		/* Non routable, so force a drop if we slip up later */
155962306a36Sopenharmony_ci		ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10));
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci	skb_reset_transport_header(skb);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	return atalk_rcv(skb, dev, pt, orig_dev);
156462306a36Sopenharmony_cifreeit:
156562306a36Sopenharmony_ci	kfree_skb(skb);
156662306a36Sopenharmony_ci	return 0;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
157062306a36Sopenharmony_ci{
157162306a36Sopenharmony_ci	struct sock *sk = sock->sk;
157262306a36Sopenharmony_ci	struct atalk_sock *at = at_sk(sk);
157362306a36Sopenharmony_ci	DECLARE_SOCKADDR(struct sockaddr_at *, usat, msg->msg_name);
157462306a36Sopenharmony_ci	int flags = msg->msg_flags;
157562306a36Sopenharmony_ci	int loopback = 0;
157662306a36Sopenharmony_ci	struct sockaddr_at local_satalk, gsat;
157762306a36Sopenharmony_ci	struct sk_buff *skb;
157862306a36Sopenharmony_ci	struct net_device *dev;
157962306a36Sopenharmony_ci	struct ddpehdr *ddp;
158062306a36Sopenharmony_ci	int size, hard_header_len;
158162306a36Sopenharmony_ci	struct atalk_route *rt, *rt_lo = NULL;
158262306a36Sopenharmony_ci	int err;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
158562306a36Sopenharmony_ci		return -EINVAL;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (len > DDP_MAXSZ)
158862306a36Sopenharmony_ci		return -EMSGSIZE;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	lock_sock(sk);
159162306a36Sopenharmony_ci	if (usat) {
159262306a36Sopenharmony_ci		err = -EBUSY;
159362306a36Sopenharmony_ci		if (sock_flag(sk, SOCK_ZAPPED))
159462306a36Sopenharmony_ci			if (atalk_autobind(sk) < 0)
159562306a36Sopenharmony_ci				goto out;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		err = -EINVAL;
159862306a36Sopenharmony_ci		if (msg->msg_namelen < sizeof(*usat) ||
159962306a36Sopenharmony_ci		    usat->sat_family != AF_APPLETALK)
160062306a36Sopenharmony_ci			goto out;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci		err = -EPERM;
160362306a36Sopenharmony_ci		/* netatalk didn't implement this check */
160462306a36Sopenharmony_ci		if (usat->sat_addr.s_node == ATADDR_BCAST &&
160562306a36Sopenharmony_ci		    !sock_flag(sk, SOCK_BROADCAST)) {
160662306a36Sopenharmony_ci			goto out;
160762306a36Sopenharmony_ci		}
160862306a36Sopenharmony_ci	} else {
160962306a36Sopenharmony_ci		err = -ENOTCONN;
161062306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED)
161162306a36Sopenharmony_ci			goto out;
161262306a36Sopenharmony_ci		usat = &local_satalk;
161362306a36Sopenharmony_ci		usat->sat_family      = AF_APPLETALK;
161462306a36Sopenharmony_ci		usat->sat_port	      = at->dest_port;
161562306a36Sopenharmony_ci		usat->sat_addr.s_node = at->dest_node;
161662306a36Sopenharmony_ci		usat->sat_addr.s_net  = at->dest_net;
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	/* Build a packet */
162062306a36Sopenharmony_ci	SOCK_DEBUG(sk, "SK %p: Got address.\n", sk);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	/* For headers */
162362306a36Sopenharmony_ci	size = sizeof(struct ddpehdr) + len + ddp_dl->header_length;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) {
162662306a36Sopenharmony_ci		rt = atrtr_find(&usat->sat_addr);
162762306a36Sopenharmony_ci	} else {
162862306a36Sopenharmony_ci		struct atalk_addr at_hint;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci		at_hint.s_node = 0;
163162306a36Sopenharmony_ci		at_hint.s_net  = at->src_net;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci		rt = atrtr_find(&at_hint);
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci	err = -ENETUNREACH;
163662306a36Sopenharmony_ci	if (!rt)
163762306a36Sopenharmony_ci		goto out;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	dev = rt->dev;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n",
164262306a36Sopenharmony_ci			sk, size, dev->name);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	hard_header_len = dev->hard_header_len;
164562306a36Sopenharmony_ci	/* Leave room for loopback hardware header if necessary */
164662306a36Sopenharmony_ci	if (usat->sat_addr.s_node == ATADDR_BCAST &&
164762306a36Sopenharmony_ci	    (dev->flags & IFF_LOOPBACK || !(rt->flags & RTF_GATEWAY))) {
164862306a36Sopenharmony_ci		struct atalk_addr at_lo;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci		at_lo.s_node = 0;
165162306a36Sopenharmony_ci		at_lo.s_net  = 0;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci		rt_lo = atrtr_find(&at_lo);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci		if (rt_lo && rt_lo->dev->hard_header_len > hard_header_len)
165662306a36Sopenharmony_ci			hard_header_len = rt_lo->dev->hard_header_len;
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	size += hard_header_len;
166062306a36Sopenharmony_ci	release_sock(sk);
166162306a36Sopenharmony_ci	skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err);
166262306a36Sopenharmony_ci	lock_sock(sk);
166362306a36Sopenharmony_ci	if (!skb)
166462306a36Sopenharmony_ci		goto out;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	skb_reserve(skb, ddp_dl->header_length);
166762306a36Sopenharmony_ci	skb_reserve(skb, hard_header_len);
166862306a36Sopenharmony_ci	skb->dev = dev;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	ddp = skb_put(skb, sizeof(struct ddpehdr));
167362306a36Sopenharmony_ci	ddp->deh_len_hops  = htons(len + sizeof(*ddp));
167462306a36Sopenharmony_ci	ddp->deh_dnet  = usat->sat_addr.s_net;
167562306a36Sopenharmony_ci	ddp->deh_snet  = at->src_net;
167662306a36Sopenharmony_ci	ddp->deh_dnode = usat->sat_addr.s_node;
167762306a36Sopenharmony_ci	ddp->deh_snode = at->src_node;
167862306a36Sopenharmony_ci	ddp->deh_dport = usat->sat_port;
167962306a36Sopenharmony_ci	ddp->deh_sport = at->src_port;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	SOCK_DEBUG(sk, "SK %p: Copy user data (%zd bytes).\n", sk, len);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	err = memcpy_from_msg(skb_put(skb, len), msg, len);
168462306a36Sopenharmony_ci	if (err) {
168562306a36Sopenharmony_ci		kfree_skb(skb);
168662306a36Sopenharmony_ci		err = -EFAULT;
168762306a36Sopenharmony_ci		goto out;
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	if (sk->sk_no_check_tx)
169162306a36Sopenharmony_ci		ddp->deh_sum = 0;
169262306a36Sopenharmony_ci	else
169362306a36Sopenharmony_ci		ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp));
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	/*
169662306a36Sopenharmony_ci	 * Loopback broadcast packets to non gateway targets (ie routes
169762306a36Sopenharmony_ci	 * to group we are in)
169862306a36Sopenharmony_ci	 */
169962306a36Sopenharmony_ci	if (ddp->deh_dnode == ATADDR_BCAST &&
170062306a36Sopenharmony_ci	    !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) {
170162306a36Sopenharmony_ci		struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci		if (skb2) {
170462306a36Sopenharmony_ci			loopback = 1;
170562306a36Sopenharmony_ci			SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk);
170662306a36Sopenharmony_ci			/*
170762306a36Sopenharmony_ci			 * If it fails it is queued/sent above in the aarp queue
170862306a36Sopenharmony_ci			 */
170962306a36Sopenharmony_ci			aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL);
171062306a36Sopenharmony_ci		}
171162306a36Sopenharmony_ci	}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (dev->flags & IFF_LOOPBACK || loopback) {
171462306a36Sopenharmony_ci		SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk);
171562306a36Sopenharmony_ci		/* loop back */
171662306a36Sopenharmony_ci		skb_orphan(skb);
171762306a36Sopenharmony_ci		if (ddp->deh_dnode == ATADDR_BCAST) {
171862306a36Sopenharmony_ci			if (!rt_lo) {
171962306a36Sopenharmony_ci				kfree_skb(skb);
172062306a36Sopenharmony_ci				err = -ENETUNREACH;
172162306a36Sopenharmony_ci				goto out;
172262306a36Sopenharmony_ci			}
172362306a36Sopenharmony_ci			dev = rt_lo->dev;
172462306a36Sopenharmony_ci			skb->dev = dev;
172562306a36Sopenharmony_ci		}
172662306a36Sopenharmony_ci		ddp_dl->request(ddp_dl, skb, dev->dev_addr);
172762306a36Sopenharmony_ci	} else {
172862306a36Sopenharmony_ci		SOCK_DEBUG(sk, "SK %p: send out.\n", sk);
172962306a36Sopenharmony_ci		if (rt->flags & RTF_GATEWAY) {
173062306a36Sopenharmony_ci		    gsat.sat_addr = rt->gateway;
173162306a36Sopenharmony_ci		    usat = &gsat;
173262306a36Sopenharmony_ci		}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci		/*
173562306a36Sopenharmony_ci		 * If it fails it is queued/sent above in the aarp queue
173662306a36Sopenharmony_ci		 */
173762306a36Sopenharmony_ci		aarp_send_ddp(dev, skb, &usat->sat_addr, NULL);
173862306a36Sopenharmony_ci	}
173962306a36Sopenharmony_ci	SOCK_DEBUG(sk, "SK %p: Done write (%zd).\n", sk, len);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ciout:
174262306a36Sopenharmony_ci	release_sock(sk);
174362306a36Sopenharmony_ci	return err ? : len;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic int atalk_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
174762306a36Sopenharmony_ci			 int flags)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
175062306a36Sopenharmony_ci	struct ddpehdr *ddp;
175162306a36Sopenharmony_ci	int copied = 0;
175262306a36Sopenharmony_ci	int offset = 0;
175362306a36Sopenharmony_ci	int err = 0;
175462306a36Sopenharmony_ci	struct sk_buff *skb;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, &err);
175762306a36Sopenharmony_ci	lock_sock(sk);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (!skb)
176062306a36Sopenharmony_ci		goto out;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	/* FIXME: use skb->cb to be able to use shared skbs */
176362306a36Sopenharmony_ci	ddp = ddp_hdr(skb);
176462306a36Sopenharmony_ci	copied = ntohs(ddp->deh_len_hops) & 1023;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	if (sk->sk_type != SOCK_RAW) {
176762306a36Sopenharmony_ci		offset = sizeof(*ddp);
176862306a36Sopenharmony_ci		copied -= offset;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (copied > size) {
177262306a36Sopenharmony_ci		copied = size;
177362306a36Sopenharmony_ci		msg->msg_flags |= MSG_TRUNC;
177462306a36Sopenharmony_ci	}
177562306a36Sopenharmony_ci	err = skb_copy_datagram_msg(skb, offset, msg, copied);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	if (!err && msg->msg_name) {
177862306a36Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_at *, sat, msg->msg_name);
177962306a36Sopenharmony_ci		sat->sat_family      = AF_APPLETALK;
178062306a36Sopenharmony_ci		sat->sat_port        = ddp->deh_sport;
178162306a36Sopenharmony_ci		sat->sat_addr.s_node = ddp->deh_snode;
178262306a36Sopenharmony_ci		sat->sat_addr.s_net  = ddp->deh_snet;
178362306a36Sopenharmony_ci		msg->msg_namelen     = sizeof(*sat);
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	skb_free_datagram(sk, skb);	/* Free the datagram. */
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ciout:
178962306a36Sopenharmony_ci	release_sock(sk);
179062306a36Sopenharmony_ci	return err ? : copied;
179162306a36Sopenharmony_ci}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci/*
179562306a36Sopenharmony_ci * AppleTalk ioctl calls.
179662306a36Sopenharmony_ci */
179762306a36Sopenharmony_cistatic int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
179862306a36Sopenharmony_ci{
179962306a36Sopenharmony_ci	int rc = -ENOIOCTLCMD;
180062306a36Sopenharmony_ci	struct sock *sk = sock->sk;
180162306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	switch (cmd) {
180462306a36Sopenharmony_ci	/* Protocol layer */
180562306a36Sopenharmony_ci	case TIOCOUTQ: {
180662306a36Sopenharmony_ci		long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci		if (amount < 0)
180962306a36Sopenharmony_ci			amount = 0;
181062306a36Sopenharmony_ci		rc = put_user(amount, (int __user *)argp);
181162306a36Sopenharmony_ci		break;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci	case TIOCINQ: {
181462306a36Sopenharmony_ci		struct sk_buff *skb;
181562306a36Sopenharmony_ci		long amount = 0;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci		spin_lock_irq(&sk->sk_receive_queue.lock);
181862306a36Sopenharmony_ci		skb = skb_peek(&sk->sk_receive_queue);
181962306a36Sopenharmony_ci		if (skb)
182062306a36Sopenharmony_ci			amount = skb->len - sizeof(struct ddpehdr);
182162306a36Sopenharmony_ci		spin_unlock_irq(&sk->sk_receive_queue.lock);
182262306a36Sopenharmony_ci		rc = put_user(amount, (int __user *)argp);
182362306a36Sopenharmony_ci		break;
182462306a36Sopenharmony_ci	}
182562306a36Sopenharmony_ci	/* Routing */
182662306a36Sopenharmony_ci	case SIOCADDRT:
182762306a36Sopenharmony_ci	case SIOCDELRT:
182862306a36Sopenharmony_ci		rc = -EPERM;
182962306a36Sopenharmony_ci		if (capable(CAP_NET_ADMIN))
183062306a36Sopenharmony_ci			rc = atrtr_ioctl(cmd, argp);
183162306a36Sopenharmony_ci		break;
183262306a36Sopenharmony_ci	/* Interface */
183362306a36Sopenharmony_ci	case SIOCGIFADDR:
183462306a36Sopenharmony_ci	case SIOCSIFADDR:
183562306a36Sopenharmony_ci	case SIOCGIFBRDADDR:
183662306a36Sopenharmony_ci	case SIOCATALKDIFADDR:
183762306a36Sopenharmony_ci	case SIOCDIFADDR:
183862306a36Sopenharmony_ci	case SIOCSARP:		/* proxy AARP */
183962306a36Sopenharmony_ci	case SIOCDARP:		/* proxy AARP */
184062306a36Sopenharmony_ci		rtnl_lock();
184162306a36Sopenharmony_ci		rc = atif_ioctl(cmd, argp);
184262306a36Sopenharmony_ci		rtnl_unlock();
184362306a36Sopenharmony_ci		break;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	return rc;
184762306a36Sopenharmony_ci}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
185162306a36Sopenharmony_cistatic int atalk_compat_routing_ioctl(struct sock *sk, unsigned int cmd,
185262306a36Sopenharmony_ci		struct compat_rtentry __user *ur)
185362306a36Sopenharmony_ci{
185462306a36Sopenharmony_ci	compat_uptr_t rtdev;
185562306a36Sopenharmony_ci	struct rtentry rt;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	if (copy_from_user(&rt.rt_dst, &ur->rt_dst,
185862306a36Sopenharmony_ci			3 * sizeof(struct sockaddr)) ||
185962306a36Sopenharmony_ci	    get_user(rt.rt_flags, &ur->rt_flags) ||
186062306a36Sopenharmony_ci	    get_user(rt.rt_metric, &ur->rt_metric) ||
186162306a36Sopenharmony_ci	    get_user(rt.rt_mtu, &ur->rt_mtu) ||
186262306a36Sopenharmony_ci	    get_user(rt.rt_window, &ur->rt_window) ||
186362306a36Sopenharmony_ci	    get_user(rt.rt_irtt, &ur->rt_irtt) ||
186462306a36Sopenharmony_ci	    get_user(rtdev, &ur->rt_dev))
186562306a36Sopenharmony_ci		return -EFAULT;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	switch (cmd) {
186862306a36Sopenharmony_ci	case SIOCDELRT:
186962306a36Sopenharmony_ci		if (rt.rt_dst.sa_family != AF_APPLETALK)
187062306a36Sopenharmony_ci			return -EINVAL;
187162306a36Sopenharmony_ci		return atrtr_delete(&((struct sockaddr_at *)
187262306a36Sopenharmony_ci				      &rt.rt_dst)->sat_addr);
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	case SIOCADDRT:
187562306a36Sopenharmony_ci		rt.rt_dev = compat_ptr(rtdev);
187662306a36Sopenharmony_ci		return atrtr_ioctl_addrt(&rt);
187762306a36Sopenharmony_ci	default:
187862306a36Sopenharmony_ci		return -EINVAL;
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_cistatic int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
188262306a36Sopenharmony_ci{
188362306a36Sopenharmony_ci	void __user *argp = compat_ptr(arg);
188462306a36Sopenharmony_ci	struct sock *sk = sock->sk;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	switch (cmd) {
188762306a36Sopenharmony_ci	case SIOCADDRT:
188862306a36Sopenharmony_ci	case SIOCDELRT:
188962306a36Sopenharmony_ci		return atalk_compat_routing_ioctl(sk, cmd, argp);
189062306a36Sopenharmony_ci	/*
189162306a36Sopenharmony_ci	 * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we
189262306a36Sopenharmony_ci	 * cannot handle it in common code. The data we access if ifreq
189362306a36Sopenharmony_ci	 * here is compatible, so we can simply call the native
189462306a36Sopenharmony_ci	 * handler.
189562306a36Sopenharmony_ci	 */
189662306a36Sopenharmony_ci	case SIOCATALKDIFADDR:
189762306a36Sopenharmony_ci		return atalk_ioctl(sock, cmd, (unsigned long)argp);
189862306a36Sopenharmony_ci	default:
189962306a36Sopenharmony_ci		return -ENOIOCTLCMD;
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci}
190262306a36Sopenharmony_ci#endif /* CONFIG_COMPAT */
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_cistatic const struct net_proto_family atalk_family_ops = {
190662306a36Sopenharmony_ci	.family		= PF_APPLETALK,
190762306a36Sopenharmony_ci	.create		= atalk_create,
190862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
190962306a36Sopenharmony_ci};
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_cistatic const struct proto_ops atalk_dgram_ops = {
191262306a36Sopenharmony_ci	.family		= PF_APPLETALK,
191362306a36Sopenharmony_ci	.owner		= THIS_MODULE,
191462306a36Sopenharmony_ci	.release	= atalk_release,
191562306a36Sopenharmony_ci	.bind		= atalk_bind,
191662306a36Sopenharmony_ci	.connect	= atalk_connect,
191762306a36Sopenharmony_ci	.socketpair	= sock_no_socketpair,
191862306a36Sopenharmony_ci	.accept		= sock_no_accept,
191962306a36Sopenharmony_ci	.getname	= atalk_getname,
192062306a36Sopenharmony_ci	.poll		= datagram_poll,
192162306a36Sopenharmony_ci	.ioctl		= atalk_ioctl,
192262306a36Sopenharmony_ci	.gettstamp	= sock_gettstamp,
192362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
192462306a36Sopenharmony_ci	.compat_ioctl	= atalk_compat_ioctl,
192562306a36Sopenharmony_ci#endif
192662306a36Sopenharmony_ci	.listen		= sock_no_listen,
192762306a36Sopenharmony_ci	.shutdown	= sock_no_shutdown,
192862306a36Sopenharmony_ci	.sendmsg	= atalk_sendmsg,
192962306a36Sopenharmony_ci	.recvmsg	= atalk_recvmsg,
193062306a36Sopenharmony_ci	.mmap		= sock_no_mmap,
193162306a36Sopenharmony_ci};
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_cistatic struct notifier_block ddp_notifier = {
193462306a36Sopenharmony_ci	.notifier_call	= ddp_device_event,
193562306a36Sopenharmony_ci};
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_cistatic struct packet_type ltalk_packet_type __read_mostly = {
193862306a36Sopenharmony_ci	.type		= cpu_to_be16(ETH_P_LOCALTALK),
193962306a36Sopenharmony_ci	.func		= ltalk_rcv,
194062306a36Sopenharmony_ci};
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic struct packet_type ppptalk_packet_type __read_mostly = {
194362306a36Sopenharmony_ci	.type		= cpu_to_be16(ETH_P_PPPTALK),
194462306a36Sopenharmony_ci	.func		= atalk_rcv,
194562306a36Sopenharmony_ci};
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cistatic unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B };
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci/* Export symbols for use by drivers when AppleTalk is a module */
195062306a36Sopenharmony_ciEXPORT_SYMBOL(atrtr_get_dev);
195162306a36Sopenharmony_ciEXPORT_SYMBOL(atalk_find_dev_addr);
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci/* Called by proto.c on kernel start up */
195462306a36Sopenharmony_cistatic int __init atalk_init(void)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	int rc;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	rc = proto_register(&ddp_proto, 0);
195962306a36Sopenharmony_ci	if (rc)
196062306a36Sopenharmony_ci		goto out;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	rc = sock_register(&atalk_family_ops);
196362306a36Sopenharmony_ci	if (rc)
196462306a36Sopenharmony_ci		goto out_proto;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv);
196762306a36Sopenharmony_ci	if (!ddp_dl) {
196862306a36Sopenharmony_ci		pr_crit("Unable to register DDP with SNAP.\n");
196962306a36Sopenharmony_ci		rc = -ENOMEM;
197062306a36Sopenharmony_ci		goto out_sock;
197162306a36Sopenharmony_ci	}
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	dev_add_pack(&ltalk_packet_type);
197462306a36Sopenharmony_ci	dev_add_pack(&ppptalk_packet_type);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	rc = register_netdevice_notifier(&ddp_notifier);
197762306a36Sopenharmony_ci	if (rc)
197862306a36Sopenharmony_ci		goto out_snap;
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	rc = aarp_proto_init();
198162306a36Sopenharmony_ci	if (rc)
198262306a36Sopenharmony_ci		goto out_dev;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	rc = atalk_proc_init();
198562306a36Sopenharmony_ci	if (rc)
198662306a36Sopenharmony_ci		goto out_aarp;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	rc = atalk_register_sysctl();
198962306a36Sopenharmony_ci	if (rc)
199062306a36Sopenharmony_ci		goto out_proc;
199162306a36Sopenharmony_ciout:
199262306a36Sopenharmony_ci	return rc;
199362306a36Sopenharmony_ciout_proc:
199462306a36Sopenharmony_ci	atalk_proc_exit();
199562306a36Sopenharmony_ciout_aarp:
199662306a36Sopenharmony_ci	aarp_cleanup_module();
199762306a36Sopenharmony_ciout_dev:
199862306a36Sopenharmony_ci	unregister_netdevice_notifier(&ddp_notifier);
199962306a36Sopenharmony_ciout_snap:
200062306a36Sopenharmony_ci	dev_remove_pack(&ppptalk_packet_type);
200162306a36Sopenharmony_ci	dev_remove_pack(&ltalk_packet_type);
200262306a36Sopenharmony_ci	unregister_snap_client(ddp_dl);
200362306a36Sopenharmony_ciout_sock:
200462306a36Sopenharmony_ci	sock_unregister(PF_APPLETALK);
200562306a36Sopenharmony_ciout_proto:
200662306a36Sopenharmony_ci	proto_unregister(&ddp_proto);
200762306a36Sopenharmony_ci	goto out;
200862306a36Sopenharmony_ci}
200962306a36Sopenharmony_cimodule_init(atalk_init);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci/*
201262306a36Sopenharmony_ci * No explicit module reference count manipulation is needed in the
201362306a36Sopenharmony_ci * protocol. Socket layer sets module reference count for us
201462306a36Sopenharmony_ci * and interfaces reference counting is done
201562306a36Sopenharmony_ci * by the network device layer.
201662306a36Sopenharmony_ci *
201762306a36Sopenharmony_ci * Ergo, before the AppleTalk module can be removed, all AppleTalk
201862306a36Sopenharmony_ci * sockets should be closed from user space.
201962306a36Sopenharmony_ci */
202062306a36Sopenharmony_cistatic void __exit atalk_exit(void)
202162306a36Sopenharmony_ci{
202262306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
202362306a36Sopenharmony_ci	atalk_unregister_sysctl();
202462306a36Sopenharmony_ci#endif /* CONFIG_SYSCTL */
202562306a36Sopenharmony_ci	atalk_proc_exit();
202662306a36Sopenharmony_ci	aarp_cleanup_module();	/* General aarp clean-up. */
202762306a36Sopenharmony_ci	unregister_netdevice_notifier(&ddp_notifier);
202862306a36Sopenharmony_ci	dev_remove_pack(&ltalk_packet_type);
202962306a36Sopenharmony_ci	dev_remove_pack(&ppptalk_packet_type);
203062306a36Sopenharmony_ci	unregister_snap_client(ddp_dl);
203162306a36Sopenharmony_ci	sock_unregister(PF_APPLETALK);
203262306a36Sopenharmony_ci	proto_unregister(&ddp_proto);
203362306a36Sopenharmony_ci}
203462306a36Sopenharmony_cimodule_exit(atalk_exit);
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
203762306a36Sopenharmony_ciMODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
203862306a36Sopenharmony_ciMODULE_DESCRIPTION("AppleTalk 0.20\n");
203962306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_APPLETALK);
2040