18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	AARP:		An implementation of the AppleTalk AARP protocol for
48c2ecf20Sopenharmony_ci *			Ethernet 'ELAP'.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *		Alan Cox  <Alan.Cox@linux.org>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *	This doesn't fit cleanly with the IP arp. Potentially we can use
98c2ecf20Sopenharmony_ci *	the generic neighbour discovery code to clean this up.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *	FIXME:
128c2ecf20Sopenharmony_ci *		We ought to handle the retransmits with a single list and a
138c2ecf20Sopenharmony_ci *	separate fast timer for when it is needed.
148c2ecf20Sopenharmony_ci *		Use neighbour discovery code.
158c2ecf20Sopenharmony_ci *		Token Ring Support.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *	References:
188c2ecf20Sopenharmony_ci *		Inside AppleTalk (2nd Ed).
198c2ecf20Sopenharmony_ci *	Fixes:
208c2ecf20Sopenharmony_ci *		Jaume Grau	-	flush caches on AARP_PROBE
218c2ecf20Sopenharmony_ci *		Rob Newberry	-	Added proxy AARP and AARP proc fs,
228c2ecf20Sopenharmony_ci *					moved probing from DDP module.
238c2ecf20Sopenharmony_ci *		Arnaldo C. Melo -	don't mangle rx packets
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
278c2ecf20Sopenharmony_ci#include <linux/slab.h>
288c2ecf20Sopenharmony_ci#include <net/sock.h>
298c2ecf20Sopenharmony_ci#include <net/datalink.h>
308c2ecf20Sopenharmony_ci#include <net/psnap.h>
318c2ecf20Sopenharmony_ci#include <linux/atalk.h>
328c2ecf20Sopenharmony_ci#include <linux/delay.h>
338c2ecf20Sopenharmony_ci#include <linux/init.h>
348c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
358c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
368c2ecf20Sopenharmony_ci#include <linux/export.h>
378c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ciint sysctl_aarp_expiry_time = AARP_EXPIRY_TIME;
408c2ecf20Sopenharmony_ciint sysctl_aarp_tick_time = AARP_TICK_TIME;
418c2ecf20Sopenharmony_ciint sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT;
428c2ecf20Sopenharmony_ciint sysctl_aarp_resolve_time = AARP_RESOLVE_TIME;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Lists of aarp entries */
458c2ecf20Sopenharmony_ci/**
468c2ecf20Sopenharmony_ci *	struct aarp_entry - AARP entry
478c2ecf20Sopenharmony_ci *	@last_sent - Last time we xmitted the aarp request
488c2ecf20Sopenharmony_ci *	@packet_queue - Queue of frames wait for resolution
498c2ecf20Sopenharmony_ci *	@status - Used for proxy AARP
508c2ecf20Sopenharmony_ci *	expires_at - Entry expiry time
518c2ecf20Sopenharmony_ci *	target_addr - DDP Address
528c2ecf20Sopenharmony_ci *	dev - Device to use
538c2ecf20Sopenharmony_ci *	hwaddr - Physical i/f address of target/router
548c2ecf20Sopenharmony_ci *	xmit_count - When this hits 10 we give up
558c2ecf20Sopenharmony_ci *	next - Next entry in chain
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistruct aarp_entry {
588c2ecf20Sopenharmony_ci	/* These first two are only used for unresolved entries */
598c2ecf20Sopenharmony_ci	unsigned long		last_sent;
608c2ecf20Sopenharmony_ci	struct sk_buff_head	packet_queue;
618c2ecf20Sopenharmony_ci	int			status;
628c2ecf20Sopenharmony_ci	unsigned long		expires_at;
638c2ecf20Sopenharmony_ci	struct atalk_addr	target_addr;
648c2ecf20Sopenharmony_ci	struct net_device	*dev;
658c2ecf20Sopenharmony_ci	char			hwaddr[ETH_ALEN];
668c2ecf20Sopenharmony_ci	unsigned short		xmit_count;
678c2ecf20Sopenharmony_ci	struct aarp_entry	*next;
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Hashed list of resolved, unresolved and proxy entries */
718c2ecf20Sopenharmony_cistatic struct aarp_entry *resolved[AARP_HASH_SIZE];
728c2ecf20Sopenharmony_cistatic struct aarp_entry *unresolved[AARP_HASH_SIZE];
738c2ecf20Sopenharmony_cistatic struct aarp_entry *proxies[AARP_HASH_SIZE];
748c2ecf20Sopenharmony_cistatic int unresolved_count;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* One lock protects it all. */
778c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(aarp_lock);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* Used to walk the list and purge/kick entries.  */
808c2ecf20Sopenharmony_cistatic struct timer_list aarp_timer;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/*
838c2ecf20Sopenharmony_ci *	Delete an aarp queue
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci *	Must run under aarp_lock.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic void __aarp_expire(struct aarp_entry *a)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	skb_queue_purge(&a->packet_queue);
908c2ecf20Sopenharmony_ci	kfree(a);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci *	Send an aarp queue entry request
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci *	Must run under aarp_lock.
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic void __aarp_send_query(struct aarp_entry *a)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	static unsigned char aarp_eth_multicast[ETH_ALEN] =
1018c2ecf20Sopenharmony_ci					{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
1028c2ecf20Sopenharmony_ci	struct net_device *dev = a->dev;
1038c2ecf20Sopenharmony_ci	struct elapaarp *eah;
1048c2ecf20Sopenharmony_ci	int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
1058c2ecf20Sopenharmony_ci	struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
1068c2ecf20Sopenharmony_ci	struct atalk_addr *sat = atalk_find_dev_addr(dev);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (!skb)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (!sat) {
1128c2ecf20Sopenharmony_ci		kfree_skb(skb);
1138c2ecf20Sopenharmony_ci		return;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Set up the buffer */
1178c2ecf20Sopenharmony_ci	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
1188c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
1198c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
1208c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*eah));
1218c2ecf20Sopenharmony_ci	skb->protocol    = htons(ETH_P_ATALK);
1228c2ecf20Sopenharmony_ci	skb->dev	 = dev;
1238c2ecf20Sopenharmony_ci	eah		 = aarp_hdr(skb);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Set up the ARP */
1268c2ecf20Sopenharmony_ci	eah->hw_type	 = htons(AARP_HW_TYPE_ETHERNET);
1278c2ecf20Sopenharmony_ci	eah->pa_type	 = htons(ETH_P_ATALK);
1288c2ecf20Sopenharmony_ci	eah->hw_len	 = ETH_ALEN;
1298c2ecf20Sopenharmony_ci	eah->pa_len	 = AARP_PA_ALEN;
1308c2ecf20Sopenharmony_ci	eah->function	 = htons(AARP_REQUEST);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ether_addr_copy(eah->hw_src, dev->dev_addr);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	eah->pa_src_zero = 0;
1358c2ecf20Sopenharmony_ci	eah->pa_src_net	 = sat->s_net;
1368c2ecf20Sopenharmony_ci	eah->pa_src_node = sat->s_node;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	eth_zero_addr(eah->hw_dst);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	eah->pa_dst_zero = 0;
1418c2ecf20Sopenharmony_ci	eah->pa_dst_net	 = a->target_addr.s_net;
1428c2ecf20Sopenharmony_ci	eah->pa_dst_node = a->target_addr.s_node;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Send it */
1458c2ecf20Sopenharmony_ci	aarp_dl->request(aarp_dl, skb, aarp_eth_multicast);
1468c2ecf20Sopenharmony_ci	/* Update the sending count */
1478c2ecf20Sopenharmony_ci	a->xmit_count++;
1488c2ecf20Sopenharmony_ci	a->last_sent = jiffies;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* This runs under aarp_lock and in softint context, so only atomic memory
1528c2ecf20Sopenharmony_ci * allocations can be used. */
1538c2ecf20Sopenharmony_cistatic void aarp_send_reply(struct net_device *dev, struct atalk_addr *us,
1548c2ecf20Sopenharmony_ci			    struct atalk_addr *them, unsigned char *sha)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct elapaarp *eah;
1578c2ecf20Sopenharmony_ci	int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
1588c2ecf20Sopenharmony_ci	struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (!skb)
1618c2ecf20Sopenharmony_ci		return;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Set up the buffer */
1648c2ecf20Sopenharmony_ci	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
1658c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
1668c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
1678c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*eah));
1688c2ecf20Sopenharmony_ci	skb->protocol    = htons(ETH_P_ATALK);
1698c2ecf20Sopenharmony_ci	skb->dev	 = dev;
1708c2ecf20Sopenharmony_ci	eah		 = aarp_hdr(skb);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* Set up the ARP */
1738c2ecf20Sopenharmony_ci	eah->hw_type	 = htons(AARP_HW_TYPE_ETHERNET);
1748c2ecf20Sopenharmony_ci	eah->pa_type	 = htons(ETH_P_ATALK);
1758c2ecf20Sopenharmony_ci	eah->hw_len	 = ETH_ALEN;
1768c2ecf20Sopenharmony_ci	eah->pa_len	 = AARP_PA_ALEN;
1778c2ecf20Sopenharmony_ci	eah->function	 = htons(AARP_REPLY);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ether_addr_copy(eah->hw_src, dev->dev_addr);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	eah->pa_src_zero = 0;
1828c2ecf20Sopenharmony_ci	eah->pa_src_net	 = us->s_net;
1838c2ecf20Sopenharmony_ci	eah->pa_src_node = us->s_node;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (!sha)
1868c2ecf20Sopenharmony_ci		eth_zero_addr(eah->hw_dst);
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		ether_addr_copy(eah->hw_dst, sha);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	eah->pa_dst_zero = 0;
1918c2ecf20Sopenharmony_ci	eah->pa_dst_net	 = them->s_net;
1928c2ecf20Sopenharmony_ci	eah->pa_dst_node = them->s_node;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* Send it */
1958c2ecf20Sopenharmony_ci	aarp_dl->request(aarp_dl, skb, sha);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/*
1998c2ecf20Sopenharmony_ci *	Send probe frames. Called from aarp_probe_network and
2008c2ecf20Sopenharmony_ci *	aarp_proxy_probe_network.
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void aarp_send_probe(struct net_device *dev, struct atalk_addr *us)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct elapaarp *eah;
2068c2ecf20Sopenharmony_ci	int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
2078c2ecf20Sopenharmony_ci	struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
2088c2ecf20Sopenharmony_ci	static unsigned char aarp_eth_multicast[ETH_ALEN] =
2098c2ecf20Sopenharmony_ci					{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (!skb)
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Set up the buffer */
2158c2ecf20Sopenharmony_ci	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
2168c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
2178c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
2188c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*eah));
2198c2ecf20Sopenharmony_ci	skb->protocol    = htons(ETH_P_ATALK);
2208c2ecf20Sopenharmony_ci	skb->dev	 = dev;
2218c2ecf20Sopenharmony_ci	eah		 = aarp_hdr(skb);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* Set up the ARP */
2248c2ecf20Sopenharmony_ci	eah->hw_type	 = htons(AARP_HW_TYPE_ETHERNET);
2258c2ecf20Sopenharmony_ci	eah->pa_type	 = htons(ETH_P_ATALK);
2268c2ecf20Sopenharmony_ci	eah->hw_len	 = ETH_ALEN;
2278c2ecf20Sopenharmony_ci	eah->pa_len	 = AARP_PA_ALEN;
2288c2ecf20Sopenharmony_ci	eah->function	 = htons(AARP_PROBE);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ether_addr_copy(eah->hw_src, dev->dev_addr);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	eah->pa_src_zero = 0;
2338c2ecf20Sopenharmony_ci	eah->pa_src_net	 = us->s_net;
2348c2ecf20Sopenharmony_ci	eah->pa_src_node = us->s_node;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	eth_zero_addr(eah->hw_dst);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	eah->pa_dst_zero = 0;
2398c2ecf20Sopenharmony_ci	eah->pa_dst_net	 = us->s_net;
2408c2ecf20Sopenharmony_ci	eah->pa_dst_node = us->s_node;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* Send it */
2438c2ecf20Sopenharmony_ci	aarp_dl->request(aarp_dl, skb, aarp_eth_multicast);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/*
2478c2ecf20Sopenharmony_ci *	Handle an aarp timer expire
2488c2ecf20Sopenharmony_ci *
2498c2ecf20Sopenharmony_ci *	Must run under the aarp_lock.
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void __aarp_expire_timer(struct aarp_entry **n)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct aarp_entry *t;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	while (*n)
2578c2ecf20Sopenharmony_ci		/* Expired ? */
2588c2ecf20Sopenharmony_ci		if (time_after(jiffies, (*n)->expires_at)) {
2598c2ecf20Sopenharmony_ci			t = *n;
2608c2ecf20Sopenharmony_ci			*n = (*n)->next;
2618c2ecf20Sopenharmony_ci			__aarp_expire(t);
2628c2ecf20Sopenharmony_ci		} else
2638c2ecf20Sopenharmony_ci			n = &((*n)->next);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/*
2678c2ecf20Sopenharmony_ci *	Kick all pending requests 5 times a second.
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci *	Must run under the aarp_lock.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_cistatic void __aarp_kick(struct aarp_entry **n)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct aarp_entry *t;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	while (*n)
2768c2ecf20Sopenharmony_ci		/* Expired: if this will be the 11th tx, we delete instead. */
2778c2ecf20Sopenharmony_ci		if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) {
2788c2ecf20Sopenharmony_ci			t = *n;
2798c2ecf20Sopenharmony_ci			*n = (*n)->next;
2808c2ecf20Sopenharmony_ci			__aarp_expire(t);
2818c2ecf20Sopenharmony_ci		} else {
2828c2ecf20Sopenharmony_ci			__aarp_send_query(*n);
2838c2ecf20Sopenharmony_ci			n = &((*n)->next);
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci *	A device has gone down. Take all entries referring to the device
2898c2ecf20Sopenharmony_ci *	and remove them.
2908c2ecf20Sopenharmony_ci *
2918c2ecf20Sopenharmony_ci *	Must run under the aarp_lock.
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_cistatic void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct aarp_entry *t;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	while (*n)
2988c2ecf20Sopenharmony_ci		if ((*n)->dev == dev) {
2998c2ecf20Sopenharmony_ci			t = *n;
3008c2ecf20Sopenharmony_ci			*n = (*n)->next;
3018c2ecf20Sopenharmony_ci			__aarp_expire(t);
3028c2ecf20Sopenharmony_ci		} else
3038c2ecf20Sopenharmony_ci			n = &((*n)->next);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/* Handle the timer event */
3078c2ecf20Sopenharmony_cistatic void aarp_expire_timeout(struct timer_list *unused)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	int ct;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
3148c2ecf20Sopenharmony_ci		__aarp_expire_timer(&resolved[ct]);
3158c2ecf20Sopenharmony_ci		__aarp_kick(&unresolved[ct]);
3168c2ecf20Sopenharmony_ci		__aarp_expire_timer(&unresolved[ct]);
3178c2ecf20Sopenharmony_ci		__aarp_expire_timer(&proxies[ct]);
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
3218c2ecf20Sopenharmony_ci	mod_timer(&aarp_timer, jiffies +
3228c2ecf20Sopenharmony_ci			       (unresolved_count ? sysctl_aarp_tick_time :
3238c2ecf20Sopenharmony_ci				sysctl_aarp_expiry_time));
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci/* Network device notifier chain handler. */
3278c2ecf20Sopenharmony_cistatic int aarp_device_event(struct notifier_block *this, unsigned long event,
3288c2ecf20Sopenharmony_ci			     void *ptr)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3318c2ecf20Sopenharmony_ci	int ct;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
3348c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (event == NETDEV_DOWN) {
3378c2ecf20Sopenharmony_ci		write_lock_bh(&aarp_lock);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
3408c2ecf20Sopenharmony_ci			__aarp_expire_device(&resolved[ct], dev);
3418c2ecf20Sopenharmony_ci			__aarp_expire_device(&unresolved[ct], dev);
3428c2ecf20Sopenharmony_ci			__aarp_expire_device(&proxies[ct], dev);
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		write_unlock_bh(&aarp_lock);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/* Expire all entries in a hash chain */
3518c2ecf20Sopenharmony_cistatic void __aarp_expire_all(struct aarp_entry **n)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct aarp_entry *t;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	while (*n) {
3568c2ecf20Sopenharmony_ci		t = *n;
3578c2ecf20Sopenharmony_ci		*n = (*n)->next;
3588c2ecf20Sopenharmony_ci		__aarp_expire(t);
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/* Cleanup all hash chains -- module unloading */
3638c2ecf20Sopenharmony_cistatic void aarp_purge(void)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	int ct;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
3688c2ecf20Sopenharmony_ci	for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
3698c2ecf20Sopenharmony_ci		__aarp_expire_all(&resolved[ct]);
3708c2ecf20Sopenharmony_ci		__aarp_expire_all(&unresolved[ct]);
3718c2ecf20Sopenharmony_ci		__aarp_expire_all(&proxies[ct]);
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci *	Create a new aarp entry.  This must use GFP_ATOMIC because it
3788c2ecf20Sopenharmony_ci *	runs while holding spinlocks.
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_cistatic struct aarp_entry *aarp_alloc(void)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct aarp_entry *a = kmalloc(sizeof(*a), GFP_ATOMIC);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (a)
3858c2ecf20Sopenharmony_ci		skb_queue_head_init(&a->packet_queue);
3868c2ecf20Sopenharmony_ci	return a;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/*
3908c2ecf20Sopenharmony_ci * Find an entry. We might return an expired but not yet purged entry. We
3918c2ecf20Sopenharmony_ci * don't care as it will do no harm.
3928c2ecf20Sopenharmony_ci *
3938c2ecf20Sopenharmony_ci * This must run under the aarp_lock.
3948c2ecf20Sopenharmony_ci */
3958c2ecf20Sopenharmony_cistatic struct aarp_entry *__aarp_find_entry(struct aarp_entry *list,
3968c2ecf20Sopenharmony_ci					    struct net_device *dev,
3978c2ecf20Sopenharmony_ci					    struct atalk_addr *sat)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	while (list) {
4008c2ecf20Sopenharmony_ci		if (list->target_addr.s_net == sat->s_net &&
4018c2ecf20Sopenharmony_ci		    list->target_addr.s_node == sat->s_node &&
4028c2ecf20Sopenharmony_ci		    list->dev == dev)
4038c2ecf20Sopenharmony_ci			break;
4048c2ecf20Sopenharmony_ci		list = list->next;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return list;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci/* Called from the DDP code, and thus must be exported. */
4118c2ecf20Sopenharmony_civoid aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	int hash = sa->s_node % (AARP_HASH_SIZE - 1);
4148c2ecf20Sopenharmony_ci	struct aarp_entry *a;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	a = __aarp_find_entry(proxies[hash], dev, sa);
4198c2ecf20Sopenharmony_ci	if (a)
4208c2ecf20Sopenharmony_ci		a->expires_at = jiffies - 1;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci/* This must run under aarp_lock. */
4268c2ecf20Sopenharmony_cistatic struct atalk_addr *__aarp_proxy_find(struct net_device *dev,
4278c2ecf20Sopenharmony_ci					    struct atalk_addr *sa)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	int hash = sa->s_node % (AARP_HASH_SIZE - 1);
4308c2ecf20Sopenharmony_ci	struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return a ? sa : NULL;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci/*
4368c2ecf20Sopenharmony_ci * Probe a Phase 1 device or a device that requires its Net:Node to
4378c2ecf20Sopenharmony_ci * be set via an ioctl.
4388c2ecf20Sopenharmony_ci */
4398c2ecf20Sopenharmony_cistatic void aarp_send_probe_phase1(struct atalk_iface *iface)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct ifreq atreq;
4428c2ecf20Sopenharmony_ci	struct sockaddr_at *sa = (struct sockaddr_at *)&atreq.ifr_addr;
4438c2ecf20Sopenharmony_ci	const struct net_device_ops *ops = iface->dev->netdev_ops;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	sa->sat_addr.s_node = iface->address.s_node;
4468c2ecf20Sopenharmony_ci	sa->sat_addr.s_net = ntohs(iface->address.s_net);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* We pass the Net:Node to the drivers/cards by a Device ioctl. */
4498c2ecf20Sopenharmony_ci	if (!(ops->ndo_do_ioctl(iface->dev, &atreq, SIOCSIFADDR))) {
4508c2ecf20Sopenharmony_ci		ops->ndo_do_ioctl(iface->dev, &atreq, SIOCGIFADDR);
4518c2ecf20Sopenharmony_ci		if (iface->address.s_net != htons(sa->sat_addr.s_net) ||
4528c2ecf20Sopenharmony_ci		    iface->address.s_node != sa->sat_addr.s_node)
4538c2ecf20Sopenharmony_ci			iface->status |= ATIF_PROBE_FAIL;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci		iface->address.s_net  = htons(sa->sat_addr.s_net);
4568c2ecf20Sopenharmony_ci		iface->address.s_node = sa->sat_addr.s_node;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_civoid aarp_probe_network(struct atalk_iface *atif)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	if (atif->dev->type == ARPHRD_LOCALTLK ||
4648c2ecf20Sopenharmony_ci	    atif->dev->type == ARPHRD_PPP)
4658c2ecf20Sopenharmony_ci		aarp_send_probe_phase1(atif);
4668c2ecf20Sopenharmony_ci	else {
4678c2ecf20Sopenharmony_ci		unsigned int count;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) {
4708c2ecf20Sopenharmony_ci			aarp_send_probe(atif->dev, &atif->address);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci			/* Defer 1/10th */
4738c2ecf20Sopenharmony_ci			msleep(100);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci			if (atif->status & ATIF_PROBE_FAIL)
4768c2ecf20Sopenharmony_ci				break;
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ciint aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	int hash, retval = -EPROTONOSUPPORT;
4848c2ecf20Sopenharmony_ci	struct aarp_entry *entry;
4858c2ecf20Sopenharmony_ci	unsigned int count;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/*
4888c2ecf20Sopenharmony_ci	 * we don't currently support LocalTalk or PPP for proxy AARP;
4898c2ecf20Sopenharmony_ci	 * if someone wants to try and add it, have fun
4908c2ecf20Sopenharmony_ci	 */
4918c2ecf20Sopenharmony_ci	if (atif->dev->type == ARPHRD_LOCALTLK ||
4928c2ecf20Sopenharmony_ci	    atif->dev->type == ARPHRD_PPP)
4938c2ecf20Sopenharmony_ci		goto out;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/*
4968c2ecf20Sopenharmony_ci	 * create a new AARP entry with the flags set to be published --
4978c2ecf20Sopenharmony_ci	 * we need this one to hang around even if it's in use
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci	entry = aarp_alloc();
5008c2ecf20Sopenharmony_ci	retval = -ENOMEM;
5018c2ecf20Sopenharmony_ci	if (!entry)
5028c2ecf20Sopenharmony_ci		goto out;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	entry->expires_at = -1;
5058c2ecf20Sopenharmony_ci	entry->status = ATIF_PROBE;
5068c2ecf20Sopenharmony_ci	entry->target_addr.s_node = sa->s_node;
5078c2ecf20Sopenharmony_ci	entry->target_addr.s_net = sa->s_net;
5088c2ecf20Sopenharmony_ci	entry->dev = atif->dev;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	hash = sa->s_node % (AARP_HASH_SIZE - 1);
5138c2ecf20Sopenharmony_ci	entry->next = proxies[hash];
5148c2ecf20Sopenharmony_ci	proxies[hash] = entry;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) {
5178c2ecf20Sopenharmony_ci		aarp_send_probe(atif->dev, sa);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		/* Defer 1/10th */
5208c2ecf20Sopenharmony_ci		write_unlock_bh(&aarp_lock);
5218c2ecf20Sopenharmony_ci		msleep(100);
5228c2ecf20Sopenharmony_ci		write_lock_bh(&aarp_lock);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci		if (entry->status & ATIF_PROBE_FAIL)
5258c2ecf20Sopenharmony_ci			break;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (entry->status & ATIF_PROBE_FAIL) {
5298c2ecf20Sopenharmony_ci		entry->expires_at = jiffies - 1; /* free the entry */
5308c2ecf20Sopenharmony_ci		retval = -EADDRINUSE; /* return network full */
5318c2ecf20Sopenharmony_ci	} else { /* clear the probing flag */
5328c2ecf20Sopenharmony_ci		entry->status &= ~ATIF_PROBE;
5338c2ecf20Sopenharmony_ci		retval = 1;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
5378c2ecf20Sopenharmony_ciout:
5388c2ecf20Sopenharmony_ci	return retval;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/* Send a DDP frame */
5428c2ecf20Sopenharmony_ciint aarp_send_ddp(struct net_device *dev, struct sk_buff *skb,
5438c2ecf20Sopenharmony_ci		  struct atalk_addr *sa, void *hwaddr)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	static char ddp_eth_multicast[ETH_ALEN] =
5468c2ecf20Sopenharmony_ci		{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
5478c2ecf20Sopenharmony_ci	int hash;
5488c2ecf20Sopenharmony_ci	struct aarp_entry *a;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* Check for LocalTalk first */
5538c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_LOCALTLK) {
5548c2ecf20Sopenharmony_ci		struct atalk_addr *at = atalk_find_dev_addr(dev);
5558c2ecf20Sopenharmony_ci		struct ddpehdr *ddp = (struct ddpehdr *)skb->data;
5568c2ecf20Sopenharmony_ci		int ft = 2;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		/*
5598c2ecf20Sopenharmony_ci		 * Compressible ?
5608c2ecf20Sopenharmony_ci		 *
5618c2ecf20Sopenharmony_ci		 * IFF: src_net == dest_net == device_net
5628c2ecf20Sopenharmony_ci		 * (zero matches anything)
5638c2ecf20Sopenharmony_ci		 */
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) &&
5668c2ecf20Sopenharmony_ci		    (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) {
5678c2ecf20Sopenharmony_ci			skb_pull(skb, sizeof(*ddp) - 4);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci			/*
5708c2ecf20Sopenharmony_ci			 *	The upper two remaining bytes are the port
5718c2ecf20Sopenharmony_ci			 *	numbers	we just happen to need. Now put the
5728c2ecf20Sopenharmony_ci			 *	length in the lower two.
5738c2ecf20Sopenharmony_ci			 */
5748c2ecf20Sopenharmony_ci			*((__be16 *)skb->data) = htons(skb->len);
5758c2ecf20Sopenharmony_ci			ft = 1;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci		/*
5788c2ecf20Sopenharmony_ci		 * Nice and easy. No AARP type protocols occur here so we can
5798c2ecf20Sopenharmony_ci		 * just shovel it out with a 3 byte LLAP header
5808c2ecf20Sopenharmony_ci		 */
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		skb_push(skb, 3);
5838c2ecf20Sopenharmony_ci		skb->data[0] = sa->s_node;
5848c2ecf20Sopenharmony_ci		skb->data[1] = at->s_node;
5858c2ecf20Sopenharmony_ci		skb->data[2] = ft;
5868c2ecf20Sopenharmony_ci		skb->dev     = dev;
5878c2ecf20Sopenharmony_ci		goto sendit;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* On a PPP link we neither compress nor aarp.  */
5918c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_PPP) {
5928c2ecf20Sopenharmony_ci		skb->protocol = htons(ETH_P_PPPTALK);
5938c2ecf20Sopenharmony_ci		skb->dev = dev;
5948c2ecf20Sopenharmony_ci		goto sendit;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* Non ELAP we cannot do. */
5988c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_ETHER)
5998c2ecf20Sopenharmony_ci		goto free_it;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	skb->dev = dev;
6028c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_ATALK);
6038c2ecf20Sopenharmony_ci	hash = sa->s_node % (AARP_HASH_SIZE - 1);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	/* Do we have a resolved entry? */
6068c2ecf20Sopenharmony_ci	if (sa->s_node == ATADDR_BCAST) {
6078c2ecf20Sopenharmony_ci		/* Send it */
6088c2ecf20Sopenharmony_ci		ddp_dl->request(ddp_dl, skb, ddp_eth_multicast);
6098c2ecf20Sopenharmony_ci		goto sent;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
6138c2ecf20Sopenharmony_ci	a = __aarp_find_entry(resolved[hash], dev, sa);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (a) { /* Return 1 and fill in the address */
6168c2ecf20Sopenharmony_ci		a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10);
6178c2ecf20Sopenharmony_ci		ddp_dl->request(ddp_dl, skb, a->hwaddr);
6188c2ecf20Sopenharmony_ci		write_unlock_bh(&aarp_lock);
6198c2ecf20Sopenharmony_ci		goto sent;
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* Do we have an unresolved entry: This is the less common path */
6238c2ecf20Sopenharmony_ci	a = __aarp_find_entry(unresolved[hash], dev, sa);
6248c2ecf20Sopenharmony_ci	if (a) { /* Queue onto the unresolved queue */
6258c2ecf20Sopenharmony_ci		skb_queue_tail(&a->packet_queue, skb);
6268c2ecf20Sopenharmony_ci		goto out_unlock;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* Allocate a new entry */
6308c2ecf20Sopenharmony_ci	a = aarp_alloc();
6318c2ecf20Sopenharmony_ci	if (!a) {
6328c2ecf20Sopenharmony_ci		/* Whoops slipped... good job it's an unreliable protocol 8) */
6338c2ecf20Sopenharmony_ci		write_unlock_bh(&aarp_lock);
6348c2ecf20Sopenharmony_ci		goto free_it;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/* Set up the queue */
6388c2ecf20Sopenharmony_ci	skb_queue_tail(&a->packet_queue, skb);
6398c2ecf20Sopenharmony_ci	a->expires_at	 = jiffies + sysctl_aarp_resolve_time;
6408c2ecf20Sopenharmony_ci	a->dev		 = dev;
6418c2ecf20Sopenharmony_ci	a->next		 = unresolved[hash];
6428c2ecf20Sopenharmony_ci	a->target_addr	 = *sa;
6438c2ecf20Sopenharmony_ci	a->xmit_count	 = 0;
6448c2ecf20Sopenharmony_ci	unresolved[hash] = a;
6458c2ecf20Sopenharmony_ci	unresolved_count++;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/* Send an initial request for the address */
6488c2ecf20Sopenharmony_ci	__aarp_send_query(a);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/*
6518c2ecf20Sopenharmony_ci	 * Switch to fast timer if needed (That is if this is the first
6528c2ecf20Sopenharmony_ci	 * unresolved entry to get added)
6538c2ecf20Sopenharmony_ci	 */
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (unresolved_count == 1)
6568c2ecf20Sopenharmony_ci		mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* Now finally, it is safe to drop the lock. */
6598c2ecf20Sopenharmony_ciout_unlock:
6608c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Tell the ddp layer we have taken over for this frame. */
6638c2ecf20Sopenharmony_ci	goto sent;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cisendit:
6668c2ecf20Sopenharmony_ci	if (skb->sk)
6678c2ecf20Sopenharmony_ci		skb->priority = skb->sk->sk_priority;
6688c2ecf20Sopenharmony_ci	if (dev_queue_xmit(skb))
6698c2ecf20Sopenharmony_ci		goto drop;
6708c2ecf20Sopenharmony_cisent:
6718c2ecf20Sopenharmony_ci	return NET_XMIT_SUCCESS;
6728c2ecf20Sopenharmony_cifree_it:
6738c2ecf20Sopenharmony_ci	kfree_skb(skb);
6748c2ecf20Sopenharmony_cidrop:
6758c2ecf20Sopenharmony_ci	return NET_XMIT_DROP;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(aarp_send_ddp);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci/*
6808c2ecf20Sopenharmony_ci *	An entry in the aarp unresolved queue has become resolved. Send
6818c2ecf20Sopenharmony_ci *	all the frames queued under it.
6828c2ecf20Sopenharmony_ci *
6838c2ecf20Sopenharmony_ci *	Must run under aarp_lock.
6848c2ecf20Sopenharmony_ci */
6858c2ecf20Sopenharmony_cistatic void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a,
6868c2ecf20Sopenharmony_ci			    int hash)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	while (*list)
6918c2ecf20Sopenharmony_ci		if (*list == a) {
6928c2ecf20Sopenharmony_ci			unresolved_count--;
6938c2ecf20Sopenharmony_ci			*list = a->next;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci			/* Move into the resolved list */
6968c2ecf20Sopenharmony_ci			a->next = resolved[hash];
6978c2ecf20Sopenharmony_ci			resolved[hash] = a;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci			/* Kick frames off */
7008c2ecf20Sopenharmony_ci			while ((skb = skb_dequeue(&a->packet_queue)) != NULL) {
7018c2ecf20Sopenharmony_ci				a->expires_at = jiffies +
7028c2ecf20Sopenharmony_ci						sysctl_aarp_expiry_time * 10;
7038c2ecf20Sopenharmony_ci				ddp_dl->request(ddp_dl, skb, a->hwaddr);
7048c2ecf20Sopenharmony_ci			}
7058c2ecf20Sopenharmony_ci		} else
7068c2ecf20Sopenharmony_ci			list = &((*list)->next);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/*
7108c2ecf20Sopenharmony_ci *	This is called by the SNAP driver whenever we see an AARP SNAP
7118c2ecf20Sopenharmony_ci *	frame. We currently only support Ethernet.
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_cistatic int aarp_rcv(struct sk_buff *skb, struct net_device *dev,
7148c2ecf20Sopenharmony_ci		    struct packet_type *pt, struct net_device *orig_dev)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	struct elapaarp *ea = aarp_hdr(skb);
7178c2ecf20Sopenharmony_ci	int hash, ret = 0;
7188c2ecf20Sopenharmony_ci	__u16 function;
7198c2ecf20Sopenharmony_ci	struct aarp_entry *a;
7208c2ecf20Sopenharmony_ci	struct atalk_addr sa, *ma, da;
7218c2ecf20Sopenharmony_ci	struct atalk_iface *ifa;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
7248c2ecf20Sopenharmony_ci		goto out0;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	/* We only do Ethernet SNAP AARP. */
7278c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_ETHER)
7288c2ecf20Sopenharmony_ci		goto out0;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/* Frame size ok? */
7318c2ecf20Sopenharmony_ci	if (!skb_pull(skb, sizeof(*ea)))
7328c2ecf20Sopenharmony_ci		goto out0;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	function = ntohs(ea->function);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	/* Sanity check fields. */
7378c2ecf20Sopenharmony_ci	if (function < AARP_REQUEST || function > AARP_PROBE ||
7388c2ecf20Sopenharmony_ci	    ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN ||
7398c2ecf20Sopenharmony_ci	    ea->pa_src_zero || ea->pa_dst_zero)
7408c2ecf20Sopenharmony_ci		goto out0;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/* Looks good. */
7438c2ecf20Sopenharmony_ci	hash = ea->pa_src_node % (AARP_HASH_SIZE - 1);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	/* Build an address. */
7468c2ecf20Sopenharmony_ci	sa.s_node = ea->pa_src_node;
7478c2ecf20Sopenharmony_ci	sa.s_net = ea->pa_src_net;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	/* Process the packet. Check for replies of me. */
7508c2ecf20Sopenharmony_ci	ifa = atalk_find_dev(dev);
7518c2ecf20Sopenharmony_ci	if (!ifa)
7528c2ecf20Sopenharmony_ci		goto out1;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (ifa->status & ATIF_PROBE &&
7558c2ecf20Sopenharmony_ci	    ifa->address.s_node == ea->pa_dst_node &&
7568c2ecf20Sopenharmony_ci	    ifa->address.s_net == ea->pa_dst_net) {
7578c2ecf20Sopenharmony_ci		ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */
7588c2ecf20Sopenharmony_ci		goto out1;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* Check for replies of proxy AARP entries */
7628c2ecf20Sopenharmony_ci	da.s_node = ea->pa_dst_node;
7638c2ecf20Sopenharmony_ci	da.s_net  = ea->pa_dst_net;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
7668c2ecf20Sopenharmony_ci	a = __aarp_find_entry(proxies[hash], dev, &da);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	if (a && a->status & ATIF_PROBE) {
7698c2ecf20Sopenharmony_ci		a->status |= ATIF_PROBE_FAIL;
7708c2ecf20Sopenharmony_ci		/*
7718c2ecf20Sopenharmony_ci		 * we do not respond to probe or request packets for
7728c2ecf20Sopenharmony_ci		 * this address while we are probing this address
7738c2ecf20Sopenharmony_ci		 */
7748c2ecf20Sopenharmony_ci		goto unlock;
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	switch (function) {
7788c2ecf20Sopenharmony_ci	case AARP_REPLY:
7798c2ecf20Sopenharmony_ci		if (!unresolved_count)	/* Speed up */
7808c2ecf20Sopenharmony_ci			break;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		/* Find the entry.  */
7838c2ecf20Sopenharmony_ci		a = __aarp_find_entry(unresolved[hash], dev, &sa);
7848c2ecf20Sopenharmony_ci		if (!a || dev != a->dev)
7858c2ecf20Sopenharmony_ci			break;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		/* We can fill one in - this is good. */
7888c2ecf20Sopenharmony_ci		ether_addr_copy(a->hwaddr, ea->hw_src);
7898c2ecf20Sopenharmony_ci		__aarp_resolved(&unresolved[hash], a, hash);
7908c2ecf20Sopenharmony_ci		if (!unresolved_count)
7918c2ecf20Sopenharmony_ci			mod_timer(&aarp_timer,
7928c2ecf20Sopenharmony_ci				  jiffies + sysctl_aarp_expiry_time);
7938c2ecf20Sopenharmony_ci		break;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	case AARP_REQUEST:
7968c2ecf20Sopenharmony_ci	case AARP_PROBE:
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci		/*
7998c2ecf20Sopenharmony_ci		 * If it is my address set ma to my address and reply.
8008c2ecf20Sopenharmony_ci		 * We can treat probe and request the same.  Probe
8018c2ecf20Sopenharmony_ci		 * simply means we shouldn't cache the querying host,
8028c2ecf20Sopenharmony_ci		 * as in a probe they are proposing an address not
8038c2ecf20Sopenharmony_ci		 * using one.
8048c2ecf20Sopenharmony_ci		 *
8058c2ecf20Sopenharmony_ci		 * Support for proxy-AARP added. We check if the
8068c2ecf20Sopenharmony_ci		 * address is one of our proxies before we toss the
8078c2ecf20Sopenharmony_ci		 * packet out.
8088c2ecf20Sopenharmony_ci		 */
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci		sa.s_node = ea->pa_dst_node;
8118c2ecf20Sopenharmony_ci		sa.s_net  = ea->pa_dst_net;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci		/* See if we have a matching proxy. */
8148c2ecf20Sopenharmony_ci		ma = __aarp_proxy_find(dev, &sa);
8158c2ecf20Sopenharmony_ci		if (!ma)
8168c2ecf20Sopenharmony_ci			ma = &ifa->address;
8178c2ecf20Sopenharmony_ci		else { /* We need to make a copy of the entry. */
8188c2ecf20Sopenharmony_ci			da.s_node = sa.s_node;
8198c2ecf20Sopenharmony_ci			da.s_net = sa.s_net;
8208c2ecf20Sopenharmony_ci			ma = &da;
8218c2ecf20Sopenharmony_ci		}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci		if (function == AARP_PROBE) {
8248c2ecf20Sopenharmony_ci			/*
8258c2ecf20Sopenharmony_ci			 * A probe implies someone trying to get an
8268c2ecf20Sopenharmony_ci			 * address. So as a precaution flush any
8278c2ecf20Sopenharmony_ci			 * entries we have for this address.
8288c2ecf20Sopenharmony_ci			 */
8298c2ecf20Sopenharmony_ci			a = __aarp_find_entry(resolved[sa.s_node %
8308c2ecf20Sopenharmony_ci						       (AARP_HASH_SIZE - 1)],
8318c2ecf20Sopenharmony_ci					      skb->dev, &sa);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci			/*
8348c2ecf20Sopenharmony_ci			 * Make it expire next tick - that avoids us
8358c2ecf20Sopenharmony_ci			 * getting into a probe/flush/learn/probe/
8368c2ecf20Sopenharmony_ci			 * flush/learn cycle during probing of a slow
8378c2ecf20Sopenharmony_ci			 * to respond host addr.
8388c2ecf20Sopenharmony_ci			 */
8398c2ecf20Sopenharmony_ci			if (a) {
8408c2ecf20Sopenharmony_ci				a->expires_at = jiffies - 1;
8418c2ecf20Sopenharmony_ci				mod_timer(&aarp_timer, jiffies +
8428c2ecf20Sopenharmony_ci					  sysctl_aarp_tick_time);
8438c2ecf20Sopenharmony_ci			}
8448c2ecf20Sopenharmony_ci		}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		if (sa.s_node != ma->s_node)
8478c2ecf20Sopenharmony_ci			break;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci		if (sa.s_net && ma->s_net && sa.s_net != ma->s_net)
8508c2ecf20Sopenharmony_ci			break;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		sa.s_node = ea->pa_src_node;
8538c2ecf20Sopenharmony_ci		sa.s_net = ea->pa_src_net;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		/* aarp_my_address has found the address to use for us.
8568c2ecf20Sopenharmony_ci		 */
8578c2ecf20Sopenharmony_ci		aarp_send_reply(dev, ma, &sa, ea->hw_src);
8588c2ecf20Sopenharmony_ci		break;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ciunlock:
8628c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
8638c2ecf20Sopenharmony_ciout1:
8648c2ecf20Sopenharmony_ci	ret = 1;
8658c2ecf20Sopenharmony_ciout0:
8668c2ecf20Sopenharmony_ci	kfree_skb(skb);
8678c2ecf20Sopenharmony_ci	return ret;
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cistatic struct notifier_block aarp_notifier = {
8718c2ecf20Sopenharmony_ci	.notifier_call = aarp_device_event,
8728c2ecf20Sopenharmony_ci};
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 };
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ciint __init aarp_proto_init(void)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	int rc;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv);
8818c2ecf20Sopenharmony_ci	if (!aarp_dl) {
8828c2ecf20Sopenharmony_ci		printk(KERN_CRIT "Unable to register AARP with SNAP.\n");
8838c2ecf20Sopenharmony_ci		return -ENOMEM;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci	timer_setup(&aarp_timer, aarp_expire_timeout, 0);
8868c2ecf20Sopenharmony_ci	aarp_timer.expires  = jiffies + sysctl_aarp_expiry_time;
8878c2ecf20Sopenharmony_ci	add_timer(&aarp_timer);
8888c2ecf20Sopenharmony_ci	rc = register_netdevice_notifier(&aarp_notifier);
8898c2ecf20Sopenharmony_ci	if (rc) {
8908c2ecf20Sopenharmony_ci		del_timer_sync(&aarp_timer);
8918c2ecf20Sopenharmony_ci		unregister_snap_client(aarp_dl);
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	return rc;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/* Remove the AARP entries associated with a device. */
8978c2ecf20Sopenharmony_civoid aarp_device_down(struct net_device *dev)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	int ct;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	write_lock_bh(&aarp_lock);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
9048c2ecf20Sopenharmony_ci		__aarp_expire_device(&resolved[ct], dev);
9058c2ecf20Sopenharmony_ci		__aarp_expire_device(&unresolved[ct], dev);
9068c2ecf20Sopenharmony_ci		__aarp_expire_device(&proxies[ct], dev);
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	write_unlock_bh(&aarp_lock);
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
9138c2ecf20Sopenharmony_ci/*
9148c2ecf20Sopenharmony_ci * Get the aarp entry that is in the chain described
9158c2ecf20Sopenharmony_ci * by the iterator.
9168c2ecf20Sopenharmony_ci * If pos is set then skip till that index.
9178c2ecf20Sopenharmony_ci * pos = 1 is the first entry
9188c2ecf20Sopenharmony_ci */
9198c2ecf20Sopenharmony_cistatic struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	int ct = iter->bucket;
9228c2ecf20Sopenharmony_ci	struct aarp_entry **table = iter->table;
9238c2ecf20Sopenharmony_ci	loff_t off = 0;
9248c2ecf20Sopenharmony_ci	struct aarp_entry *entry;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci rescan:
9278c2ecf20Sopenharmony_ci	while (ct < AARP_HASH_SIZE) {
9288c2ecf20Sopenharmony_ci		for (entry = table[ct]; entry; entry = entry->next) {
9298c2ecf20Sopenharmony_ci			if (!pos || ++off == *pos) {
9308c2ecf20Sopenharmony_ci				iter->table = table;
9318c2ecf20Sopenharmony_ci				iter->bucket = ct;
9328c2ecf20Sopenharmony_ci				return entry;
9338c2ecf20Sopenharmony_ci			}
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci		++ct;
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (table == resolved) {
9398c2ecf20Sopenharmony_ci		ct = 0;
9408c2ecf20Sopenharmony_ci		table = unresolved;
9418c2ecf20Sopenharmony_ci		goto rescan;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci	if (table == unresolved) {
9448c2ecf20Sopenharmony_ci		ct = 0;
9458c2ecf20Sopenharmony_ci		table = proxies;
9468c2ecf20Sopenharmony_ci		goto rescan;
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci	return NULL;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic void *aarp_seq_start(struct seq_file *seq, loff_t *pos)
9528c2ecf20Sopenharmony_ci	__acquires(aarp_lock)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct aarp_iter_state *iter = seq->private;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	read_lock_bh(&aarp_lock);
9578c2ecf20Sopenharmony_ci	iter->table     = resolved;
9588c2ecf20Sopenharmony_ci	iter->bucket    = 0;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic void *aarp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	struct aarp_entry *entry = v;
9668c2ecf20Sopenharmony_ci	struct aarp_iter_state *iter = seq->private;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	++*pos;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	/* first line after header */
9718c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN)
9728c2ecf20Sopenharmony_ci		entry = iter_next(iter, NULL);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* next entry in current bucket */
9758c2ecf20Sopenharmony_ci	else if (entry->next)
9768c2ecf20Sopenharmony_ci		entry = entry->next;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	/* next bucket or table */
9798c2ecf20Sopenharmony_ci	else {
9808c2ecf20Sopenharmony_ci		++iter->bucket;
9818c2ecf20Sopenharmony_ci		entry = iter_next(iter, NULL);
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci	return entry;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic void aarp_seq_stop(struct seq_file *seq, void *v)
9878c2ecf20Sopenharmony_ci	__releases(aarp_lock)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	read_unlock_bh(&aarp_lock);
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic const char *dt2str(unsigned long ticks)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	static char buf[32];
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100) / HZ);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	return buf;
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_cistatic int aarp_seq_show(struct seq_file *seq, void *v)
10028c2ecf20Sopenharmony_ci{
10038c2ecf20Sopenharmony_ci	struct aarp_iter_state *iter = seq->private;
10048c2ecf20Sopenharmony_ci	struct aarp_entry *entry = v;
10058c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN)
10088c2ecf20Sopenharmony_ci		seq_puts(seq,
10098c2ecf20Sopenharmony_ci			 "Address  Interface   Hardware Address"
10108c2ecf20Sopenharmony_ci			 "   Expires LastSend  Retry Status\n");
10118c2ecf20Sopenharmony_ci	else {
10128c2ecf20Sopenharmony_ci		seq_printf(seq, "%04X:%02X  %-12s",
10138c2ecf20Sopenharmony_ci			   ntohs(entry->target_addr.s_net),
10148c2ecf20Sopenharmony_ci			   (unsigned int) entry->target_addr.s_node,
10158c2ecf20Sopenharmony_ci			   entry->dev ? entry->dev->name : "????");
10168c2ecf20Sopenharmony_ci		seq_printf(seq, "%pM", entry->hwaddr);
10178c2ecf20Sopenharmony_ci		seq_printf(seq, " %8s",
10188c2ecf20Sopenharmony_ci			   dt2str((long)entry->expires_at - (long)now));
10198c2ecf20Sopenharmony_ci		if (iter->table == unresolved)
10208c2ecf20Sopenharmony_ci			seq_printf(seq, " %8s %6hu",
10218c2ecf20Sopenharmony_ci				   dt2str(now - entry->last_sent),
10228c2ecf20Sopenharmony_ci				   entry->xmit_count);
10238c2ecf20Sopenharmony_ci		else
10248c2ecf20Sopenharmony_ci			seq_puts(seq, "                ");
10258c2ecf20Sopenharmony_ci		seq_printf(seq, " %s\n",
10268c2ecf20Sopenharmony_ci			   (iter->table == resolved) ? "resolved"
10278c2ecf20Sopenharmony_ci			   : (iter->table == unresolved) ? "unresolved"
10288c2ecf20Sopenharmony_ci			   : (iter->table == proxies) ? "proxies"
10298c2ecf20Sopenharmony_ci			   : "unknown");
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci	return 0;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ciconst struct seq_operations aarp_seq_ops = {
10358c2ecf20Sopenharmony_ci	.start  = aarp_seq_start,
10368c2ecf20Sopenharmony_ci	.next   = aarp_seq_next,
10378c2ecf20Sopenharmony_ci	.stop   = aarp_seq_stop,
10388c2ecf20Sopenharmony_ci	.show   = aarp_seq_show,
10398c2ecf20Sopenharmony_ci};
10408c2ecf20Sopenharmony_ci#endif
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci/* General module cleanup. Called from cleanup_module() in ddp.c. */
10438c2ecf20Sopenharmony_civoid aarp_cleanup_module(void)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	del_timer_sync(&aarp_timer);
10468c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&aarp_notifier);
10478c2ecf20Sopenharmony_ci	unregister_snap_client(aarp_dl);
10488c2ecf20Sopenharmony_ci	aarp_purge();
10498c2ecf20Sopenharmony_ci}
1050