/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <net/arp.h>
#include "main.h"
#include "hash.h"
#include "distributed-arp-table.h"
#include "hard-interface.h"
#include "originator.h"
#include "send.h"
#include "types.h"
#include "translation-table.h"
#include "unicast.h"
static void batadv_dat_purge(struct work_struct *work);
/**
* batadv_dat_start_timer - initialise the DAT periodic worker
* @bat_priv: the bat priv with all the soft interface information
*/
static void batadv_dat_start_timer(struct batadv_priv *bat_priv)
{
INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge);
queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work,
msecs_to_jiffies(10000));
}
/**
* batadv_dat_entry_free_ref - decrements the dat_entry refcounter and possibly
* free it
* @dat_entry: the oentry to free
*/
static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry)
{
if (atomic_dec_and_test(&dat_entry->refcount))
kfree_rcu(dat_entry, rcu);
}
/**
* batadv_dat_to_purge - checks whether a dat_entry has to be purged or not
* @dat_entry: the entry to check
*
* Returns true if the entry has to be purged now, false otherwise
*/
static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
{
return batadv_has_timed_out(dat_entry->last_update,
BATADV_DAT_ENTRY_TIMEOUT);
}
/**
* __batadv_dat_purge - delete entries from the DAT local storage
* @bat_priv: the bat priv with all the soft interface information
* @to_purge: function in charge to decide whether an entry has to be purged or
* not. This function takes the dat_entry as argument and has to
* returns a boolean value: true is the entry has to be deleted,
* false otherwise
*
* Loops over each entry in the DAT local storage and delete it if and only if
* the to_purge function passed as argument returns true
*/
static void __batadv_dat_purge(struct batadv_priv *bat_priv,
bool (*to_purge)(struct batadv_dat_entry *))
{
spinlock_t *list_lock; /* protects write access to the hash lists */
struct batadv_dat_entry *dat_entry;
struct hlist_node *node, *node_tmp;
struct hlist_head *head;
uint32_t i;
if (!bat_priv->dat.hash)
return;
for (i = 0; i < bat_priv->dat.hash->size; i++) {
head = &bat_priv->dat.hash->table[i];
list_lock = &bat_priv->dat.hash->list_locks[i];
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(dat_entry, node, node_tmp, head,
hash_entry) {
/* if an helper function has been passed as parameter,
* ask it if the entry has to be purged or not
*/
if (to_purge && !to_purge(dat_entry))
continue;
hlist_del_rcu(node);
batadv_dat_entry_free_ref(dat_entry);
}
spin_unlock_bh(list_lock);
}
}
/**
* batadv_dat_purge - periodic task that deletes old entries from the local DAT
* hash table
* @work: kernel work struct
*/
static void batadv_dat_purge(struct work_struct *work)
{
struct delayed_work *delayed_work;
struct batadv_priv_dat *priv_dat;
struct batadv_priv *bat_priv;
delayed_work = container_of(work, struct delayed_work, work);
priv_dat = container_of(delayed_work, struct batadv_priv_dat, work);
bat_priv = container_of(priv_dat, struct batadv_priv, dat);
__batadv_dat_purge(bat_priv, batadv_dat_to_purge);
batadv_dat_start_timer(bat_priv);
}
/**
* batadv_compare_dat - comparing function used in the local DAT hash table
* @node: node in the local table
* @data2: second object to compare the node to
*
* Returns 1 if the two entry are the same, 0 otherwise
*/
static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
{
const void *data1 = container_of(node, struct batadv_dat_entry,
hash_entry);
return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0);
}
/**
* batadv_arp_hw_src - extract the hw_src field from an ARP packet
* @skb: ARP packet
* @hdr_size: size of the possible header before the ARP packet
*
* Returns the value of the hw_src field in the ARP packet
*/
static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
{
uint8_t *addr;
addr = (uint8_t *)(skb->data