![Sven Eckelmann](/assets/img/avatar_default.png)
Free_netdev is registered as destructor in interface_setup for every soft_device. This destructor is automatically called from unregister_netdev and we must not call it again for the freed net_device. Signed-off-by: Sven Eckelmann <sven.eckelmann@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
297 lines
6.2 KiB
C
297 lines
6.2 KiB
C
/*
|
|
* Copyright (C) 2007-2010 B.A.T.M.A.N. contributors:
|
|
*
|
|
* Marek Lindner, Simon Wunderlich
|
|
*
|
|
* 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 "main.h"
|
|
#include "bat_sysfs.h"
|
|
#include "routing.h"
|
|
#include "send.h"
|
|
#include "originator.h"
|
|
#include "soft-interface.h"
|
|
#include "device.h"
|
|
#include "translation-table.h"
|
|
#include "hard-interface.h"
|
|
#include "types.h"
|
|
#include "vis.h"
|
|
#include "hash.h"
|
|
|
|
struct list_head if_list;
|
|
struct hlist_head forw_bat_list;
|
|
struct hlist_head forw_bcast_list;
|
|
struct hashtable_t *orig_hash;
|
|
|
|
DEFINE_SPINLOCK(orig_hash_lock);
|
|
DEFINE_SPINLOCK(forw_bat_list_lock);
|
|
DEFINE_SPINLOCK(forw_bcast_list_lock);
|
|
|
|
atomic_t vis_interval;
|
|
atomic_t bcast_queue_left;
|
|
atomic_t batman_queue_left;
|
|
|
|
int16_t num_hna;
|
|
|
|
struct net_device *soft_device;
|
|
|
|
unsigned char broadcastAddr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
atomic_t module_state;
|
|
|
|
static struct packet_type batman_adv_packet_type __read_mostly = {
|
|
.type = __constant_htons(ETH_P_BATMAN),
|
|
.func = batman_skb_recv,
|
|
};
|
|
|
|
struct workqueue_struct *bat_event_workqueue;
|
|
|
|
#ifdef CONFIG_BATMAN_ADV_DEBUG
|
|
int debug;
|
|
|
|
module_param(debug, int, 0644);
|
|
|
|
int bat_debug_type(int type)
|
|
{
|
|
return debug & type;
|
|
}
|
|
#endif
|
|
|
|
int init_module(void)
|
|
{
|
|
int retval;
|
|
|
|
INIT_LIST_HEAD(&if_list);
|
|
INIT_HLIST_HEAD(&forw_bat_list);
|
|
INIT_HLIST_HEAD(&forw_bcast_list);
|
|
|
|
atomic_set(&module_state, MODULE_INACTIVE);
|
|
|
|
atomic_set(&vis_interval, 1000);/* TODO: raise this later, this is only
|
|
* for debugging now. */
|
|
atomic_set(&bcast_queue_left, BCAST_QUEUE_LEN);
|
|
atomic_set(&batman_queue_left, BATMAN_QUEUE_LEN);
|
|
|
|
/* the name should not be longer than 10 chars - see
|
|
* http://lwn.net/Articles/23634/ */
|
|
bat_event_workqueue = create_singlethread_workqueue("bat_events");
|
|
|
|
if (!bat_event_workqueue)
|
|
return -ENOMEM;
|
|
|
|
bat_device_init();
|
|
|
|
/* initialize layer 2 interface */
|
|
soft_device = alloc_netdev(sizeof(struct bat_priv) , "bat%d",
|
|
interface_setup);
|
|
|
|
if (!soft_device) {
|
|
printk(KERN_ERR "batman-adv:"
|
|
"Unable to allocate the batman interface\n");
|
|
goto end;
|
|
}
|
|
|
|
retval = register_netdev(soft_device);
|
|
|
|
if (retval < 0) {
|
|
printk(KERN_ERR "batman-adv:"
|
|
"Unable to register the batman interface: %i\n", retval);
|
|
goto free_soft_device;
|
|
}
|
|
|
|
retval = sysfs_add_meshif(soft_device);
|
|
|
|
if (retval < 0)
|
|
goto unreg_soft_device;
|
|
|
|
register_netdevice_notifier(&hard_if_notifier);
|
|
dev_add_pack(&batman_adv_packet_type);
|
|
|
|
printk(KERN_INFO "batman-adv:"
|
|
"B.A.T.M.A.N. advanced %s%s (compatibility version %i) loaded\n",
|
|
SOURCE_VERSION, REVISION_VERSION_STR, COMPAT_VERSION);
|
|
|
|
return 0;
|
|
|
|
unreg_soft_device:
|
|
unregister_netdev(soft_device);
|
|
soft_device = NULL;
|
|
return -ENOMEM;
|
|
|
|
free_soft_device:
|
|
free_netdev(soft_device);
|
|
soft_device = NULL;
|
|
end:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
void cleanup_module(void)
|
|
{
|
|
deactivate_module();
|
|
|
|
unregister_netdevice_notifier(&hard_if_notifier);
|
|
hardif_remove_interfaces();
|
|
|
|
if (soft_device) {
|
|
sysfs_del_meshif(soft_device);
|
|
unregister_netdev(soft_device);
|
|
soft_device = NULL;
|
|
}
|
|
|
|
dev_remove_pack(&batman_adv_packet_type);
|
|
|
|
destroy_workqueue(bat_event_workqueue);
|
|
bat_event_workqueue = NULL;
|
|
}
|
|
|
|
/* activates the module, creates bat device, starts timer ... */
|
|
void activate_module(void)
|
|
{
|
|
if (originator_init() < 1)
|
|
goto err;
|
|
|
|
if (hna_local_init() < 1)
|
|
goto err;
|
|
|
|
if (hna_global_init() < 1)
|
|
goto err;
|
|
|
|
hna_local_add(soft_device->dev_addr);
|
|
|
|
if (bat_device_setup() < 1)
|
|
goto end;
|
|
|
|
if (vis_init() < 1)
|
|
goto err;
|
|
|
|
update_min_mtu();
|
|
atomic_set(&module_state, MODULE_ACTIVE);
|
|
goto end;
|
|
|
|
err:
|
|
printk(KERN_ERR "batman-adv:"
|
|
"Unable to allocate memory for mesh information structures: "
|
|
"out of mem ?\n");
|
|
deactivate_module();
|
|
end:
|
|
return;
|
|
}
|
|
|
|
/* shuts down the whole module.*/
|
|
void deactivate_module(void)
|
|
{
|
|
atomic_set(&module_state, MODULE_DEACTIVATING);
|
|
|
|
purge_outstanding_packets(NULL);
|
|
flush_workqueue(bat_event_workqueue);
|
|
|
|
vis_quit();
|
|
|
|
/* TODO: unregister BATMAN pack */
|
|
|
|
originator_free();
|
|
|
|
hna_local_free();
|
|
hna_global_free();
|
|
|
|
synchronize_net();
|
|
bat_device_destroy();
|
|
|
|
synchronize_rcu();
|
|
atomic_set(&module_state, MODULE_INACTIVE);
|
|
}
|
|
|
|
void inc_module_count(void)
|
|
{
|
|
try_module_get(THIS_MODULE);
|
|
}
|
|
|
|
void dec_module_count(void)
|
|
{
|
|
module_put(THIS_MODULE);
|
|
}
|
|
|
|
int addr_to_string(char *buff, uint8_t *addr)
|
|
{
|
|
return sprintf(buff, MAC_FMT,
|
|
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
|
|
}
|
|
|
|
/* returns 1 if they are the same originator */
|
|
|
|
int compare_orig(void *data1, void *data2)
|
|
{
|
|
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
|
|
}
|
|
|
|
/* hashfunction to choose an entry in a hash table of given size */
|
|
/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */
|
|
int choose_orig(void *data, int32_t size)
|
|
{
|
|
unsigned char *key = data;
|
|
uint32_t hash = 0;
|
|
size_t i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
hash += key[i];
|
|
hash += (hash << 10);
|
|
hash ^= (hash >> 6);
|
|
}
|
|
|
|
hash += (hash << 3);
|
|
hash ^= (hash >> 11);
|
|
hash += (hash << 15);
|
|
|
|
return hash % size;
|
|
}
|
|
|
|
int is_my_mac(uint8_t *addr)
|
|
{
|
|
struct batman_if *batman_if;
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(batman_if, &if_list, list) {
|
|
if ((batman_if->net_dev) &&
|
|
(compare_orig(batman_if->net_dev->dev_addr, addr))) {
|
|
rcu_read_unlock();
|
|
return 1;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
return 0;
|
|
|
|
}
|
|
|
|
int is_bcast(uint8_t *addr)
|
|
{
|
|
return (addr[0] == (uint8_t)0xff) && (addr[1] == (uint8_t)0xff);
|
|
}
|
|
|
|
int is_mcast(uint8_t *addr)
|
|
{
|
|
return *addr & 0x01;
|
|
}
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE);
|
|
#ifdef REVISION_VERSION
|
|
MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION);
|
|
#else
|
|
MODULE_VERSION(SOURCE_VERSION);
|
|
#endif
|