/*
* AARP: An implementation of the AppleTalk AARP protocol for
* Ethernet 'ELAP'.
*
* Alan Cox <Alan.Cox@linux.org>
*
* This doesn't fit cleanly with the IP arp. Potentially we can use
* the generic neighbour discovery code to clean this up.
*
* FIXME:
* We ought to handle the retransmits with a single list and a
* separate fast timer for when it is needed.
* Use neighbour discovery code.
* Token Ring Support.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*
* References:
* Inside AppleTalk (2nd Ed).
* Fixes:
* Jaume Grau - flush caches on AARP_PROBE
* Rob Newberry - Added proxy AARP and AARP proc fs,
* moved probing from DDP module.
* Arnaldo C. Melo - don't mangle rx packets
*
*/
#include <linux/if_arp.h>
#include <net/sock.h>
#include <net/datalink.h>
#include <net/psnap.h>
#include <linux/atalk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME;
int sysctl_aarp_tick_time = AARP_TICK_TIME;
int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT;
int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME;
/* Lists of aarp entries */
/**
* struct aarp_entry - AARP entry
* @last_sent - Last time we xmitted the aarp request
* @packet_queue - Queue of frames wait for resolution
* @status - Used for proxy AARP
* expires_at - Entry expiry time
* target_addr - DDP Address
* dev - Device to use
* hwaddr - Physical i/f address of target/router
* xmit_count - When this hits 10 we give up
* next - Next entry in chain
*/
struct aarp_entry {
/* These first two are only used for unresolved entries */
unsigned long last_sent;
struct sk_buff_head packet_queue;
int status;
unsigned long expires_at;
struct atalk_addr target_addr;
struct net_device *dev;
char hwaddr[6];
unsigned short xmit_count;
struct aarp_entry *next;
};
/* Hashed list of resolved, unresolved and proxy entries */
static struct aarp_entry *resolved[AARP_HASH_SIZE];
static struct aarp_entry *unresolved[AARP_HASH_SIZE];
static struct aarp_entry *proxies[AARP_HASH_SIZE];
static int unresolved_count;
/* One lock protects it all. */
static DEFINE_RWLOCK(aarp_lock);
/* Used to walk the list and purge/kick entries. */
static struct timer_list aarp_timer;
/*
* Delete an aarp queue
*
* Must run under aarp_lock.
*/
static void __aarp_expire(struct aarp_entry *a)
{
skb_queue_purge(&a->packet_queue);
kfree(a);
}
/*
* Send an aarp queue entry request
*
* Must run under aarp_lock.
*/
static void __aarp_send_query(struct aarp_entry *a)
{
static unsigned char aarp_eth_multicast[ETH_ALEN] =
{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
struct net_device *dev = a->dev;
struct elapaarp *eah;
int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
struct atalk_addr *sat = atalk_find_dev_addr(dev);
if (!skb)
return;
if (!sat) {
kfree_skb(skb);
return;
}
/* Set up the buffer */
skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
skb_put(skb, sizeof(*eah));
skb->protocol = htons(ETH_P_ATALK);
skb->dev = dev;
eah = aarp_hdr(skb);
/* Set up the ARP */
eah->hw_type = htons(AARP_HW_TYPE_ETHERNET);
eah->pa_type = htons(ETH_P_ATALK);
eah->hw_len = ETH_ALEN;
eah->pa_len = AARP_PA_ALEN;
eah->function = htons(AARP_REQUEST);
memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN);
eah->pa_src_zero = 0;
eah->pa_src_net = sat->s_net;
eah->pa_src_node = sat->s_node;
memset(eah->hw_dst, '\0', ETH_ALEN);
eah->pa_dst_zero = 0;
eah->pa_dst_net = a->target_addr.s_net;
eah->pa_dst_node = a->target_addr.s_node;
/* Send it */
aarp_dl->request(aarp_dl, skb, aarp_eth_multicast);
/* Update the sending count */
a->xmit_count++;
a->last_sent = jiffies;
}
/* This runs under aarp_lock and in softint context, so only atomic memory
* allocations can be used. */
static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us,
struct atalk_addr *them, unsigned char *sha)
{
struct elapaarp *