The following commit has been merged in the linux branch: commit f2204f0631662cc7431fb06c7897ac58df8e4b89 Merge: 89b7f227af4a8dbcaf9f17dcfe5ae150b5a00251 9ec2d8f98263fb1f14319f4d1607858c76d183b8 Author: Sven Eckelmann sven.eckelmann@gmx.de Date: Fri Jun 25 23:08:01 2010 +0200
Merge branch 'maint' into linux
Conflicts: drivers/staging/batman-adv/Makefile drivers/staging/batman-adv/Makefile.kbuild drivers/staging/batman-adv/compat.h
diff --combined drivers/staging/batman-adv/aggregation.h index 84401ca,71a91b3..71a91b3 --- a/drivers/staging/batman-adv/aggregation.h +++ b/drivers/staging/batman-adv/aggregation.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_AGGREGATION_H_ + #define _NET_BATMAN_ADV_AGGREGATION_H_ + #include "main.h"
/* is there another aggregated packet here? */ @@@ -36,3 -39,5 +39,5 @@@ void add_bat_packet_to_list(struct bat_ unsigned long send_time); void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, int packet_len, struct batman_if *if_incoming); + + #endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ diff --combined drivers/staging/batman-adv/bat_debugfs.h index 5cdd332,72df532..72df532 --- a/drivers/staging/batman-adv/bat_debugfs.h +++ b/drivers/staging/batman-adv/bat_debugfs.h @@@ -20,8 -20,8 +20,8 @@@ */
- #ifndef BAT_DEBUGFS_H - #define BAT_DEBUGFS_H + #ifndef _NET_BATMAN_ADV_DEBUGFS_H_ + #define _NET_BATMAN_ADV_DEBUGFS_H_
#define DEBUGFS_BAT_SUBDIR "batman_adv"
@@@ -30,4 -30,4 +30,4 @@@ void debugfs_destroy(void) int debugfs_add_meshif(struct net_device *dev); void debugfs_del_meshif(struct net_device *dev);
- #endif + #endif /* _NET_BATMAN_ADV_DEBUGFS_H_ */ diff --combined drivers/staging/batman-adv/bat_sysfs.h index cb45a91,7f186c0..7f186c0 --- a/drivers/staging/batman-adv/bat_sysfs.h +++ b/drivers/staging/batman-adv/bat_sysfs.h @@@ -20,8 -20,8 +20,8 @@@ */
- #ifndef BAT_SYSFS_H - #define BAT_SYSFS_H + #ifndef _NET_BATMAN_ADV_SYSFS_H_ + #define _NET_BATMAN_ADV_SYSFS_H_
#define SYSFS_IF_MESH_SUBDIR "mesh" #define SYSFS_IF_BAT_SUBDIR "batman_adv" @@@ -39,4 -39,4 +39,4 @@@ void sysfs_del_meshif(struct net_devic int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); void sysfs_del_hardif(struct kobject **hardif_obj);
- #endif + #endif /* _NET_BATMAN_ADV_SYSFS_H_ */ diff --combined drivers/staging/batman-adv/bitarray.h index dad13bf,01897d6..01897d6 --- a/drivers/staging/batman-adv/bitarray.h +++ b/drivers/staging/batman-adv/bitarray.h @@@ -19,6 -19,8 +19,8 @@@ * */
+ #ifndef _NET_BATMAN_ADV_BITARRAY_H_ + #define _NET_BATMAN_ADV_BITARRAY_H_
/* you should choose something big, if you don't want to waste cpu */ #define TYPE_OF_WORD unsigned long @@@ -40,3 -42,5 +42,5 @@@ char bit_get_packet(TYPE_OF_WORD *seq_b
/* count the hamming weight, how many good packets did we receive? */ int bit_packet_count(TYPE_OF_WORD *seq_bits); + + #endif /* _NET_BATMAN_ADV_BITARRAY_H_ */ diff --combined drivers/staging/batman-adv/hard-interface.h index 1e5fc3e,d5640b0..d5640b0 --- a/drivers/staging/batman-adv/hard-interface.h +++ b/drivers/staging/batman-adv/hard-interface.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_ + #define _NET_BATMAN_ADV_HARD_INTERFACE_H_ + #define IF_NOT_IN_USE 0 #define IF_TO_BE_REMOVED 1 #define IF_INACTIVE 2 @@@ -38,3 -41,5 +41,5 @@@ int batman_skb_recv(struct sk_buff *skb struct net_device *orig_dev); int hardif_min_mtu(void); void update_min_mtu(void); + + #endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ diff --combined drivers/staging/batman-adv/hash.h index 0505595,c483e11..c483e11 --- a/drivers/staging/batman-adv/hash.h +++ b/drivers/staging/batman-adv/hash.h @@@ -19,8 -19,9 +19,9 @@@ * */
- #ifndef _BATMAN_HASH_H - #define _BATMAN_HASH_H + #ifndef _NET_BATMAN_ADV_HASH_H_ + #define _NET_BATMAN_ADV_HASH_H_ + #define HASHIT(name) struct hash_it_t name = { \ .index = -1, .bucket = NULL, \ .prev_bucket = NULL, \ @@@ -95,4 -96,5 +96,5 @@@ struct hashtable_t *hash_resize(struct * the returned iterator to access the elements until hash_it_t returns NULL. */ struct hash_it_t *hash_iterate(struct hashtable_t *hash, struct hash_it_t *iter_in); - #endif + + #endif /* _NET_BATMAN_ADV_HASH_H_ */ diff --combined drivers/staging/batman-adv/icmp_socket.h index 2dc954a,bf9b348..bf9b348 --- a/drivers/staging/batman-adv/icmp_socket.h +++ b/drivers/staging/batman-adv/icmp_socket.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_ICMP_SOCKET_H_ + #define _NET_BATMAN_ADV_ICMP_SOCKET_H_ + #include "types.h"
#define ICMP_SOCKET "socket" @@@ -27,3 -30,5 +30,5 @@@ void bat_socket_init(void) int bat_socket_setup(struct bat_priv *bat_priv); void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet, size_t icmp_len); + + #endif /* _NET_BATMAN_ADV_ICMP_SOCKET_H_ */ diff --combined drivers/staging/batman-adv/main.c index 72fccb1,0000000..ae88806 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/main.c +++ b/drivers/staging/batman-adv/main.c @@@ -1,299 -1,0 +1,299 @@@ +/* + * 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 "bat_debugfs.h" +#include "routing.h" +#include "send.h" +#include "originator.h" +#include "soft-interface.h" +#include "icmp_socket.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 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}; ++unsigned char broadcast_addr[] = {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(&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_socket_init(); + debugfs_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; + + retval = debugfs_add_meshif(soft_device); + + if (retval < 0) + goto unreg_sysfs; + + 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_sysfs: + sysfs_del_meshif(soft_device); +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) { + debugfs_del_meshif(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, 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 (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(); + debugfs_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, "%pM", addr); +} + +/* 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 diff --combined drivers/staging/batman-adv/main.h index fe5ee51,e308673..e308673 --- a/drivers/staging/batman-adv/main.h +++ b/drivers/staging/batman-adv/main.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_MAIN_H_ + #define _NET_BATMAN_ADV_MAIN_H_ + /* Kernel Programming */ #define LINUX
@@@ -27,7 -30,7 +30,7 @@@ #define DRIVER_DESC "B.A.T.M.A.N. advanced" #define DRIVER_DEVICE "batman-adv"
- #define SOURCE_VERSION "2010.0.0" + #define SOURCE_VERSION "maint"
/* B.A.T.M.A.N. parameters */ @@@ -36,10 -39,10 +39,10 @@@ #define JITTER 20 #define TTL 50 /* Time To Live of broadcast messages */
- #define PURGE_TIMEOUT 200000 /* purge originators after time in ms if no + #define PURGE_TIMEOUT 200 /* purge originators after time in seconds if no * valid packet comes in -> TODO: check * influence on TQ_LOCAL_WINDOW_SIZE */ - #define LOCAL_HNA_TIMEOUT 3600000 + #define LOCAL_HNA_TIMEOUT 3600 /* in seconds */
#define TQ_LOCAL_WINDOW_SIZE 64 /* sliding packet range of received originator * messages in squence numbers (should be a @@@ -148,7 -151,7 +151,7 @@@ extern int16_t num_hna
extern struct net_device *soft_device;
- extern unsigned char broadcastAddr[]; + extern unsigned char broadcast_addr[]; extern atomic_t module_state; extern struct workqueue_struct *bat_event_workqueue;
@@@ -162,3 -165,5 +165,5 @@@ int choose_orig(void *data, int32_t siz int is_my_mac(uint8_t *addr); int is_bcast(uint8_t *addr); int is_mcast(uint8_t *addr); + + #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --combined drivers/staging/batman-adv/originator.c index 195c1ee,0000000..26bbf2d mode 100644,000000..100644 --- a/drivers/staging/batman-adv/originator.c +++ b/drivers/staging/batman-adv/originator.c @@@ -1,501 -1,0 +1,499 @@@ +/* + * Copyright (C) 2009-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 + * + */ + +/* increase the reference counter for this originator */ + +#include "main.h" +#include "originator.h" +#include "hash.h" +#include "translation-table.h" +#include "routing.h" +#include "hard-interface.h" + +static DECLARE_DELAYED_WORK(purge_orig_wq, purge_orig); + +static void start_purge_timer(void) +{ + queue_delayed_work(bat_event_workqueue, &purge_orig_wq, 1 * HZ); +} + +int originator_init(void) +{ + unsigned long flags; + if (orig_hash) + return 1; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_hash = hash_new(128, compare_orig, choose_orig); + + if (!orig_hash) + goto err; + + spin_unlock_irqrestore(&orig_hash_lock, flags); + start_purge_timer(); + return 1; + +err: + spin_unlock_irqrestore(&orig_hash_lock, flags); + return 0; +} + +struct neigh_node * +create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, + uint8_t *neigh, struct batman_if *if_incoming) +{ + struct neigh_node *neigh_node; + + bat_dbg(DBG_BATMAN, "Creating new last-hop neighbor of originator\n"); + + neigh_node = kzalloc(sizeof(struct neigh_node), GFP_ATOMIC); + if (!neigh_node) + return NULL; + + INIT_LIST_HEAD(&neigh_node->list); + + memcpy(neigh_node->addr, neigh, ETH_ALEN); + neigh_node->orig_node = orig_neigh_node; + neigh_node->if_incoming = if_incoming; + + list_add_tail(&neigh_node->list, &orig_node->neigh_list); + return neigh_node; +} + +static void free_orig_node(void *data) +{ + struct list_head *list_pos, *list_pos_tmp; + struct neigh_node *neigh_node; + struct orig_node *orig_node = (struct orig_node *)data; + + /* for all neighbors towards this originator ... */ + list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { + neigh_node = list_entry(list_pos, struct neigh_node, list); + + list_del(list_pos); + kfree(neigh_node); + } + + hna_global_del_orig(orig_node, "originator timed out"); + + kfree(orig_node->bcast_own); + kfree(orig_node->bcast_own_sum); + kfree(orig_node); +} + +void originator_free(void) +{ + unsigned long flags; + + if (!orig_hash) + return; + + cancel_delayed_work_sync(&purge_orig_wq); + + spin_lock_irqsave(&orig_hash_lock, flags); + hash_delete(orig_hash, free_orig_node); + orig_hash = NULL; + spin_unlock_irqrestore(&orig_hash_lock, flags); +} + +/* this function finds or creates an originator entry for the given + * address if it does not exits */ +struct orig_node *get_orig_node(uint8_t *addr) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct orig_node *orig_node; + struct hashtable_t *swaphash; + int size; + + orig_node = ((struct orig_node *)hash_find(orig_hash, addr)); + + if (orig_node != NULL) + return orig_node; + + bat_dbg(DBG_BATMAN, "Creating new originator: %pM\n", addr); + + orig_node = kzalloc(sizeof(struct orig_node), GFP_ATOMIC); + if (!orig_node) + return NULL; + + INIT_LIST_HEAD(&orig_node->neigh_list); + + memcpy(orig_node->orig, addr, ETH_ALEN); + orig_node->router = NULL; + orig_node->hna_buff = NULL; + orig_node->bcast_seqno_reset = jiffies - 1 + - msecs_to_jiffies(RESET_PROTECTION_MS); + orig_node->batman_seqno_reset = jiffies - 1 + - msecs_to_jiffies(RESET_PROTECTION_MS); + + size = bat_priv->num_ifaces * sizeof(TYPE_OF_WORD) * NUM_WORDS; + + orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bcast_own) + goto free_orig_node; + + size = bat_priv->num_ifaces * sizeof(uint8_t); + orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bcast_own_sum) + goto free_bcast_own; + + if (hash_add(orig_hash, orig_node) < 0) + goto free_bcast_own_sum; + + if (orig_hash->elements * 4 > orig_hash->size) { + swaphash = hash_resize(orig_hash, orig_hash->size * 2); + + if (swaphash == NULL) + printk(KERN_ERR + "batman-adv:Couldn't resize orig hash table\n"); + else + orig_hash = swaphash; + } + + return orig_node; +free_bcast_own_sum: + kfree(orig_node->bcast_own_sum); +free_bcast_own: + kfree(orig_node->bcast_own); +free_orig_node: + kfree(orig_node); + return NULL; +} + +static bool purge_orig_neighbors(struct orig_node *orig_node, + struct neigh_node **best_neigh_node) +{ + struct list_head *list_pos, *list_pos_tmp; + struct neigh_node *neigh_node; + bool neigh_purged = false; + + *best_neigh_node = NULL; + + /* for all neighbors towards this originator ... */ + list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { + neigh_node = list_entry(list_pos, struct neigh_node, list); + + if ((time_after(jiffies, - (neigh_node->last_valid + - ((PURGE_TIMEOUT * HZ) / 1000)))) || ++ neigh_node->last_valid + PURGE_TIMEOUT * HZ)) || + (neigh_node->if_incoming->if_status == + IF_TO_BE_REMOVED)) { + + if (neigh_node->if_incoming->if_status == + IF_TO_BE_REMOVED) + bat_dbg(DBG_BATMAN, + "neighbor purge: originator %pM, " + "neighbor: %pM, iface: %s\n", + orig_node->orig, neigh_node->addr, + neigh_node->if_incoming->dev); + else + bat_dbg(DBG_BATMAN, + "neighbor timeout: originator %pM, " + "neighbor: %pM, last_valid: %lu\n", + orig_node->orig, neigh_node->addr, + (neigh_node->last_valid / HZ)); + + neigh_purged = true; + list_del(list_pos); + kfree(neigh_node); + } else { + if ((*best_neigh_node == NULL) || + (neigh_node->tq_avg > (*best_neigh_node)->tq_avg)) + *best_neigh_node = neigh_node; + } + } + return neigh_purged; +} + +static bool purge_orig_node(struct orig_node *orig_node) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct neigh_node *best_neigh_node; + + if (time_after(jiffies, - (orig_node->last_valid + - ((2 * PURGE_TIMEOUT * HZ) / 1000)))) { ++ orig_node->last_valid + 2 * PURGE_TIMEOUT * HZ)) { + + bat_dbg(DBG_BATMAN, + "Originator timeout: originator %pM, last_valid %lu\n", + orig_node->orig, (orig_node->last_valid / HZ)); + return true; + } else { + if (purge_orig_neighbors(orig_node, &best_neigh_node)) { + update_routes(orig_node, best_neigh_node, + orig_node->hna_buff, + orig_node->hna_buff_len); + /* update bonding candidates, we could have lost + * some candidates. */ + update_bonding_candidates(bat_priv, orig_node); + } + } + + return false; +} + +void purge_orig(struct work_struct *work) +{ + HASHIT(hashit); + struct orig_node *orig_node; + unsigned long flags; + + spin_lock_irqsave(&orig_hash_lock, flags); + + /* for all origins... */ + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + if (purge_orig_node(orig_node)) { + hash_remove_bucket(orig_hash, &hashit); + free_orig_node(orig_node); + } + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* if work == NULL we were not called by the timer + * and thus do not need to re-arm the timer */ + if (work) + start_purge_timer(); +} + +int orig_seq_print_text(struct seq_file *seq, void *offset) +{ + HASHIT(hashit); + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct orig_node *orig_node; + struct neigh_node *neigh_node; + int batman_count = 0; + unsigned long flags; + char orig_str[ETH_STR_LEN], router_str[ETH_STR_LEN]; + + if ((!bat_priv->primary_if) || + (bat_priv->primary_if->if_status != IF_ACTIVE)) { + if (!bat_priv->primary_if) + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + + return seq_printf(seq, "BATMAN mesh %s " + "disabled - primary interface not active\n", + net_dev->name); + } + + rcu_read_lock(); + seq_printf(seq, " %-14s (%s/%i) %17s [%10s]: %20s " + "... [B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s (%s)]\n", + "Originator", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF", + "Potential nexthops", SOURCE_VERSION, REVISION_VERSION_STR, + bat_priv->primary_if->dev, bat_priv->primary_if->addr_str, + net_dev->name); + rcu_read_unlock(); + + spin_lock_irqsave(&orig_hash_lock, flags); + + while (hash_iterate(orig_hash, &hashit)) { + + orig_node = hashit.bucket->data; + + if (!orig_node->router) + continue; + + if (orig_node->router->tq_avg == 0) + continue; + + addr_to_string(orig_str, orig_node->orig); + addr_to_string(router_str, orig_node->router->addr); + + seq_printf(seq, "%-17s (%3i) %17s [%10s]:", + orig_str, orig_node->router->tq_avg, router_str, + orig_node->router->if_incoming->dev); + + list_for_each_entry(neigh_node, &orig_node->neigh_list, list) { + addr_to_string(orig_str, neigh_node->addr); + seq_printf(seq, " %17s (%3i)", orig_str, + neigh_node->tq_avg); + } + + seq_printf(seq, "\n"); + batman_count++; + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + if ((batman_count == 0)) + seq_printf(seq, "No batman nodes in range ...\n"); + + return 0; +} + +static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) +{ + void *data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(TYPE_OF_WORD) * NUM_WORDS, + GFP_ATOMIC); + if (!data_ptr) { + printk(KERN_ERR + "batman-adv:Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own, + (max_if_num - 1) * sizeof(TYPE_OF_WORD) * NUM_WORDS); + kfree(orig_node->bcast_own); + orig_node->bcast_own = data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { + printk(KERN_ERR + "batman-adv:Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own_sum, + (max_if_num - 1) * sizeof(uint8_t)); + kfree(orig_node->bcast_own_sum); + orig_node->bcast_own_sum = data_ptr; + + return 0; +} + +int orig_hash_add_if(struct batman_if *batman_if, int max_if_num) +{ + struct orig_node *orig_node; + HASHIT(hashit); + + /* resize all orig nodes because orig_node->bcast_own(_sum) depend on + * if_num */ + spin_lock(&orig_hash_lock); + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + + if (orig_node_add_if(orig_node, max_if_num) == -1) + goto err; + } + + spin_unlock(&orig_hash_lock); + return 0; + +err: + spin_unlock(&orig_hash_lock); + return -ENOMEM; +} + +static int orig_node_del_if(struct orig_node *orig_node, + int max_if_num, int del_if_num) +{ + void *data_ptr = NULL; + int chunk_size; + + /* last interface was removed */ + if (max_if_num == 0) + goto free_bcast_own; + + chunk_size = sizeof(TYPE_OF_WORD) * NUM_WORDS; + data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC); + if (!data_ptr) { + printk(KERN_ERR + "batman-adv:Can't resize orig: out of memory\n"); + return -1; + } + + /* copy first part */ + memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size); + + /* copy second part */ + memcpy(data_ptr, + orig_node->bcast_own + ((del_if_num + 1) * chunk_size), + (max_if_num - del_if_num) * chunk_size); + +free_bcast_own: + kfree(orig_node->bcast_own); + orig_node->bcast_own = data_ptr; + + if (max_if_num == 0) + goto free_own_sum; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { + printk(KERN_ERR + "batman-adv:Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own_sum, + del_if_num * sizeof(uint8_t)); + + memcpy(data_ptr, + orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)), + (max_if_num - del_if_num) * sizeof(uint8_t)); + +free_own_sum: + kfree(orig_node->bcast_own_sum); + orig_node->bcast_own_sum = data_ptr; + + return 0; +} + +int orig_hash_del_if(struct batman_if *batman_if, int max_if_num) +{ + struct batman_if *batman_if_tmp; + struct orig_node *orig_node; + HASHIT(hashit); + int ret; + + /* resize all orig nodes because orig_node->bcast_own(_sum) depend on + * if_num */ + spin_lock(&orig_hash_lock); + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + + ret = orig_node_del_if(orig_node, max_if_num, + batman_if->if_num); + + if (ret == -1) + goto err; + } + + /* renumber remaining batman interfaces _inside_ of orig_hash_lock */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if_tmp, &if_list, list) { + if (batman_if_tmp->if_status == IF_NOT_IN_USE) + continue; + + if (batman_if == batman_if_tmp) + continue; + + if (batman_if_tmp->if_num > batman_if->if_num) + batman_if_tmp->if_num--; + } + rcu_read_unlock(); + + batman_if->if_num = -1; + spin_unlock(&orig_hash_lock); + return 0; + +err: + spin_unlock(&orig_hash_lock); + return -ENOMEM; +} diff --combined drivers/staging/batman-adv/originator.h index 6632538,e88411d..e88411d --- a/drivers/staging/batman-adv/originator.h +++ b/drivers/staging/batman-adv/originator.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_ORIGINATOR_H_ + #define _NET_BATMAN_ADV_ORIGINATOR_H_ + int originator_init(void); void originator_free(void); void purge_orig(struct work_struct *work); @@@ -29,3 -32,5 +32,5 @@@ create_neighbor(struct orig_node *orig_ int orig_seq_print_text(struct seq_file *seq, void *offset); int orig_hash_add_if(struct batman_if *batman_if, int max_if_num); int orig_hash_del_if(struct batman_if *batman_if, int max_if_num); + + #endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */ diff --combined drivers/staging/batman-adv/packet.h index 8a04418,abb5e46..abb5e46 --- a/drivers/staging/batman-adv/packet.h +++ b/drivers/staging/batman-adv/packet.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_PACKET_H_ + #define _NET_BATMAN_ADV_PACKET_H_ + #define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */
#define BAT_PACKET 0x01 @@@ -113,3 -116,5 +116,5 @@@ struct vis_packet uint8_t target_orig[6]; /* who should receive this packet */ uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */ } __attribute__((packed)); + + #endif /* _NET_BATMAN_ADV_PACKET_H_ */ diff --combined drivers/staging/batman-adv/ring_buffer.h index b8c9456,6b0cb9a..6b0cb9a --- a/drivers/staging/batman-adv/ring_buffer.h +++ b/drivers/staging/batman-adv/ring_buffer.h @@@ -19,5 -19,10 +19,10 @@@ * */
+ #ifndef _NET_BATMAN_ADV_RING_BUFFER_H_ + #define _NET_BATMAN_ADV_RING_BUFFER_H_ + void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value); uint8_t ring_buffer_avg(uint8_t lq_recv[]); + + #endif /* _NET_BATMAN_ADV_RING_BUFFER_H_ */ diff --combined drivers/staging/batman-adv/routing.c index acd8f74,0000000..127cfbf mode 100644,000000..100644 --- a/drivers/staging/batman-adv/routing.c +++ b/drivers/staging/batman-adv/routing.c @@@ -1,1291 -1,0 +1,1291 @@@ +/* + * 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 "routing.h" +#include "send.h" +#include "hash.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "icmp_socket.h" +#include "translation-table.h" +#include "originator.h" +#include "types.h" +#include "ring_buffer.h" +#include "vis.h" +#include "aggregation.h" + +static DECLARE_WAIT_QUEUE_HEAD(thread_wait); + +void slide_own_bcast_window(struct batman_if *batman_if) +{ + HASHIT(hashit); + struct orig_node *orig_node; + TYPE_OF_WORD *word; + unsigned long flags; + + spin_lock_irqsave(&orig_hash_lock, flags); + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + word = &(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]); + + bit_get_packet(word, 1, 0); + orig_node->bcast_own_sum[batman_if->if_num] = + bit_packet_count(word); + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); +} + +static void update_HNA(struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len) +{ + if ((hna_buff_len != orig_node->hna_buff_len) || + ((hna_buff_len > 0) && + (orig_node->hna_buff_len > 0) && + (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) { + + if (orig_node->hna_buff_len > 0) + hna_global_del_orig(orig_node, + "originator changed hna"); + + if ((hna_buff_len > 0) && (hna_buff != NULL)) + hna_global_add_orig(orig_node, hna_buff, hna_buff_len); + } +} + +static void update_route(struct orig_node *orig_node, + struct neigh_node *neigh_node, + unsigned char *hna_buff, int hna_buff_len) +{ + /* route deleted */ + if ((orig_node->router != NULL) && (neigh_node == NULL)) { + + bat_dbg(DBG_ROUTES, "Deleting route towards: %pM\n", + orig_node->orig); + hna_global_del_orig(orig_node, "originator timed out"); + + /* route added */ + } else if ((orig_node->router == NULL) && (neigh_node != NULL)) { + + bat_dbg(DBG_ROUTES, + "Adding route towards: %pM (via %pM)\n", + orig_node->orig, neigh_node->addr); + hna_global_add_orig(orig_node, hna_buff, hna_buff_len); + + /* route changed */ + } else { + bat_dbg(DBG_ROUTES, + "Changing route towards: %pM " + "(now via %pM - was via %pM)\n", + orig_node->orig, neigh_node->addr, + orig_node->router->addr); + } + + orig_node->router = neigh_node; +} + + +void update_routes(struct orig_node *orig_node, + struct neigh_node *neigh_node, + unsigned char *hna_buff, int hna_buff_len) +{ + + if (orig_node == NULL) + return; + + if (orig_node->router != neigh_node) + update_route(orig_node, neigh_node, hna_buff, hna_buff_len); + /* may be just HNA changed */ + else + update_HNA(orig_node, hna_buff, hna_buff_len); +} + - static int isBidirectionalNeigh(struct orig_node *orig_node, ++static int is_bidirectional_neigh(struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet, + struct batman_if *if_incoming) +{ + struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + unsigned char total_count; + + if (orig_node == orig_neigh_node) { + list_for_each_entry(tmp_neigh_node, + &orig_node->neigh_list, + list) { + + if (compare_orig(tmp_neigh_node->addr, + orig_neigh_node->orig) && + (tmp_neigh_node->if_incoming == if_incoming)) + neigh_node = tmp_neigh_node; + } + + if (!neigh_node) + neigh_node = create_neighbor(orig_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); + /* create_neighbor failed, return 0 */ + if (!neigh_node) + return 0; + + neigh_node->last_valid = jiffies; + } else { + /* find packet count of corresponding one hop neighbor */ + list_for_each_entry(tmp_neigh_node, + &orig_neigh_node->neigh_list, list) { + + if (compare_orig(tmp_neigh_node->addr, + orig_neigh_node->orig) && + (tmp_neigh_node->if_incoming == if_incoming)) + neigh_node = tmp_neigh_node; + } + + if (!neigh_node) + neigh_node = create_neighbor(orig_neigh_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); + /* create_neighbor failed, return 0 */ + if (!neigh_node) + return 0; + } + + orig_node->last_valid = jiffies; + + /* pay attention to not get a value bigger than 100 % */ + total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > + neigh_node->real_packet_count ? + neigh_node->real_packet_count : + orig_neigh_node->bcast_own_sum[if_incoming->if_num]); + + /* if we have too few packets (too less data) we set tq_own to zero */ + /* if we receive too few packets it is not considered bidirectional */ + if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || + (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) + orig_neigh_node->tq_own = 0; + else + /* neigh_node->real_packet_count is never zero as we + * only purge old information when getting new + * information */ + orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / + neigh_node->real_packet_count; + + /* + * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does + * affect the nearly-symmetric links only a little, but + * punishes asymmetric links more. This will give a value + * between 0 and TQ_MAX_VALUE + */ + orig_neigh_node->tq_asym_penalty = + TQ_MAX_VALUE - + (TQ_MAX_VALUE * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / + (TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE); + + batman_packet->tq = ((batman_packet->tq * + orig_neigh_node->tq_own * + orig_neigh_node->tq_asym_penalty) / + (TQ_MAX_VALUE * TQ_MAX_VALUE)); + + bat_dbg(DBG_BATMAN, + "bidirectional: " + "orig = %-15pM neigh = %-15pM => own_bcast = %2i, " + "real recv = %2i, local tq: %3i, asym_penalty: %3i, " + "total tq: %3i\n", + orig_node->orig, orig_neigh_node->orig, total_count, + neigh_node->real_packet_count, orig_neigh_node->tq_own, + orig_neigh_node->tq_asym_penalty, batman_packet->tq); + + /* if link has the minimum required transmission quality + * consider it bidirectional */ + if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) + return 1; + + return 0; +} + +static void update_orig(struct orig_node *orig_node, struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + struct batman_if *if_incoming, + unsigned char *hna_buff, int hna_buff_len, + char is_duplicate) +{ + struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + int tmp_hna_buff_len; + + bat_dbg(DBG_BATMAN, "update_originator(): " + "Searching and updating originator entry of received packet\n"); + + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming)) { + neigh_node = tmp_neigh_node; + continue; + } + + if (is_duplicate) + continue; + + ring_buffer_set(tmp_neigh_node->tq_recv, + &tmp_neigh_node->tq_index, 0); + tmp_neigh_node->tq_avg = + ring_buffer_avg(tmp_neigh_node->tq_recv); + } + + if (!neigh_node) { + struct orig_node *orig_tmp; + + orig_tmp = get_orig_node(ethhdr->h_source); + if (!orig_tmp) + return; + + neigh_node = create_neighbor(orig_node, + orig_tmp, + ethhdr->h_source, if_incoming); + if (!neigh_node) + return; + } else + bat_dbg(DBG_BATMAN, + "Updating existing last-hop neighbor of originator\n"); + + orig_node->flags = batman_packet->flags; + neigh_node->last_valid = jiffies; + + ring_buffer_set(neigh_node->tq_recv, + &neigh_node->tq_index, + batman_packet->tq); + neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); + + if (!is_duplicate) { + orig_node->last_ttl = batman_packet->ttl; + neigh_node->last_ttl = batman_packet->ttl; + } + + tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? + batman_packet->num_hna * ETH_ALEN : hna_buff_len); + + /* if this neighbor already is our next hop there is nothing + * to change */ + if (orig_node->router == neigh_node) + goto update_hna; + + /* if this neighbor does not offer a better TQ we won't consider it */ + if ((orig_node->router) && + (orig_node->router->tq_avg > neigh_node->tq_avg)) + goto update_hna; + + /* if the TQ is the same and the link not more symetric we + * won't consider it either */ + if ((orig_node->router) && + ((neigh_node->tq_avg == orig_node->router->tq_avg) && + (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] + >= neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) + goto update_hna; + + update_routes(orig_node, neigh_node, hna_buff, tmp_hna_buff_len); + return; + +update_hna: + update_routes(orig_node, orig_node->router, hna_buff, tmp_hna_buff_len); +} + +/* checks whether the host restarted and is in the protection time. + * returns: + * 0 if the packet is to be accepted + * 1 if the packet is to be ignored. + */ +static int window_protected(int32_t seq_num_diff, + unsigned long *last_reset) +{ + if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) + || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) { + if (time_after(jiffies, *last_reset + + msecs_to_jiffies(RESET_PROTECTION_MS))) { + + *last_reset = jiffies; + bat_dbg(DBG_BATMAN, + "old packet received, start protection\n"); + + return 0; + } else + return 1; + } + return 0; +} + +/* processes a batman packet for all interfaces, adjusts the sequence number and + * finds out whether it is a duplicate. + * returns: + * 1 the packet is a duplicate + * 0 the packet has not yet been received + * -1 the packet is old and has been received while the seqno window + * was protected. Caller should drop it. + */ +static char count_real_packets(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + struct batman_if *if_incoming) +{ + struct orig_node *orig_node; + struct neigh_node *tmp_neigh_node; + char is_duplicate = 0; + int32_t seq_diff; + int need_update = 0; + int set_mark; + + orig_node = get_orig_node(batman_packet->orig); + if (orig_node == NULL) + return 0; + + seq_diff = batman_packet->seqno - orig_node->last_real_seqno; + + /* signalize caller that the packet is to be dropped. */ + if (window_protected(seq_diff, &orig_node->batman_seqno_reset)) + return -1; + + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, + orig_node->last_real_seqno, + batman_packet->seqno); + + if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming)) + set_mark = 1; + else + set_mark = 0; + + /* if the window moved, set the update flag. */ + need_update |= bit_get_packet(tmp_neigh_node->real_bits, + seq_diff, set_mark); + + tmp_neigh_node->real_packet_count = + bit_packet_count(tmp_neigh_node->real_bits); + } + + if (need_update) { + bat_dbg(DBG_BATMAN, "updating last_seqno: old %d, new %d\n", + orig_node->last_real_seqno, batman_packet->seqno); + orig_node->last_real_seqno = batman_packet->seqno; + } + + return is_duplicate; +} + +/* copy primary address for bonding */ +static void mark_bonding_address(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet) + +{ + if (batman_packet->flags & PRIMARIES_FIRST_HOP) + memcpy(orig_neigh_node->primary_addr, + orig_node->orig, ETH_ALEN); + + return; +} + +/* mark possible bond.candidates in the neighbor list */ +void update_bonding_candidates(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + int candidates; + int interference_candidate; + int best_tq; + struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; + struct neigh_node *first_candidate, *last_candidate; + + /* update the candidates for this originator */ + if (!orig_node->router) { + orig_node->bond.candidates = 0; + return; + } + + best_tq = orig_node->router->tq_avg; + + /* update bond.candidates */ + + candidates = 0; + + /* mark other nodes which also received "PRIMARIES FIRST HOP" packets + * as "bonding partner" */ + + /* first, zero the list */ + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + tmp_neigh_node->next_bond_candidate = NULL; + } + + first_candidate = NULL; + last_candidate = NULL; + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + /* only consider if it has the same primary address ... */ + if (memcmp(orig_node->orig, + tmp_neigh_node->orig_node->primary_addr, + ETH_ALEN) != 0) + continue; + + /* ... and is good enough to be considered */ + if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) + continue; + + /* check if we have another candidate with the same + * mac address or interface. If we do, we won't + * select this candidate because of possible interference. */ + + interference_candidate = 0; + list_for_each_entry(tmp_neigh_node2, + &orig_node->neigh_list, list) { + + if (tmp_neigh_node2 == tmp_neigh_node) + continue; + + /* we only care if the other candidate is even + * considered as candidate. */ + if (tmp_neigh_node2->next_bond_candidate == NULL) + continue; + + + if ((tmp_neigh_node->if_incoming == + tmp_neigh_node2->if_incoming) + || (memcmp(tmp_neigh_node->addr, + tmp_neigh_node2->addr, ETH_ALEN) == 0)) { + + interference_candidate = 1; + break; + } + } + /* don't care further if it is an interference candidate */ + if (interference_candidate) + continue; + + if (first_candidate == NULL) { + first_candidate = tmp_neigh_node; + tmp_neigh_node->next_bond_candidate = first_candidate; + } else + tmp_neigh_node->next_bond_candidate = last_candidate; + + last_candidate = tmp_neigh_node; + + candidates++; + } + + if (candidates > 0) { + first_candidate->next_bond_candidate = last_candidate; + orig_node->bond.selected = first_candidate; + } + + orig_node->bond.candidates = candidates; +} + +void receive_bat_packet(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + unsigned char *hna_buff, int hna_buff_len, + struct batman_if *if_incoming) +{ + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct batman_if *batman_if; + struct orig_node *orig_neigh_node, *orig_node; + char has_directlink_flag; + char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; + char is_broadcast = 0, is_bidirectional, is_single_hop_neigh; + char is_duplicate; + uint32_t if_incoming_seqno; + + /* Silently drop when the batman packet is actually not a + * correct packet. + * + * This might happen if a packet is padded (e.g. Ethernet has a + * minimum frame length of 64 byte) and the aggregation interprets + * it as an additional length. + * + * TODO: A more sane solution would be to have a bit in the + * batman_packet to detect whether the packet is the last + * packet in an aggregation. Here we expect that the padding + * is always zero (or not 0x01) + */ + if (batman_packet->packet_type != BAT_PACKET) + return; + + /* could be changed by schedule_own_packet() */ + if_incoming_seqno = atomic_read(&if_incoming->seqno); + + has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); + + is_single_hop_neigh = (compare_orig(ethhdr->h_source, + batman_packet->orig) ? 1 : 0); + + bat_dbg(DBG_BATMAN, "Received BATMAN packet via NB: %pM, IF: %s [%s] " + "(from OG: %pM, via prev OG: %pM, seqno %d, tq %d, " + "TTL %d, V %d, IDF %d)\n", + ethhdr->h_source, if_incoming->dev, if_incoming->addr_str, + batman_packet->orig, batman_packet->prev_sender, + batman_packet->seqno, batman_packet->tq, batman_packet->ttl, + batman_packet->version, has_directlink_flag); + + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->if_status != IF_ACTIVE) + continue; + + if (compare_orig(ethhdr->h_source, + batman_if->net_dev->dev_addr)) + is_my_addr = 1; + + if (compare_orig(batman_packet->orig, + batman_if->net_dev->dev_addr)) + is_my_orig = 1; + + if (compare_orig(batman_packet->prev_sender, + batman_if->net_dev->dev_addr)) + is_my_oldorig = 1; + - if (compare_orig(ethhdr->h_source, broadcastAddr)) ++ if (compare_orig(ethhdr->h_source, broadcast_addr)) + is_broadcast = 1; + } + + if (batman_packet->version != COMPAT_VERSION) { + bat_dbg(DBG_BATMAN, + "Drop packet: incompatible batman version (%i)\n", + batman_packet->version); + return; + } + + if (is_my_addr) { + bat_dbg(DBG_BATMAN, + "Drop packet: received my own broadcast (sender: %pM" + ")\n", + ethhdr->h_source); + return; + } + + if (is_broadcast) { + bat_dbg(DBG_BATMAN, "Drop packet: " + "ignoring all packets with broadcast source addr (sender: %pM" + ")\n", ethhdr->h_source); + return; + } + + if (is_my_orig) { + TYPE_OF_WORD *word; + int offset; + + orig_neigh_node = get_orig_node(ethhdr->h_source); + + if (!orig_neigh_node) + return; + + /* neighbor has to indicate direct link and it has to + * come via the corresponding interface */ + /* if received seqno equals last send seqno save new + * seqno for bidirectional check */ + if (has_directlink_flag && + compare_orig(if_incoming->net_dev->dev_addr, + batman_packet->orig) && + (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { + offset = if_incoming->if_num * NUM_WORDS; + word = &(orig_neigh_node->bcast_own[offset]); + bit_mark(word, 0); + orig_neigh_node->bcast_own_sum[if_incoming->if_num] = + bit_packet_count(word); + } + + bat_dbg(DBG_BATMAN, "Drop packet: " + "originator packet from myself (via neighbor)\n"); + return; + } + + if (is_my_oldorig) { + bat_dbg(DBG_BATMAN, + "Drop packet: ignoring all rebroadcast echos (sender: " + "%pM)\n", ethhdr->h_source); + return; + } + + orig_node = get_orig_node(batman_packet->orig); + if (orig_node == NULL) + return; + + is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming); + + if (is_duplicate == -1) { + bat_dbg(DBG_BATMAN, + "Drop packet: packet within seqno protection time " + "(sender: %pM)\n", ethhdr->h_source); + return; + } + + if (batman_packet->tq == 0) { + bat_dbg(DBG_BATMAN, + "Drop packet: originator packet with tq equal 0\n"); + return; + } + + /* avoid temporary routing loops */ + if ((orig_node->router) && + (orig_node->router->orig_node->router) && + (compare_orig(orig_node->router->addr, + batman_packet->prev_sender)) && + !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && + (compare_orig(orig_node->router->addr, + orig_node->router->orig_node->router->addr))) { + bat_dbg(DBG_BATMAN, + "Drop packet: ignoring all rebroadcast packets that " + "may make me loop (sender: %pM)\n", ethhdr->h_source); + return; + } + + /* if sender is a direct neighbor the sender mac equals + * originator mac */ + orig_neigh_node = (is_single_hop_neigh ? + orig_node : get_orig_node(ethhdr->h_source)); + if (orig_neigh_node == NULL) + return; + + /* drop packet if sender is not a direct neighbor and if we + * don't route towards it */ + if (!is_single_hop_neigh && + (orig_neigh_node->router == NULL)) { + bat_dbg(DBG_BATMAN, "Drop packet: OGM via unknown neighbor!\n"); + return; + } + - is_bidirectional = isBidirectionalNeigh(orig_node, orig_neigh_node, ++ is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, + batman_packet, if_incoming); + + /* update ranking if it is not a duplicate or has the same + * seqno and similar ttl as the non-duplicate */ + if (is_bidirectional && + (!is_duplicate || + ((orig_node->last_real_seqno == batman_packet->seqno) && + (orig_node->last_ttl - 3 <= batman_packet->ttl)))) + update_orig(orig_node, ethhdr, batman_packet, + if_incoming, hna_buff, hna_buff_len, is_duplicate); + + mark_bonding_address(bat_priv, orig_node, + orig_neigh_node, batman_packet); + update_bonding_candidates(bat_priv, orig_node); + + /* is single hop (direct) neighbor */ + if (is_single_hop_neigh) { + + /* mark direct link on incoming interface */ + schedule_forward_packet(orig_node, ethhdr, batman_packet, + 1, hna_buff_len, if_incoming); + + bat_dbg(DBG_BATMAN, "Forwarding packet: " + "rebroadcast neighbor packet with direct link flag\n"); + return; + } + + /* multihop originator */ + if (!is_bidirectional) { + bat_dbg(DBG_BATMAN, + "Drop packet: not received via bidirectional link\n"); + return; + } + + if (is_duplicate) { + bat_dbg(DBG_BATMAN, "Drop packet: duplicate packet received\n"); + return; + } + + bat_dbg(DBG_BATMAN, + "Forwarding packet: rebroadcast originator packet\n"); + schedule_forward_packet(orig_node, ethhdr, batman_packet, + 0, hna_buff_len, if_incoming); +} + +int recv_bat_packet(struct sk_buff *skb, + struct batman_if *batman_if) +{ + struct ethhdr *ethhdr; + unsigned long flags; + struct sk_buff *skb_old; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < sizeof(struct batman_packet)) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with broadcast indication but unicast recipient */ + if (!is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* TODO: we use headlen instead of "length", because + * only this data is paged in. */ + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, skb_headlen(skb))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + spin_lock_irqsave(&orig_hash_lock, flags); + receive_aggr_bat_packet(ethhdr, + skb->data, + skb_headlen(skb), + batman_if); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + kfree_skb(skb); + return NET_RX_SUCCESS; +} + +static int recv_my_icmp_packet(struct sk_buff *skb, size_t icmp_len) +{ + struct orig_node *orig_node; + struct icmp_packet_rr *icmp_packet; + struct ethhdr *ethhdr; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* add data to device queue */ + if (icmp_packet->msg_type != ECHO_REQUEST) { + bat_socket_receive_packet(icmp_packet, icmp_len); + return NET_RX_DROP; + } + + /* answer echo request (ping) */ + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *)hash_find(orig_hash, + icmp_packet->orig)); + ret = NET_RX_DROP; + + if ((orig_node != NULL) && + (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + skb_old = NULL; + if (!skb_clone_writable(skb, icmp_len)) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); + icmp_packet->msg_type = ECHO_REPLY; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; +} + +static int recv_icmp_ttl_exceeded(struct sk_buff *skb, size_t icmp_len) +{ + struct orig_node *orig_node; + struct icmp_packet *icmp_packet; + struct ethhdr *ethhdr; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* send TTL exceeded if packet is an echo request (traceroute) */ + if (icmp_packet->msg_type != ECHO_REQUEST) { + printk(KERN_WARNING "batman-adv:" + "Warning - can't forward icmp packet from %pM to %pM: " + "ttl exceeded\n", + icmp_packet->orig, icmp_packet->dst); + return NET_RX_DROP; + } + + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, icmp_packet->orig)); + ret = NET_RX_DROP; + + if ((orig_node != NULL) && + (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, icmp_len)) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); + icmp_packet->msg_type = TTL_EXCEEDED; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; +} + + +int recv_icmp_packet(struct sk_buff *skb) +{ + struct icmp_packet_rr *icmp_packet; + struct ethhdr *ethhdr; + struct orig_node *orig_node; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int hdr_size = sizeof(struct icmp_packet); + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + /** + * we truncate all incoming icmp packets if they don't match our size + */ + if (skb_headlen(skb) >= sizeof(struct icmp_packet_rr)) + hdr_size = sizeof(struct icmp_packet_rr); + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with unicast indication but broadcast recipient */ + if (is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + + /* add record route information if not full */ + if ((hdr_size == sizeof(struct icmp_packet_rr)) && + (icmp_packet->rr_cur < BAT_RR_LEN)) { + memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), + ethhdr->h_dest, ETH_ALEN); + icmp_packet->rr_cur++; + } + + /* packet for me */ + if (is_my_mac(icmp_packet->dst)) + return recv_my_icmp_packet(skb, hdr_size); + + /* TTL exceeded */ + if (icmp_packet->ttl < 2) + return recv_icmp_ttl_exceeded(skb, hdr_size); + + ret = NET_RX_DROP; + + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, icmp_packet->dst)); + + if ((orig_node != NULL) && + (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, hdr_size)) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + /* decrement ttl */ + icmp_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; +} + +/* find a suitable router for this originator, and use + * bonding if possible. */ +struct neigh_node *find_router(struct orig_node *orig_node, + struct batman_if *recv_if) +{ + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct orig_node *primary_orig_node; + struct orig_node *router_orig; + struct neigh_node *router, *first_candidate, *best_router; + static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; + int bonding_enabled; + + if (!orig_node) + return NULL; + + if (!orig_node->router) + return NULL; + + /* without bonding, the first node should + * always choose the default router. */ + + bonding_enabled = atomic_read(&bat_priv->bonding_enabled); + if (!bonding_enabled && (recv_if == NULL)) + return orig_node->router; + + router_orig = orig_node->router->orig_node; + + /* if we have something in the primary_addr, we can search + * for a potential bonding candidate. */ + if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) + return orig_node->router; + + /* find the orig_node which has the primary interface. might + * even be the same as our router_orig in many cases */ + + if (memcmp(router_orig->primary_addr, + router_orig->orig, ETH_ALEN) == 0) { + primary_orig_node = router_orig; + } else { + primary_orig_node = hash_find(orig_hash, + router_orig->primary_addr); + if (!primary_orig_node) + return orig_node->router; + } + + /* with less than 2 candidates, we can't do any + * bonding and prefer the original router. */ + + if (primary_orig_node->bond.candidates < 2) + return orig_node->router; + + + /* all nodes between should choose a candidate which + * is is not on the interface where the packet came + * in. */ + first_candidate = primary_orig_node->bond.selected; + router = first_candidate; + + if (bonding_enabled) { + /* in the bonding case, send the packets in a round + * robin fashion over the remaining interfaces. */ + do { + /* recv_if == NULL on the first node. */ + if (router->if_incoming != recv_if) + break; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + primary_orig_node->bond.selected = router->next_bond_candidate; + + } else { + /* if bonding is disabled, use the best of the + * remaining candidates which are not using + * this interface. */ + best_router = first_candidate; + + do { + /* recv_if == NULL on the first node. */ + if ((router->if_incoming != recv_if) && + (router->tq_avg > best_router->tq_avg)) + best_router = router; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + router = best_router; + } + + return router; +} + +int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct unicast_packet *unicast_packet; + struct orig_node *orig_node; + struct neigh_node *router; + struct ethhdr *ethhdr; + struct batman_if *batman_if; + struct sk_buff *skb_old; + uint8_t dstaddr[ETH_ALEN]; + int hdr_size = sizeof(struct unicast_packet); + unsigned long flags; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + + /* packet with unicast indication but broadcast recipient */ + if (is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + unicast_packet = (struct unicast_packet *) skb->data; + + /* packet for me */ + if (is_my_mac(unicast_packet->dest)) { + interface_rx(skb, hdr_size); + return NET_RX_SUCCESS; + } + + /* TTL exceeded */ + if (unicast_packet->ttl < 2) { + printk(KERN_WARNING "batman-adv:Warning - " + "can't forward unicast packet from %pM to %pM: " + "ttl exceeded\n", + ethhdr->h_source, unicast_packet->dest); + return NET_RX_DROP; + } + + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, unicast_packet->dest)); + + router = find_router(orig_node, recv_if); + + if (!router) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + + batman_if = router->if_incoming; + memcpy(dstaddr, router->addr, ETH_ALEN); + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, sizeof(struct unicast_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + unicast_packet = (struct unicast_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + /* decrement ttl */ + unicast_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + + return NET_RX_SUCCESS; +} + +int recv_bcast_packet(struct sk_buff *skb) +{ + struct orig_node *orig_node; + struct bcast_packet *bcast_packet; + struct ethhdr *ethhdr; + int hdr_size = sizeof(struct bcast_packet); + int32_t seq_diff; + unsigned long flags; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with broadcast indication but unicast recipient */ + if (!is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* ignore broadcasts sent by myself */ + if (is_my_mac(ethhdr->h_source)) + return NET_RX_DROP; + + bcast_packet = (struct bcast_packet *)skb->data; + + /* ignore broadcasts originated by myself */ + if (is_my_mac(bcast_packet->orig)) + return NET_RX_DROP; + + if (bcast_packet->ttl < 2) + return NET_RX_DROP; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, bcast_packet->orig)); + + if (orig_node == NULL) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + /* check whether the packet is a duplicate */ + if (get_bit_status(orig_node->bcast_bits, + orig_node->last_bcast_seqno, + ntohl(bcast_packet->seqno))) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; + + /* check whether the packet is old and the host just restarted. */ + if (window_protected(seq_diff, &orig_node->bcast_seqno_reset)) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + /* mark broadcast in flood history, update window position + * if required. */ + if (bit_get_packet(orig_node->bcast_bits, seq_diff, 1)) + orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); + + spin_unlock_irqrestore(&orig_hash_lock, flags); + /* rebroadcast packet */ + add_bcast_packet_to_list(skb); + + /* broadcast for me */ + interface_rx(skb, hdr_size); + + return NET_RX_SUCCESS; +} + +int recv_vis_packet(struct sk_buff *skb) +{ + struct vis_packet *vis_packet; + struct ethhdr *ethhdr; + struct bat_priv *bat_priv; + int hdr_size = sizeof(struct vis_packet); + + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + vis_packet = (struct vis_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + /* ignore own packets */ + if (is_my_mac(vis_packet->vis_orig)) + return NET_RX_DROP; + + if (is_my_mac(vis_packet->sender_orig)) + return NET_RX_DROP; + + /* FIXME: each batman_if will be attached to a softif */ + bat_priv = netdev_priv(soft_device); + + switch (vis_packet->vis_type) { + case VIS_TYPE_SERVER_SYNC: + /* TODO: handle fragmented skbs properly */ + receive_server_sync_packet(bat_priv, vis_packet, + skb_headlen(skb)); + break; + + case VIS_TYPE_CLIENT_UPDATE: + /* TODO: handle fragmented skbs properly */ + receive_client_update_packet(bat_priv, vis_packet, + skb_headlen(skb)); + break; + + default: /* ignore unknown packet */ + break; + } + + /* We take a copy of the data in the packet, so we should + always free the skbuf. */ + return NET_RX_DROP; +} diff --combined drivers/staging/batman-adv/routing.h index 43387a2,3eac64e..3eac64e --- a/drivers/staging/batman-adv/routing.h +++ b/drivers/staging/batman-adv/routing.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_ROUTING_H_ + #define _NET_BATMAN_ADV_ROUTING_H_ + #include "types.h"
void slide_own_bcast_window(struct batman_if *batman_if); @@@ -39,3 -42,5 +42,5 @@@ struct neigh_node *find_router(struct o struct batman_if *recv_if); void update_bonding_candidates(struct bat_priv *bat_priv, struct orig_node *orig_node); + + #endif /* _NET_BATMAN_ADV_ROUTING_H_ */ diff --combined drivers/staging/batman-adv/send.c index e61a62c,0000000..d4b92ef mode 100644,000000..100644 --- a/drivers/staging/batman-adv/send.c +++ b/drivers/staging/batman-adv/send.c @@@ -1,576 -1,0 +1,576 @@@ +/* + * 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 "send.h" +#include "routing.h" +#include "translation-table.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "types.h" +#include "vis.h" +#include "aggregation.h" + +#include <linux/netfilter_bridge.h> + +static void send_outstanding_bcast_packet(struct work_struct *work); + +/* apply hop penalty for a normal link */ +static uint8_t hop_penalty(const uint8_t tq) +{ + return (tq * (TQ_MAX_VALUE - TQ_HOP_PENALTY)) / (TQ_MAX_VALUE); +} + +/* when do we schedule our own packet to be sent */ +static unsigned long own_send_time(struct bat_priv *bat_priv) +{ - return jiffies + - (((atomic_read(&bat_priv->orig_interval) - JITTER + - (random32() % 2*JITTER)) * HZ) / 1000); ++ return jiffies + msecs_to_jiffies( ++ atomic_read(&bat_priv->orig_interval) - ++ JITTER + (random32() % 2*JITTER)); +} + +/* when do we schedule a forwarded packet to be sent */ +static unsigned long forward_send_time(struct bat_priv *bat_priv) +{ - return jiffies + (((random32() % (JITTER/2)) * HZ) / 1000); ++ return jiffies + msecs_to_jiffies(random32() % (JITTER/2)); +} + +/* send out an already prepared packet to the given address via the + * specified batman interface */ +int send_skb_packet(struct sk_buff *skb, + struct batman_if *batman_if, + uint8_t *dst_addr) +{ + struct ethhdr *ethhdr; + + if (batman_if->if_status != IF_ACTIVE) + goto send_skb_err; + + if (unlikely(!batman_if->net_dev)) + goto send_skb_err; + + if (!(batman_if->net_dev->flags & IFF_UP)) { + printk(KERN_WARNING + "batman-adv:Interface %s " + "is not up - can't send packet via that interface!\n", + batman_if->dev); + goto send_skb_err; + } + + /* push to the ethernet header. */ + if (my_skb_push(skb, sizeof(struct ethhdr)) < 0) + goto send_skb_err; + + skb_reset_mac_header(skb); + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); + ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); + + skb_set_network_header(skb, ETH_HLEN); + skb->priority = TC_PRIO_CONTROL; + skb->protocol = __constant_htons(ETH_P_BATMAN); + + skb->dev = batman_if->net_dev; + + /* dev_queue_xmit() returns a negative result on error. However on + * congestion and traffic shaping, it drops and returns NET_XMIT_DROP + * (which is > 0). This will not be treated as an error. + * Also, if netfilter/ebtables wants to block outgoing batman + * packets then giving them a chance to do so here */ + + return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, + dev_queue_xmit); +send_skb_err: + kfree_skb(skb); + return NET_XMIT_DROP; +} + +/* sends a raw packet. */ +void send_raw_packet(unsigned char *pack_buff, int pack_buff_len, + struct batman_if *batman_if, uint8_t *dst_addr) +{ + struct sk_buff *skb; + char *data; + + skb = dev_alloc_skb(pack_buff_len + sizeof(struct ethhdr)); + if (!skb) + return; + data = skb_put(skb, pack_buff_len + sizeof(struct ethhdr)); + memcpy(data + sizeof(struct ethhdr), pack_buff, pack_buff_len); + /* pull back to the batman "network header" */ + skb_pull(skb, sizeof(struct ethhdr)); + send_skb_packet(skb, batman_if, dst_addr); +} + +/* Send a packet to a given interface */ +static void send_packet_to_if(struct forw_packet *forw_packet, + struct batman_if *batman_if) +{ + char *fwd_str; + uint8_t packet_num; + int16_t buff_pos; + struct batman_packet *batman_packet; + + if (batman_if->if_status != IF_ACTIVE) + return; + + packet_num = 0; + buff_pos = 0; + batman_packet = (struct batman_packet *) + (forw_packet->packet_buff); + + /* adjust all flags and log packets */ + while (aggregated_packet(buff_pos, + forw_packet->packet_len, + batman_packet->num_hna)) { + + /* we might have aggregated direct link packets with an + * ordinary base packet */ + if ((forw_packet->direct_link_flags & (1 << packet_num)) && + (forw_packet->if_incoming == batman_if)) + batman_packet->flags |= DIRECTLINK; + else + batman_packet->flags &= ~DIRECTLINK; + + fwd_str = (packet_num > 0 ? "Forwarding" : (forw_packet->own ? + "Sending own" : + "Forwarding")); + bat_dbg(DBG_BATMAN, + "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d," + " IDF %s) on interface %s [%s]\n", + fwd_str, (packet_num > 0 ? "aggregated " : ""), + batman_packet->orig, ntohl(batman_packet->seqno), + batman_packet->tq, batman_packet->ttl, + (batman_packet->flags & DIRECTLINK ? + "on" : "off"), + batman_if->dev, batman_if->addr_str); + + buff_pos += sizeof(struct batman_packet) + + (batman_packet->num_hna * ETH_ALEN); + packet_num++; + batman_packet = (struct batman_packet *) + (forw_packet->packet_buff + buff_pos); + } + + send_raw_packet(forw_packet->packet_buff, + forw_packet->packet_len, - batman_if, broadcastAddr); ++ batman_if, broadcast_addr); +} + +/* send a batman packet */ +static void send_packet(struct forw_packet *forw_packet) +{ + struct batman_if *batman_if; + struct batman_packet *batman_packet = + (struct batman_packet *)(forw_packet->packet_buff); + unsigned char directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0); + + if (!forw_packet->if_incoming) { + printk(KERN_ERR "batman-adv: Error - can't forward packet: " + "incoming iface not specified\n"); + return; + } + + if (forw_packet->if_incoming->if_status != IF_ACTIVE) + return; + + /* multihomed peer assumed */ + /* non-primary OGMs are only broadcasted on their interface */ + if ((directlink && (batman_packet->ttl == 1)) || + (forw_packet->own && (forw_packet->if_incoming->if_num > 0))) { + + /* FIXME: what about aggregated packets ? */ + bat_dbg(DBG_BATMAN, + "%s packet (originator %pM, seqno %d, TTL %d) " + "on interface %s [%s]\n", + (forw_packet->own ? "Sending own" : "Forwarding"), + batman_packet->orig, ntohl(batman_packet->seqno), + batman_packet->ttl, forw_packet->if_incoming->dev, + forw_packet->if_incoming->addr_str); + + send_raw_packet(forw_packet->packet_buff, + forw_packet->packet_len, + forw_packet->if_incoming, - broadcastAddr); ++ broadcast_addr); + return; + } + + /* broadcast on every interface */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) + send_packet_to_if(forw_packet, batman_if); + rcu_read_unlock(); +} + +static void rebuild_batman_packet(struct batman_if *batman_if) +{ + int new_len; + unsigned char *new_buff; + struct batman_packet *batman_packet; + + new_len = sizeof(struct batman_packet) + (num_hna * ETH_ALEN); + new_buff = kmalloc(new_len, GFP_ATOMIC); + + /* keep old buffer if kmalloc should fail */ + if (new_buff) { + memcpy(new_buff, batman_if->packet_buff, + sizeof(struct batman_packet)); + batman_packet = (struct batman_packet *)new_buff; + + batman_packet->num_hna = hna_local_fill_buffer( + new_buff + sizeof(struct batman_packet), + new_len - sizeof(struct batman_packet)); + + kfree(batman_if->packet_buff); + batman_if->packet_buff = new_buff; + batman_if->packet_len = new_len; + } +} + +void schedule_own_packet(struct batman_if *batman_if) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + unsigned long send_time; + struct batman_packet *batman_packet; + int vis_server; + + if ((batman_if->if_status == IF_NOT_IN_USE) || + (batman_if->if_status == IF_TO_BE_REMOVED)) + return; + + vis_server = atomic_read(&bat_priv->vis_mode); + + /** + * the interface gets activated here to avoid race conditions between + * the moment of activating the interface in + * hardif_activate_interface() where the originator mac is set and + * outdated packets (especially uninitialized mac addresses) in the + * packet queue + */ + if (batman_if->if_status == IF_TO_BE_ACTIVATED) + batman_if->if_status = IF_ACTIVE; + + /* if local hna has changed and interface is a primary interface */ + if ((atomic_read(&hna_local_changed)) && + (batman_if == bat_priv->primary_if)) + rebuild_batman_packet(batman_if); + + /** + * NOTE: packet_buff might just have been re-allocated in + * rebuild_batman_packet() + */ + batman_packet = (struct batman_packet *)batman_if->packet_buff; + + /* change sequence number to network order */ + batman_packet->seqno = + htonl((uint32_t)atomic_read(&batman_if->seqno)); + + if (vis_server == VIS_TYPE_SERVER_SYNC) + batman_packet->flags |= VIS_SERVER; + else + batman_packet->flags &= ~VIS_SERVER; + + atomic_inc(&batman_if->seqno); + + slide_own_bcast_window(batman_if); + send_time = own_send_time(bat_priv); + add_bat_packet_to_list(bat_priv, + batman_if->packet_buff, + batman_if->packet_len, + batman_if, 1, send_time); +} + +void schedule_forward_packet(struct orig_node *orig_node, + struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + uint8_t directlink, int hna_buff_len, + struct batman_if *if_incoming) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + unsigned char in_tq, in_ttl, tq_avg = 0; + unsigned long send_time; + + if (batman_packet->ttl <= 1) { + bat_dbg(DBG_BATMAN, "ttl exceeded\n"); + return; + } + + in_tq = batman_packet->tq; + in_ttl = batman_packet->ttl; + + batman_packet->ttl--; + memcpy(batman_packet->prev_sender, ethhdr->h_source, ETH_ALEN); + + /* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast + * of our best tq value */ + if ((orig_node->router) && (orig_node->router->tq_avg != 0)) { + + /* rebroadcast ogm of best ranking neighbor as is */ + if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) { + batman_packet->tq = orig_node->router->tq_avg; + + if (orig_node->router->last_ttl) + batman_packet->ttl = orig_node->router->last_ttl + - 1; + } + + tq_avg = orig_node->router->tq_avg; + } + + /* apply hop penalty */ + batman_packet->tq = hop_penalty(batman_packet->tq); + + bat_dbg(DBG_BATMAN, "Forwarding packet: tq_orig: %i, tq_avg: %i, " + "tq_forw: %i, ttl_orig: %i, ttl_forw: %i\n", + in_tq, tq_avg, batman_packet->tq, in_ttl - 1, + batman_packet->ttl); + + batman_packet->seqno = htonl(batman_packet->seqno); + + /* switch of primaries first hop flag when forwarding */ + batman_packet->flags &= ~PRIMARIES_FIRST_HOP; + if (directlink) + batman_packet->flags |= DIRECTLINK; + else + batman_packet->flags &= ~DIRECTLINK; + + send_time = forward_send_time(bat_priv); + add_bat_packet_to_list(bat_priv, + (unsigned char *)batman_packet, + sizeof(struct batman_packet) + hna_buff_len, + if_incoming, 0, send_time); +} + +static void forw_packet_free(struct forw_packet *forw_packet) +{ + if (forw_packet->skb) + kfree_skb(forw_packet->skb); + kfree(forw_packet->packet_buff); + kfree(forw_packet); +} + +static void _add_bcast_packet_to_list(struct forw_packet *forw_packet, + unsigned long send_time) +{ + unsigned long flags; + INIT_HLIST_NODE(&forw_packet->list); + + /* add new packet to packet list */ + spin_lock_irqsave(&forw_bcast_list_lock, flags); + hlist_add_head(&forw_packet->list, &forw_bcast_list); + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + /* start timer for this packet */ + INIT_DELAYED_WORK(&forw_packet->delayed_work, + send_outstanding_bcast_packet); + queue_delayed_work(bat_event_workqueue, &forw_packet->delayed_work, + send_time); +} + +#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) +/* add a broadcast packet to the queue and setup timers. broadcast packets + * are sent multiple times to increase probability for beeing received. + * + * This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on + * errors. + * + * The skb is not consumed, so the caller should make sure that the + * skb is freed. */ +int add_bcast_packet_to_list(struct sk_buff *skb) +{ + struct forw_packet *forw_packet; + struct bcast_packet *bcast_packet; + + if (!atomic_dec_not_zero(&bcast_queue_left)) { + bat_dbg(DBG_BATMAN, "bcast packet queue full\n"); + goto out; + } + + forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + + if (!forw_packet) + goto out_and_inc; + + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + goto packet_free; + + /* as we have a copy now, it is safe to decrease the TTL */ + bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet->ttl--; + + skb_reset_mac_header(skb); + + forw_packet->skb = skb; + forw_packet->packet_buff = NULL; + + /* how often did we send the bcast packet ? */ + forw_packet->num_packets = 0; + + _add_bcast_packet_to_list(forw_packet, 1); + return NETDEV_TX_OK; + +packet_free: + kfree(forw_packet); +out_and_inc: + atomic_inc(&bcast_queue_left); +out: + return NETDEV_TX_BUSY; +} + +static void send_outstanding_bcast_packet(struct work_struct *work) +{ + struct batman_if *batman_if; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct forw_packet *forw_packet = + container_of(delayed_work, struct forw_packet, delayed_work); + unsigned long flags; + struct sk_buff *skb1; + + spin_lock_irqsave(&forw_bcast_list_lock, flags); + hlist_del(&forw_packet->list); + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + if (atomic_read(&module_state) == MODULE_DEACTIVATING) + goto out; + + /* rebroadcast packet */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + /* send a copy of the saved skb */ + skb1 = skb_copy(forw_packet->skb, GFP_ATOMIC); + if (skb1) + send_skb_packet(skb1, - batman_if, broadcastAddr); ++ batman_if, broadcast_addr); + } + rcu_read_unlock(); + + forw_packet->num_packets++; + + /* if we still have some more bcasts to send */ + if (forw_packet->num_packets < 3) { + _add_bcast_packet_to_list(forw_packet, ((5 * HZ) / 1000)); + return; + } + +out: + forw_packet_free(forw_packet); + atomic_inc(&bcast_queue_left); +} + +void send_outstanding_bat_packet(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct forw_packet *forw_packet = + container_of(delayed_work, struct forw_packet, delayed_work); + unsigned long flags; + + spin_lock_irqsave(&forw_bat_list_lock, flags); + hlist_del(&forw_packet->list); + spin_unlock_irqrestore(&forw_bat_list_lock, flags); + + if (atomic_read(&module_state) == MODULE_DEACTIVATING) + goto out; + + send_packet(forw_packet); + + /** + * we have to have at least one packet in the queue + * to determine the queues wake up time unless we are + * shutting down + */ + if (forw_packet->own) + schedule_own_packet(forw_packet->if_incoming); + +out: + /* don't count own packet */ + if (!forw_packet->own) + atomic_inc(&batman_queue_left); + + forw_packet_free(forw_packet); +} + +void purge_outstanding_packets(struct batman_if *batman_if) +{ + struct forw_packet *forw_packet; + struct hlist_node *tmp_node, *safe_tmp_node; + unsigned long flags; + + if (batman_if) + bat_dbg(DBG_BATMAN, "purge_outstanding_packets(): %s\n", + batman_if->dev); + else + bat_dbg(DBG_BATMAN, "purge_outstanding_packets()\n"); + + /* free bcast list */ + spin_lock_irqsave(&forw_bcast_list_lock, flags); + hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, + &forw_bcast_list, list) { + + /** + * if purge_outstanding_packets() was called with an argmument + * we delete only packets belonging to the given interface + */ + if ((batman_if) && + (forw_packet->if_incoming != batman_if)) + continue; + + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + /** + * send_outstanding_bcast_packet() will lock the list to + * delete the item from the list + */ + cancel_delayed_work_sync(&forw_packet->delayed_work); + spin_lock_irqsave(&forw_bcast_list_lock, flags); + } + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + /* free batman packet list */ + spin_lock_irqsave(&forw_bat_list_lock, flags); + hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, + &forw_bat_list, list) { + + /** + * if purge_outstanding_packets() was called with an argmument + * we delete only packets belonging to the given interface + */ + if ((batman_if) && + (forw_packet->if_incoming != batman_if)) + continue; + + spin_unlock_irqrestore(&forw_bat_list_lock, flags); + + /** + * send_outstanding_bat_packet() will lock the list to + * delete the item from the list + */ + cancel_delayed_work_sync(&forw_packet->delayed_work); + spin_lock_irqsave(&forw_bat_list_lock, flags); + } + spin_unlock_irqrestore(&forw_bat_list_lock, flags); +} diff --combined drivers/staging/batman-adv/send.h index 0a0990d,b64c627..b64c627 --- a/drivers/staging/batman-adv/send.h +++ b/drivers/staging/batman-adv/send.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_SEND_H_ + #define _NET_BATMAN_ADV_SEND_H_ + #include "types.h"
int send_skb_packet(struct sk_buff *skb, @@@ -35,3 -38,5 +38,5 @@@ void schedule_forward_packet(struct ori int add_bcast_packet_to_list(struct sk_buff *skb); void send_outstanding_bat_packet(struct work_struct *work); void purge_outstanding_packets(struct batman_if *batman_if); + + #endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --combined drivers/staging/batman-adv/soft-interface.c index ef7860d,0000000..2ea97de mode 100644,000000..100644 --- a/drivers/staging/batman-adv/soft-interface.c +++ b/drivers/staging/batman-adv/soft-interface.c @@@ -1,361 -1,0 +1,361 @@@ +/* + * 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 "soft-interface.h" +#include "hard-interface.h" +#include "routing.h" +#include "send.h" +#include "translation-table.h" +#include "types.h" +#include "hash.h" +#include <linux/slab.h> +#include <linux/ethtool.h> +#include <linux/etherdevice.h> + +static uint32_t bcast_seqno = 1; /* give own bcast messages seq numbers to avoid + * broadcast storms */ +static int32_t skb_packets; +static int32_t skb_bad_packets; + - unsigned char mainIfAddr[ETH_ALEN]; ++unsigned char main_if_addr[ETH_ALEN]; +static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); +static void bat_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info); +static u32 bat_get_msglevel(struct net_device *dev); +static void bat_set_msglevel(struct net_device *dev, u32 value); +static u32 bat_get_link(struct net_device *dev); +static u32 bat_get_rx_csum(struct net_device *dev); +static int bat_set_rx_csum(struct net_device *dev, u32 data); + +static const struct ethtool_ops bat_ethtool_ops = { + .get_settings = bat_get_settings, + .get_drvinfo = bat_get_drvinfo, + .get_msglevel = bat_get_msglevel, + .set_msglevel = bat_set_msglevel, + .get_link = bat_get_link, + .get_rx_csum = bat_get_rx_csum, + .set_rx_csum = bat_set_rx_csum +}; + +void set_main_if_addr(uint8_t *addr) +{ - memcpy(mainIfAddr, addr, ETH_ALEN); ++ memcpy(main_if_addr, addr, ETH_ALEN); +} + +int my_skb_push(struct sk_buff *skb, unsigned int len) +{ + int result = 0; + + skb_packets++; + if (skb_headroom(skb) < len) { + skb_bad_packets++; + result = pskb_expand_head(skb, len, 0, GFP_ATOMIC); + + if (result < 0) + return result; + } + + skb_push(skb, len); + return 0; +} + +static int interface_open(struct net_device *dev) +{ + netif_start_queue(dev); + return 0; +} + +static int interface_release(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +static struct net_device_stats *interface_stats(struct net_device *dev) +{ + struct bat_priv *priv = netdev_priv(dev); + return &priv->stats; +} + +static int interface_set_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + /* only modify hna-table if it has been initialised before */ + if (atomic_read(&module_state) == MODULE_ACTIVE) { + hna_local_remove(dev->dev_addr, "mac address changed"); + hna_local_add(addr->sa_data); + } + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + + return 0; +} + +static int interface_change_mtu(struct net_device *dev, int new_mtu) +{ + /* check ranges */ + if ((new_mtu < 68) || (new_mtu > hardif_min_mtu())) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + +int interface_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct unicast_packet *unicast_packet; + struct bcast_packet *bcast_packet; + struct orig_node *orig_node; + struct neigh_node *router; + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct bat_priv *priv = netdev_priv(dev); + struct batman_if *batman_if; + struct bat_priv *bat_priv; + uint8_t dstaddr[6]; + int data_len = skb->len; + unsigned long flags; + + if (atomic_read(&module_state) != MODULE_ACTIVE) + goto dropped; + + /* FIXME: each batman_if will be attached to a softif */ + bat_priv = netdev_priv(soft_device); + + dev->trans_start = jiffies; + /* TODO: check this for locks */ + hna_local_add(ethhdr->h_source); + + /* ethernet packet should be broadcasted */ + if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) { + + if (my_skb_push(skb, sizeof(struct bcast_packet)) < 0) + goto dropped; + + bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet->version = COMPAT_VERSION; + bcast_packet->ttl = TTL; + + /* batman packet type: broadcast */ + bcast_packet->packet_type = BAT_BCAST; + + /* hw address of first interface is the orig mac because only + * this mac is known throughout the mesh */ - memcpy(bcast_packet->orig, mainIfAddr, ETH_ALEN); ++ memcpy(bcast_packet->orig, main_if_addr, ETH_ALEN); + + /* set broadcast sequence number */ + bcast_packet->seqno = htonl(bcast_seqno); + + /* broadcast packet. on success, increase seqno. */ + if (add_bcast_packet_to_list(skb) == NETDEV_TX_OK) + bcast_seqno++; + + /* a copy is stored in the bcast list, therefore removing + * the original skb. */ + kfree_skb(skb); + + /* unicast packet */ + } else { + spin_lock_irqsave(&orig_hash_lock, flags); + /* get routing information */ + orig_node = ((struct orig_node *)hash_find(orig_hash, + ethhdr->h_dest)); + + /* check for hna host */ + if (!orig_node) + orig_node = transtable_search(ethhdr->h_dest); + + router = find_router(orig_node, NULL); + + if (!router) + goto unlock; + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + + batman_if = router->if_incoming; + memcpy(dstaddr, router->addr, ETH_ALEN); + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + if (batman_if->if_status != IF_ACTIVE) + goto dropped; + + if (my_skb_push(skb, sizeof(struct unicast_packet)) < 0) + goto dropped; + + unicast_packet = (struct unicast_packet *)skb->data; + + unicast_packet->version = COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->packet_type = BAT_UNICAST; + /* set unicast ttl */ + unicast_packet->ttl = TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + + send_skb_packet(skb, batman_if, dstaddr); + } + + priv->stats.tx_packets++; + priv->stats.tx_bytes += data_len; + goto end; + +unlock: + spin_unlock_irqrestore(&orig_hash_lock, flags); +dropped: + priv->stats.tx_dropped++; + kfree_skb(skb); +end: + return NETDEV_TX_OK; +} + +void interface_rx(struct sk_buff *skb, int hdr_size) +{ + struct net_device *dev = soft_device; + struct bat_priv *priv = netdev_priv(dev); + + /* check if enough space is available for pulling, and pull */ + if (!pskb_may_pull(skb, hdr_size)) { + kfree_skb(skb); + return; + } + skb_pull_rcsum(skb, hdr_size); +/* skb_set_mac_header(skb, -sizeof(struct ethhdr));*/ + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + + /* should not be neccesary anymore as we use skb_pull_rcsum() + * TODO: please verify this and remove this TODO + * -- Dec 21st 2009, Simon Wunderlich */ + +/* skb->ip_summed = CHECKSUM_UNNECESSARY;*/ + + /* TODO: set skb->pkt_type to PACKET_BROADCAST, PACKET_MULTICAST, + * PACKET_OTHERHOST or PACKET_HOST */ + + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; + + dev->last_rx = jiffies; + + netif_rx(skb); +} + +#ifdef HAVE_NET_DEVICE_OPS +static const struct net_device_ops bat_netdev_ops = { + .ndo_open = interface_open, + .ndo_stop = interface_release, + .ndo_get_stats = interface_stats, + .ndo_set_mac_address = interface_set_mac_addr, + .ndo_change_mtu = interface_change_mtu, + .ndo_start_xmit = interface_tx, + .ndo_validate_addr = eth_validate_addr +}; +#endif + +void interface_setup(struct net_device *dev) +{ + struct bat_priv *priv = netdev_priv(dev); + char dev_addr[ETH_ALEN]; + + ether_setup(dev); + +#ifdef HAVE_NET_DEVICE_OPS + dev->netdev_ops = &bat_netdev_ops; +#else + dev->open = interface_open; + dev->stop = interface_release; + dev->get_stats = interface_stats; + dev->set_mac_address = interface_set_mac_addr; + dev->change_mtu = interface_change_mtu; + dev->hard_start_xmit = interface_tx; +#endif + dev->destructor = free_netdev; + + dev->mtu = hardif_min_mtu(); + dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the + * skbuff for our header */ + + /* generate random address */ + random_ether_addr(dev_addr); + memcpy(dev->dev_addr, dev_addr, ETH_ALEN); + + SET_ETHTOOL_OPS(dev, &bat_ethtool_ops); + + memset(priv, 0, sizeof(struct bat_priv)); +} + +/* ethtool */ +static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + cmd->supported = 0; + cmd->advertising = 0; + cmd->speed = SPEED_10; + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_TP; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + + return 0; +} + +static void bat_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, "B.A.T.M.A.N. advanced"); + strcpy(info->version, SOURCE_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, "batman"); +} + +static u32 bat_get_msglevel(struct net_device *dev) +{ + return -EOPNOTSUPP; +} + +static void bat_set_msglevel(struct net_device *dev, u32 value) +{ +} + +static u32 bat_get_link(struct net_device *dev) +{ + return 1; +} + +static u32 bat_get_rx_csum(struct net_device *dev) +{ + return 0; +} + +static int bat_set_rx_csum(struct net_device *dev, u32 data) +{ + return -EOPNOTSUPP; +} diff --combined drivers/staging/batman-adv/soft-interface.h index 3852c57,6364854..6364854 --- a/drivers/staging/batman-adv/soft-interface.h +++ b/drivers/staging/batman-adv/soft-interface.h @@@ -19,10 -19,15 +19,15 @@@ * */
+ #ifndef _NET_BATMAN_ADV_SOFT_INTERFACE_H_ + #define _NET_BATMAN_ADV_SOFT_INTERFACE_H_ + void set_main_if_addr(uint8_t *addr); void interface_setup(struct net_device *dev); int interface_tx(struct sk_buff *skb, struct net_device *dev); void interface_rx(struct sk_buff *skb, int hdr_size); int my_skb_push(struct sk_buff *skb, unsigned int len);
- extern unsigned char mainIfAddr[]; + extern unsigned char main_if_addr[]; + + #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ diff --combined drivers/staging/batman-adv/translation-table.c index 9fd32a9,0000000..9e3c845 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/translation-table.c +++ b/drivers/staging/batman-adv/translation-table.c @@@ -1,499 -1,0 +1,498 @@@ +/* + * 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 "translation-table.h" +#include "soft-interface.h" +#include "types.h" +#include "hash.h" + +struct hashtable_t *hna_local_hash; +static struct hashtable_t *hna_global_hash; +atomic_t hna_local_changed; + +DEFINE_SPINLOCK(hna_local_hash_lock); +static DEFINE_SPINLOCK(hna_global_hash_lock); + +static void hna_local_purge(struct work_struct *work); +static DECLARE_DELAYED_WORK(hna_local_purge_wq, hna_local_purge); +static void _hna_global_del_orig(struct hna_global_entry *hna_global_entry, + char *message); + +static void hna_local_start_timer(void) +{ + queue_delayed_work(bat_event_workqueue, &hna_local_purge_wq, 10 * HZ); +} + +int hna_local_init(void) +{ + if (hna_local_hash) + return 1; + + hna_local_hash = hash_new(128, compare_orig, choose_orig); + + if (!hna_local_hash) + return 0; + + atomic_set(&hna_local_changed, 0); + hna_local_start_timer(); + + return 1; +} + +void hna_local_add(uint8_t *addr) +{ + struct hna_local_entry *hna_local_entry; + struct hna_global_entry *hna_global_entry; + struct hashtable_t *swaphash; + unsigned long flags; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + hna_local_entry = + ((struct hna_local_entry *)hash_find(hna_local_hash, addr)); + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + if (hna_local_entry != NULL) { + hna_local_entry->last_seen = jiffies; + return; + } + + /* only announce as many hosts as possible in the batman-packet and + space in batman_packet->num_hna That also should give a limit to + MAC-flooding. */ + if ((num_hna + 1 > (ETH_DATA_LEN - BAT_PACKET_LEN) / ETH_ALEN) || + (num_hna + 1 > 255)) { + bat_dbg(DBG_ROUTES, + "Can't add new local hna entry (%pM): " + "number of local hna entries exceeds packet size\n", + addr); + return; + } + + bat_dbg(DBG_ROUTES, "Creating new local hna entry: %pM\n", + addr); + + hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC); + if (!hna_local_entry) + return; + + memcpy(hna_local_entry->addr, addr, ETH_ALEN); + hna_local_entry->last_seen = jiffies; + + /* the batman interface mac address should never be purged */ + if (compare_orig(addr, soft_device->dev_addr)) + hna_local_entry->never_purge = 1; + else + hna_local_entry->never_purge = 0; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + hash_add(hna_local_hash, hna_local_entry); + num_hna++; + atomic_set(&hna_local_changed, 1); + + if (hna_local_hash->elements * 4 > hna_local_hash->size) { + swaphash = hash_resize(hna_local_hash, + hna_local_hash->size * 2); + + if (swaphash == NULL) + printk(KERN_ERR "batman-adv:" + "Couldn't resize local hna hash table\n"); + else + hna_local_hash = swaphash; + } + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + /* remove address from global hash if present */ + spin_lock_irqsave(&hna_global_hash_lock, flags); + + hna_global_entry = + ((struct hna_global_entry *)hash_find(hna_global_hash, addr)); + + if (hna_global_entry != NULL) + _hna_global_del_orig(hna_global_entry, "local hna received"); + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); +} + +int hna_local_fill_buffer(unsigned char *buff, int buff_len) +{ + struct hna_local_entry *hna_local_entry; + HASHIT(hashit); + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + while (hash_iterate(hna_local_hash, &hashit)) { + + if (buff_len < (i + 1) * ETH_ALEN) + break; + + hna_local_entry = hashit.bucket->data; + memcpy(buff + (i * ETH_ALEN), hna_local_entry->addr, ETH_ALEN); + + i++; + } + + /* if we did not get all new local hnas see you next time ;-) */ + if (i == num_hna) + atomic_set(&hna_local_changed, 0); + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + return i; +} + +int hna_local_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hna_local_entry *hna_local_entry; + HASHIT(hashit); + HASHIT(hashit_count); + unsigned long flags; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Locally retrieved addresses (from %s) " + "announced via HNA:\n", + net_dev->name); + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + buf_size = 1; + /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ + while (hash_iterate(hna_local_hash, &hashit_count)) + buf_size += 21; + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + return -ENOMEM; + } + buff[0] = '\0'; + pos = 0; + + while (hash_iterate(hna_local_hash, &hashit)) { + hna_local_entry = hashit.bucket->data; + + pos += snprintf(buff + pos, 22, " * %pM\n", + hna_local_entry->addr); + } + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void _hna_local_del(void *data) +{ + kfree(data); + num_hna--; + atomic_set(&hna_local_changed, 1); +} + +static void hna_local_del(struct hna_local_entry *hna_local_entry, + char *message) +{ + bat_dbg(DBG_ROUTES, "Deleting local hna entry (%pM): %s\n", + hna_local_entry->addr, message); + + hash_remove(hna_local_hash, hna_local_entry->addr); + _hna_local_del(hna_local_entry); +} + +void hna_local_remove(uint8_t *addr, char *message) +{ + struct hna_local_entry *hna_local_entry; + unsigned long flags; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + hna_local_entry = (struct hna_local_entry *) + hash_find(hna_local_hash, addr); + if (hna_local_entry) + hna_local_del(hna_local_entry, message); + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); +} + +static void hna_local_purge(struct work_struct *work) +{ + struct hna_local_entry *hna_local_entry; + HASHIT(hashit); + unsigned long flags; + unsigned long timeout; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + while (hash_iterate(hna_local_hash, &hashit)) { + hna_local_entry = hashit.bucket->data; + - timeout = hna_local_entry->last_seen + - ((LOCAL_HNA_TIMEOUT / 1000) * HZ); ++ timeout = hna_local_entry->last_seen + LOCAL_HNA_TIMEOUT * HZ; + if ((!hna_local_entry->never_purge) && + time_after(jiffies, timeout)) + hna_local_del(hna_local_entry, "address timed out"); + } + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + hna_local_start_timer(); +} + +void hna_local_free(void) +{ + if (!hna_local_hash) + return; + + cancel_delayed_work_sync(&hna_local_purge_wq); + hash_delete(hna_local_hash, _hna_local_del); + hna_local_hash = NULL; +} + +int hna_global_init(void) +{ + if (hna_global_hash) + return 1; + + hna_global_hash = hash_new(128, compare_orig, choose_orig); + + if (!hna_global_hash) + return 0; + + return 1; +} + +void hna_global_add_orig(struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len) +{ + struct hna_global_entry *hna_global_entry; + struct hna_local_entry *hna_local_entry; + struct hashtable_t *swaphash; + int hna_buff_count = 0; + unsigned long flags; + unsigned char *hna_ptr; + + while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) { + spin_lock_irqsave(&hna_global_hash_lock, flags); + + hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); + hna_global_entry = (struct hna_global_entry *) + hash_find(hna_global_hash, hna_ptr); + + if (hna_global_entry == NULL) { + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + hna_global_entry = + kmalloc(sizeof(struct hna_global_entry), + GFP_ATOMIC); + + if (!hna_global_entry) + break; + + memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN); + + bat_dbg(DBG_ROUTES, + "Creating new global hna entry: " + "%pM (via %pM)\n", + hna_global_entry->addr, orig_node->orig); + + spin_lock_irqsave(&hna_global_hash_lock, flags); + hash_add(hna_global_hash, hna_global_entry); + + } + + hna_global_entry->orig_node = orig_node; + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + /* remove address from local hash if present */ + spin_lock_irqsave(&hna_local_hash_lock, flags); + + hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); + hna_local_entry = (struct hna_local_entry *) + hash_find(hna_local_hash, hna_ptr); + + if (hna_local_entry != NULL) + hna_local_del(hna_local_entry, "global hna received"); + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + hna_buff_count++; + } + + /* initialize, and overwrite if malloc succeeds */ + orig_node->hna_buff = NULL; + orig_node->hna_buff_len = 0; + + if (hna_buff_len > 0) { + orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC); + if (orig_node->hna_buff) { + memcpy(orig_node->hna_buff, hna_buff, hna_buff_len); + orig_node->hna_buff_len = hna_buff_len; + } + } + + spin_lock_irqsave(&hna_global_hash_lock, flags); + + if (hna_global_hash->elements * 4 > hna_global_hash->size) { + swaphash = hash_resize(hna_global_hash, + hna_global_hash->size * 2); + + if (swaphash == NULL) + printk(KERN_ERR "batman-adv:" + "Couldn't resize global hna hash table\n"); + else + hna_global_hash = swaphash; + } + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); +} + +int hna_global_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hna_global_entry *hna_global_entry; + HASHIT(hashit); + HASHIT(hashit_count); + unsigned long flags; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Globally announced HNAs received via the mesh %s\n", + net_dev->name); + + spin_lock_irqsave(&hna_global_hash_lock, flags); + + buf_size = 1; + /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/ + while (hash_iterate(hna_global_hash, &hashit_count)) + buf_size += 43; + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + return -ENOMEM; + } + buff[0] = '\0'; + pos = 0; + + while (hash_iterate(hna_global_hash, &hashit)) { + hna_global_entry = hashit.bucket->data; + + pos += snprintf(buff + pos, 44, + " * %pM via %pM\n", hna_global_entry->addr, + hna_global_entry->orig_node->orig); + } + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void _hna_global_del_orig(struct hna_global_entry *hna_global_entry, + char *message) +{ + bat_dbg(DBG_ROUTES, "Deleting global hna entry %pM (via %pM): %s\n", + hna_global_entry->addr, hna_global_entry->orig_node->orig, + message); + + hash_remove(hna_global_hash, hna_global_entry->addr); + kfree(hna_global_entry); +} + +void hna_global_del_orig(struct orig_node *orig_node, char *message) +{ + struct hna_global_entry *hna_global_entry; + int hna_buff_count = 0; + unsigned long flags; + unsigned char *hna_ptr; + + if (orig_node->hna_buff_len == 0) + return; + + spin_lock_irqsave(&hna_global_hash_lock, flags); + + while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { + hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); + hna_global_entry = (struct hna_global_entry *) + hash_find(hna_global_hash, hna_ptr); + + if ((hna_global_entry != NULL) && + (hna_global_entry->orig_node == orig_node)) + _hna_global_del_orig(hna_global_entry, message); + + hna_buff_count++; + } + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + orig_node->hna_buff_len = 0; + kfree(orig_node->hna_buff); + orig_node->hna_buff = NULL; +} + +static void hna_global_del(void *data) +{ + kfree(data); +} + +void hna_global_free(void) +{ + if (!hna_global_hash) + return; + + hash_delete(hna_global_hash, hna_global_del); + hna_global_hash = NULL; +} + +struct orig_node *transtable_search(uint8_t *addr) +{ + struct hna_global_entry *hna_global_entry; + unsigned long flags; + + spin_lock_irqsave(&hna_global_hash_lock, flags); + hna_global_entry = (struct hna_global_entry *) + hash_find(hna_global_hash, addr); + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + if (hna_global_entry == NULL) + return NULL; + + return hna_global_entry->orig_node; +} diff --combined drivers/staging/batman-adv/translation-table.h index 232208f,fa93e37..fa93e37 --- a/drivers/staging/batman-adv/translation-table.h +++ b/drivers/staging/batman-adv/translation-table.h @@@ -19,6 -19,9 +19,9 @@@ * */
+ #ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ + #define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ + #include "types.h"
int hna_local_init(void); @@@ -38,3 -41,5 +41,5 @@@ struct orig_node *transtable_search(uin extern spinlock_t hna_local_hash_lock; extern struct hashtable_t *hna_local_hash; extern atomic_t hna_local_changed; + + #endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --combined drivers/staging/batman-adv/types.h index e1fc460,82602d0..82602d0 --- a/drivers/staging/batman-adv/types.h +++ b/drivers/staging/batman-adv/types.h @@@ -23,8 -23,8 +23,8 @@@
- #ifndef TYPES_H - #define TYPES_H + #ifndef _NET_BATMAN_ADV_TYPES_H_ + #define _NET_BATMAN_ADV_TYPES_H_
#include "packet.h" #include "bitarray.h" @@@ -172,4 -172,4 +172,4 @@@ struct if_list_entry struct hlist_node list; };
- #endif + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ diff --combined drivers/staging/batman-adv/vis.c index d9ab981,0000000..ddee0f2 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/vis.c +++ b/drivers/staging/batman-adv/vis.c @@@ -1,818 -1,0 +1,818 @@@ +/* + * Copyright (C) 2008-2010 B.A.T.M.A.N. contributors: + * + * 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 "send.h" +#include "translation-table.h" +#include "vis.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "hash.h" + +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + they handle overflows/underflows and can correctly check for a + predecessor/successor unless the variable sequence number has grown by + more then 2**(bitwidth(x)-1)-1. + This means that for a uint8_t with the maximum value 255, it would think: + * when adding nothing - it is neither a predecessor nor a successor + * before adding more than 127 to the starting value - it is a predecessor, + * when adding 128 - it is neither a predecessor nor a successor, + * after adding more than 127 to the starting value - it is a successor */ +#define seq_before(x, y) ({typeof(x) _dummy = (x - y); \ + _dummy > smallest_signed_int(_dummy); }) +#define seq_after(x, y) seq_before(y, x) + +static struct hashtable_t *vis_hash; +static DEFINE_SPINLOCK(vis_hash_lock); +static DEFINE_SPINLOCK(recv_list_lock); +static struct vis_info *my_vis_info; +static struct list_head send_list; /* always locked with vis_hash_lock */ + +static void start_vis_timer(void); + +/* free the info */ +static void free_info(struct kref *ref) +{ + struct vis_info *info = container_of(ref, struct vis_info, refcount); + struct recvlist_node *entry, *tmp; + unsigned long flags; + + list_del_init(&info->send_list); + spin_lock_irqsave(&recv_list_lock, flags); + list_for_each_entry_safe(entry, tmp, &info->recv_list, list) { + list_del(&entry->list); + kfree(entry); + } + spin_unlock_irqrestore(&recv_list_lock, flags); + kfree(info); +} + +/* Compare two vis packets, used by the hashing algorithm */ +static int vis_info_cmp(void *data1, void *data2) +{ + struct vis_info *d1, *d2; + d1 = data1; + d2 = data2; + return compare_orig(d1->packet.vis_orig, d2->packet.vis_orig); +} + +/* hash function to choose an entry in a hash table of given size */ +/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ +static int vis_info_choose(void *data, int size) +{ + struct vis_info *vis_info = data; + unsigned char *key; + uint32_t hash = 0; + size_t i; + + key = vis_info->packet.vis_orig; + for (i = 0; i < ETH_ALEN; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +/* insert interface to the list of interfaces of one originator, if it + * does not already exist in the list */ +static void vis_data_insert_interface(const uint8_t *interface, + struct hlist_head *if_list, + bool primary) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (compare_orig(entry->addr, (void *)interface)) + return; + } + + /* its a new address, add it to the list */ + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, interface, ETH_ALEN); + entry->primary = primary; + hlist_add_head(&entry->list, if_list); +} + +static ssize_t vis_data_read_prim_sec(char *buff, struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + char tmp_addr_str[ETH_STR_LEN]; + size_t len = 0; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (entry->primary) + len += sprintf(buff + len, "PRIMARY, "); + else { + addr_to_string(tmp_addr_str, entry->addr); + len += sprintf(buff + len, "SEC %s, ", tmp_addr_str); + } + } + + return len; +} + +static size_t vis_data_count_prim_sec(struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + size_t count = 0; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (entry->primary) + count += 9; + else + count += 23; + } + + return count; +} + +/* read an entry */ +static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, + uint8_t *src, bool primary) +{ + char to[18]; + + /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ + addr_to_string(to, entry->dest); + if (primary && entry->quality == 0) + return sprintf(buff, "HNA %s, ", to); + else if (compare_orig(entry->src, src)) + return sprintf(buff, "TQ %s %d, ", to, entry->quality); + + return 0; +} + +int vis_seq_print_text(struct seq_file *seq, void *offset) +{ + HASHIT(hashit); + HASHIT(hashit_count); + struct vis_info *info; + struct vis_info_entry *entries; + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + HLIST_HEAD(vis_if_list); + struct if_list_entry *entry; + struct hlist_node *pos, *n; + int i; + char tmp_addr_str[ETH_STR_LEN]; + unsigned long flags; + int vis_server = atomic_read(&bat_priv->vis_mode); + size_t buff_pos, buf_size; + char *buff; + + if ((!bat_priv->primary_if) || + (vis_server == VIS_TYPE_CLIENT_UPDATE)) + return 0; + + buf_size = 1; + /* Estimate length */ + spin_lock_irqsave(&vis_hash_lock, flags); + while (hash_iterate(vis_hash, &hashit_count)) { + info = hashit_count.bucket->data; + entries = (struct vis_info_entry *) + ((char *)info + sizeof(struct vis_info)); + + for (i = 0; i < info->packet.entries; i++) { + if (entries[i].quality == 0) + continue; + vis_data_insert_interface(entries[i].src, &vis_if_list, + compare_orig(entries[i].src, + info->packet.vis_orig)); + } + + hlist_for_each_entry(entry, pos, &vis_if_list, list) { + buf_size += 18 + 26 * info->packet.entries; + + /* add primary/secondary records */ + if (compare_orig(entry->addr, info->packet.vis_orig)) + buf_size += + vis_data_count_prim_sec(&vis_if_list); + + buf_size += 1; + } + + hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, list) { + hlist_del(&entry->list); + kfree(entry); + } + } + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_irqrestore(&vis_hash_lock, flags); + return -ENOMEM; + } + buff[0] = '\0'; + buff_pos = 0; + + while (hash_iterate(vis_hash, &hashit)) { + info = hashit.bucket->data; + entries = (struct vis_info_entry *) + ((char *)info + sizeof(struct vis_info)); + + for (i = 0; i < info->packet.entries; i++) { + if (entries[i].quality == 0) + continue; + vis_data_insert_interface(entries[i].src, &vis_if_list, + compare_orig(entries[i].src, + info->packet.vis_orig)); + } + + hlist_for_each_entry(entry, pos, &vis_if_list, list) { + addr_to_string(tmp_addr_str, entry->addr); + buff_pos += sprintf(buff + buff_pos, "%s,", + tmp_addr_str); + + for (i = 0; i < info->packet.entries; i++) + buff_pos += vis_data_read_entry(buff + buff_pos, + &entries[i], + entry->addr, + entry->primary); + + /* add primary/secondary records */ + if (compare_orig(entry->addr, info->packet.vis_orig)) + buff_pos += + vis_data_read_prim_sec(buff + buff_pos, + &vis_if_list); + + buff_pos += sprintf(buff + buff_pos, "\n"); + } + + hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, list) { + hlist_del(&entry->list); + kfree(entry); + } + } + + spin_unlock_irqrestore(&vis_hash_lock, flags); + + seq_printf(seq, "%s", buff); + kfree(buff); + + return 0; +} + +/* add the info packet to the send list, if it was not + * already linked in. */ +static void send_list_add(struct vis_info *info) +{ + if (list_empty(&info->send_list)) { + kref_get(&info->refcount); + list_add_tail(&info->send_list, &send_list); + } +} + +/* delete the info packet from the send list, if it was + * linked in. */ +static void send_list_del(struct vis_info *info) +{ + if (!list_empty(&info->send_list)) { + list_del_init(&info->send_list); + kref_put(&info->refcount, free_info); + } +} + +/* tries to add one entry to the receive list. */ +static void recv_list_add(struct list_head *recv_list, char *mac) +{ + struct recvlist_node *entry; + unsigned long flags; + + entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); + if (!entry) + return; + + memcpy(entry->mac, mac, ETH_ALEN); + spin_lock_irqsave(&recv_list_lock, flags); + list_add_tail(&entry->list, recv_list); + spin_unlock_irqrestore(&recv_list_lock, flags); +} + +/* returns 1 if this mac is in the recv_list */ +static int recv_list_is_in(struct list_head *recv_list, char *mac) +{ + struct recvlist_node *entry; + unsigned long flags; + + spin_lock_irqsave(&recv_list_lock, flags); + list_for_each_entry(entry, recv_list, list) { + if (memcmp(entry->mac, mac, ETH_ALEN) == 0) { + spin_unlock_irqrestore(&recv_list_lock, flags); + return 1; + } + } + spin_unlock_irqrestore(&recv_list_lock, flags); + return 0; +} + +/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old, + * broken.. ). vis hash must be locked outside. is_new is set when the packet + * is newer than old entries in the hash. */ +static struct vis_info *add_packet(struct vis_packet *vis_packet, + int vis_info_len, int *is_new, + int make_broadcast) +{ + struct vis_info *info, *old_info; + struct vis_info search_elem; + + *is_new = 0; + /* sanity check */ + if (vis_hash == NULL) + return NULL; + + /* see if the packet is already in vis_hash */ + memcpy(search_elem.packet.vis_orig, vis_packet->vis_orig, ETH_ALEN); + old_info = hash_find(vis_hash, &search_elem); + + if (old_info != NULL) { + if (!seq_after(ntohl(vis_packet->seqno), + ntohl(old_info->packet.seqno))) { + if (old_info->packet.seqno == vis_packet->seqno) { + recv_list_add(&old_info->recv_list, + vis_packet->sender_orig); + return old_info; + } else { + /* newer packet is already in hash. */ + return NULL; + } + } + /* remove old entry */ + hash_remove(vis_hash, old_info); + send_list_del(old_info); + kref_put(&old_info->refcount, free_info); + } + + info = kmalloc(sizeof(struct vis_info) + vis_info_len, GFP_ATOMIC); + if (info == NULL) + return NULL; + + kref_init(&info->refcount); + INIT_LIST_HEAD(&info->send_list); + INIT_LIST_HEAD(&info->recv_list); + info->first_seen = jiffies; + memcpy(&info->packet, vis_packet, + sizeof(struct vis_packet) + vis_info_len); + + /* initialize and add new packet. */ + *is_new = 1; + + /* Make it a broadcast packet, if required */ + if (make_broadcast) - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); ++ memcpy(info->packet.target_orig, broadcast_addr, ETH_ALEN); + + /* repair if entries is longer than packet. */ + if (info->packet.entries * sizeof(struct vis_info_entry) > vis_info_len) + info->packet.entries = vis_info_len / + sizeof(struct vis_info_entry); + + recv_list_add(&info->recv_list, info->packet.sender_orig); + + /* try to add it */ + if (hash_add(vis_hash, info) < 0) { + /* did not work (for some reason) */ + kref_put(&old_info->refcount, free_info); + info = NULL; + } + + return info; +} + +/* handle the server sync packet, forward if needed. */ +void receive_server_sync_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len) +{ + struct vis_info *info; + int is_new, make_broadcast; + unsigned long flags; + int vis_server = atomic_read(&bat_priv->vis_mode); + + make_broadcast = (vis_server == VIS_TYPE_SERVER_SYNC); + + spin_lock_irqsave(&vis_hash_lock, flags); + info = add_packet(vis_packet, vis_info_len, &is_new, make_broadcast); + if (info == NULL) + goto end; + + /* only if we are server ourselves and packet is newer than the one in + * hash.*/ + if (vis_server == VIS_TYPE_SERVER_SYNC && is_new) + send_list_add(info); +end: + spin_unlock_irqrestore(&vis_hash_lock, flags); +} + +/* handle an incoming client update packet and schedule forward if needed. */ +void receive_client_update_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len) +{ + struct vis_info *info; + int is_new; + unsigned long flags; + int vis_server = atomic_read(&bat_priv->vis_mode); + int are_target = 0; + + /* clients shall not broadcast. */ + if (is_bcast(vis_packet->target_orig)) + return; + + /* Are we the target for this VIS packet? */ + if (vis_server == VIS_TYPE_SERVER_SYNC && + is_my_mac(vis_packet->target_orig)) + are_target = 1; + + spin_lock_irqsave(&vis_hash_lock, flags); + info = add_packet(vis_packet, vis_info_len, &is_new, are_target); + if (info == NULL) + goto end; + /* note that outdated packets will be dropped at this point. */ + + + /* send only if we're the target server or ... */ + if (are_target && is_new) { + info->packet.vis_type = VIS_TYPE_SERVER_SYNC; /* upgrade! */ + send_list_add(info); + + /* ... we're not the recipient (and thus need to forward). */ + } else if (!is_my_mac(info->packet.target_orig)) { + send_list_add(info); + } +end: + spin_unlock_irqrestore(&vis_hash_lock, flags); +} + +/* Walk the originators and find the VIS server with the best tq. Set the packet + * address to its address and return the best_tq. + * + * Must be called with the originator hash locked */ +static int find_best_vis_server(struct vis_info *info) +{ + HASHIT(hashit); + struct orig_node *orig_node; + int best_tq = -1; + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + if ((orig_node != NULL) && + (orig_node->router != NULL) && + (orig_node->flags & VIS_SERVER) && + (orig_node->router->tq_avg > best_tq)) { + best_tq = orig_node->router->tq_avg; + memcpy(info->packet.target_orig, orig_node->orig, + ETH_ALEN); + } + } + return best_tq; +} + +/* Return true if the vis packet is full. */ +static bool vis_packet_full(struct vis_info *info) +{ + if (info->packet.entries + 1 > + (1000 - sizeof(struct vis_info)) / sizeof(struct vis_info_entry)) + return true; + return false; +} + +/* generates a packet of own vis data, + * returns 0 on success, -1 if no packet could be generated */ +static int generate_vis_packet(struct bat_priv *bat_priv) +{ + HASHIT(hashit_local); + HASHIT(hashit_global); + struct orig_node *orig_node; + struct vis_info *info = (struct vis_info *)my_vis_info; + struct vis_info_entry *entry, *entry_array; + struct hna_local_entry *hna_local_entry; + int best_tq = -1; + unsigned long flags; + + info->first_seen = jiffies; + info->packet.vis_type = atomic_read(&bat_priv->vis_mode); + + spin_lock_irqsave(&orig_hash_lock, flags); - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); ++ memcpy(info->packet.target_orig, broadcast_addr, ETH_ALEN); + info->packet.ttl = TTL; + info->packet.seqno = htonl(ntohl(info->packet.seqno) + 1); + info->packet.entries = 0; + + if (info->packet.vis_type == VIS_TYPE_CLIENT_UPDATE) { + best_tq = find_best_vis_server(info); + if (best_tq < 0) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return -1; + } + } + + entry_array = (struct vis_info_entry *) + ((char *)info + sizeof(struct vis_info)); + + while (hash_iterate(orig_hash, &hashit_global)) { + orig_node = hashit_global.bucket->data; + if (orig_node->router != NULL + && compare_orig(orig_node->router->addr, + orig_node->orig) + && (orig_node->router->if_incoming->if_status == + IF_ACTIVE) + && orig_node->router->tq_avg > 0) { + + /* fill one entry into buffer. */ + entry = &entry_array[info->packet.entries]; + memcpy(entry->src, + orig_node->router->if_incoming->net_dev->dev_addr, + ETH_ALEN); + memcpy(entry->dest, orig_node->orig, ETH_ALEN); + entry->quality = orig_node->router->tq_avg; + info->packet.entries++; + + if (vis_packet_full(info)) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return 0; + } + } + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + spin_lock_irqsave(&hna_local_hash_lock, flags); + while (hash_iterate(hna_local_hash, &hashit_local)) { + hna_local_entry = hashit_local.bucket->data; + entry = &entry_array[info->packet.entries]; + memset(entry->src, 0, ETH_ALEN); + memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN); + entry->quality = 0; /* 0 means HNA */ + info->packet.entries++; + + if (vis_packet_full(info)) { + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + return 0; + } + } + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + return 0; +} + +/* free old vis packets. Must be called with this vis_hash_lock + * held */ +static void purge_vis_packets(void) +{ + HASHIT(hashit); + struct vis_info *info; + + while (hash_iterate(vis_hash, &hashit)) { + info = hashit.bucket->data; + if (info == my_vis_info) /* never purge own data. */ + continue; + if (time_after(jiffies, - info->first_seen + (VIS_TIMEOUT*HZ)/1000)) { ++ info->first_seen + VIS_TIMEOUT * HZ)) { + hash_remove_bucket(vis_hash, &hashit); + send_list_del(info); + kref_put(&info->refcount, free_info); + } + } +} + +static void broadcast_vis_packet(struct vis_info *info, int packet_length) +{ + HASHIT(hashit); + struct orig_node *orig_node; + unsigned long flags; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + + spin_lock_irqsave(&orig_hash_lock, flags); + + /* send to all routers in range. */ + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + + /* if it's a vis server and reachable, send it. */ + if ((!orig_node) || (!orig_node->router)) + continue; + if (!(orig_node->flags & VIS_SERVER)) + continue; + /* don't send it if we already received the packet from + * this node. */ + if (recv_list_is_in(&info->recv_list, orig_node->orig)) + continue; + + memcpy(info->packet.target_orig, orig_node->orig, ETH_ALEN); + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + send_raw_packet((unsigned char *)&info->packet, + packet_length, batman_if, dstaddr); + + spin_lock_irqsave(&orig_hash_lock, flags); + + } + spin_unlock_irqrestore(&orig_hash_lock, flags); - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); ++ memcpy(info->packet.target_orig, broadcast_addr, ETH_ALEN); +} + +static void unicast_vis_packet(struct vis_info *info, int packet_length) +{ + struct orig_node *orig_node; + unsigned long flags; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, info->packet.target_orig)); + + if ((!orig_node) || (!orig_node->router)) + goto out; + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + send_raw_packet((unsigned char *)&info->packet, + packet_length, batman_if, dstaddr); + return; + +out: + spin_unlock_irqrestore(&orig_hash_lock, flags); +} + +/* only send one vis packet. called from send_vis_packets() */ +static void send_vis_packet(struct vis_info *info) +{ + int packet_length; + + if (info->packet.ttl < 2) { + printk(KERN_WARNING "batman-adv: Error - can't send vis packet: ttl exceeded\n"); + return; + } + - memcpy(info->packet.sender_orig, mainIfAddr, ETH_ALEN); ++ memcpy(info->packet.sender_orig, main_if_addr, ETH_ALEN); + info->packet.ttl--; + + packet_length = sizeof(struct vis_packet) + + info->packet.entries * sizeof(struct vis_info_entry); + + if (is_bcast(info->packet.target_orig)) + broadcast_vis_packet(info, packet_length); + else + unicast_vis_packet(info, packet_length); + info->packet.ttl++; /* restore TTL */ +} + +/* called from timer; send (and maybe generate) vis packet. */ +static void send_vis_packets(struct work_struct *work) +{ + struct vis_info *info, *temp; + unsigned long flags; + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + + spin_lock_irqsave(&vis_hash_lock, flags); + + purge_vis_packets(); + + if (generate_vis_packet(bat_priv) == 0) { + /* schedule if generation was successful */ + send_list_add(my_vis_info); + } + + list_for_each_entry_safe(info, temp, &send_list, send_list) { + + kref_get(&info->refcount); + spin_unlock_irqrestore(&vis_hash_lock, flags); + + send_vis_packet(info); + + spin_lock_irqsave(&vis_hash_lock, flags); + send_list_del(info); + kref_put(&info->refcount, free_info); + } + spin_unlock_irqrestore(&vis_hash_lock, flags); + start_vis_timer(); +} +static DECLARE_DELAYED_WORK(vis_timer_wq, send_vis_packets); + +/* init the vis server. this may only be called when if_list is already + * initialized (e.g. bat0 is initialized, interfaces have been added) */ +int vis_init(void) +{ + unsigned long flags; + if (vis_hash) + return 1; + + spin_lock_irqsave(&vis_hash_lock, flags); + + vis_hash = hash_new(256, vis_info_cmp, vis_info_choose); + if (!vis_hash) { + printk(KERN_ERR "batman-adv:Can't initialize vis_hash\n"); + goto err; + } + + my_vis_info = kmalloc(1000, GFP_ATOMIC); + if (!my_vis_info) { + printk(KERN_ERR "batman-adv:Can't initialize vis packet\n"); + goto err; + } + + /* prefill the vis info */ + my_vis_info->first_seen = jiffies - msecs_to_jiffies(VIS_INTERVAL); + INIT_LIST_HEAD(&my_vis_info->recv_list); + INIT_LIST_HEAD(&my_vis_info->send_list); + kref_init(&my_vis_info->refcount); + my_vis_info->packet.version = COMPAT_VERSION; + my_vis_info->packet.packet_type = BAT_VIS; + my_vis_info->packet.ttl = TTL; + my_vis_info->packet.seqno = 0; + my_vis_info->packet.entries = 0; + + INIT_LIST_HEAD(&send_list); + - memcpy(my_vis_info->packet.vis_orig, mainIfAddr, ETH_ALEN); - memcpy(my_vis_info->packet.sender_orig, mainIfAddr, ETH_ALEN); ++ memcpy(my_vis_info->packet.vis_orig, main_if_addr, ETH_ALEN); ++ memcpy(my_vis_info->packet.sender_orig, main_if_addr, ETH_ALEN); + + if (hash_add(vis_hash, my_vis_info) < 0) { + printk(KERN_ERR + "batman-adv:Can't add own vis packet into hash\n"); + /* not in hash, need to remove it manually. */ + kref_put(&my_vis_info->refcount, free_info); + goto err; + } + + spin_unlock_irqrestore(&vis_hash_lock, flags); + start_vis_timer(); + return 1; + +err: + spin_unlock_irqrestore(&vis_hash_lock, flags); + vis_quit(); + return 0; +} + +/* Decrease the reference count on a hash item info */ +static void free_info_ref(void *data) +{ + struct vis_info *info = data; + + send_list_del(info); + kref_put(&info->refcount, free_info); +} + +/* shutdown vis-server */ +void vis_quit(void) +{ + unsigned long flags; + if (!vis_hash) + return; + + cancel_delayed_work_sync(&vis_timer_wq); + + spin_lock_irqsave(&vis_hash_lock, flags); + /* properly remove, kill timers ... */ + hash_delete(vis_hash, free_info_ref); + vis_hash = NULL; + my_vis_info = NULL; + spin_unlock_irqrestore(&vis_hash_lock, flags); +} + +/* schedule packets for (re)transmission */ +static void start_vis_timer(void) +{ + queue_delayed_work(bat_event_workqueue, &vis_timer_wq, + (VIS_INTERVAL * HZ) / 1000); +} diff --combined drivers/staging/batman-adv/vis.h index 1cfadce,bb13bf1..bb13bf1 --- a/drivers/staging/batman-adv/vis.h +++ b/drivers/staging/batman-adv/vis.h @@@ -19,7 -19,10 +19,10 @@@ * */
- #define VIS_TIMEOUT 200000 + #ifndef _NET_BATMAN_ADV_VIS_H_ + #define _NET_BATMAN_ADV_VIS_H_ + + #define VIS_TIMEOUT 200 /* timeout of vis packets in seconds */
struct vis_info { unsigned long first_seen; @@@ -53,3 -56,5 +56,5 @@@ void receive_client_update_packet(struc int vis_info_len); int vis_init(void); void vis_quit(void); + + #endif /* _NET_BATMAN_ADV_VIS_H_ */