Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[pandora-kernel.git] / net / batman-adv / translation-table.c
index 7b72966..fb6931d 100644 (file)
 #include "translation-table.h"
 #include "soft-interface.h"
 #include "hard-interface.h"
+#include "send.h"
 #include "hash.h"
 #include "originator.h"
+#include "routing.h"
 
-static void tt_local_purge(struct work_struct *work);
-static void _tt_global_del_orig(struct bat_priv *bat_priv,
-                                struct tt_global_entry *tt_global_entry,
-                                char *message);
+#include <linux/crc16.h>
+
+static void _tt_global_del(struct bat_priv *bat_priv,
+                          struct tt_global_entry *tt_global_entry,
+                          const char *message);
+static void tt_purge(struct work_struct *work);
 
 /* returns 1 if they are the same mac addr */
-static int compare_ltt(struct hlist_node *node, void *data2)
+static int compare_ltt(const struct hlist_node *node, const void *data2)
 {
-       void *data1 = container_of(node, struct tt_local_entry, hash_entry);
+       const void *data1 = container_of(node, struct tt_local_entry,
+                                        hash_entry);
 
        return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
 }
 
 /* returns 1 if they are the same mac addr */
-static int compare_gtt(struct hlist_node *node, void *data2)
+static int compare_gtt(const struct hlist_node *node, const void *data2)
 {
-       void *data1 = container_of(node, struct tt_global_entry, hash_entry);
+       const void *data1 = container_of(node, struct tt_global_entry,
+                                        hash_entry);
 
        return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
 }
 
-static void tt_local_start_timer(struct bat_priv *bat_priv)
+static void tt_start_timer(struct bat_priv *bat_priv)
 {
-       INIT_DELAYED_WORK(&bat_priv->tt_work, tt_local_purge);
-       queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, 10 * HZ);
+       INIT_DELAYED_WORK(&bat_priv->tt_work, tt_purge);
+       queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work,
+                          msecs_to_jiffies(5000));
 }
 
 static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,
-                                                  void *data)
+                                                const void *data)
 {
        struct hashtable_t *hash = bat_priv->tt_local_hash;
        struct hlist_head *head;
@@ -73,6 +80,9 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,
                if (!compare_eth(tt_local_entry, data))
                        continue;
 
+               if (!atomic_inc_not_zero(&tt_local_entry->refcount))
+                       continue;
+
                tt_local_entry_tmp = tt_local_entry;
                break;
        }
@@ -82,7 +92,7 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,
 }
 
 static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv,
-                                                    void *data)
+                                                  const void *data)
 {
        struct hashtable_t *hash = bat_priv->tt_global_hash;
        struct hlist_head *head;
@@ -102,6 +112,9 @@ static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv,
                if (!compare_eth(tt_global_entry, data))
                        continue;
 
+               if (!atomic_inc_not_zero(&tt_global_entry->refcount))
+                       continue;
+
                tt_global_entry_tmp = tt_global_entry;
                break;
        }
@@ -110,7 +123,54 @@ static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv,
        return tt_global_entry_tmp;
 }
 
-int tt_local_init(struct bat_priv *bat_priv)
+static bool is_out_of_time(unsigned long starting_time, unsigned long timeout)
+{
+       unsigned long deadline;
+       deadline = starting_time + msecs_to_jiffies(timeout);
+
+       return time_after(jiffies, deadline);
+}
+
+static void tt_local_entry_free_ref(struct tt_local_entry *tt_local_entry)
+{
+       if (atomic_dec_and_test(&tt_local_entry->refcount))
+               kfree_rcu(tt_local_entry, rcu);
+}
+
+static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry)
+{
+       if (atomic_dec_and_test(&tt_global_entry->refcount))
+               kfree_rcu(tt_global_entry, rcu);
+}
+
+static void tt_local_event(struct bat_priv *bat_priv, const uint8_t *addr,
+                          uint8_t flags)
+{
+       struct tt_change_node *tt_change_node;
+
+       tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC);
+
+       if (!tt_change_node)
+               return;
+
+       tt_change_node->change.flags = flags;
+       memcpy(tt_change_node->change.addr, addr, ETH_ALEN);
+
+       spin_lock_bh(&bat_priv->tt_changes_list_lock);
+       /* track the change in the OGMinterval list */
+       list_add_tail(&tt_change_node->list, &bat_priv->tt_changes_list);
+       atomic_inc(&bat_priv->tt_local_changes);
+       spin_unlock_bh(&bat_priv->tt_changes_list_lock);
+
+       atomic_set(&bat_priv->tt_ogm_append_cnt, 0);
+}
+
+int tt_len(int changes_num)
+{
+       return changes_num * sizeof(struct tt_change);
+}
+
+static int tt_local_init(struct bat_priv *bat_priv)
 {
        if (bat_priv->tt_local_hash)
                return 1;
@@ -120,116 +180,114 @@ int tt_local_init(struct bat_priv *bat_priv)
        if (!bat_priv->tt_local_hash)
                return 0;
 
-       atomic_set(&bat_priv->tt_local_changed, 0);
-       tt_local_start_timer(bat_priv);
-
        return 1;
 }
 
-void tt_local_add(struct net_device *soft_iface, uint8_t *addr)
+void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
 {
        struct bat_priv *bat_priv = netdev_priv(soft_iface);
-       struct tt_local_entry *tt_local_entry;
-       struct tt_global_entry *tt_global_entry;
-       int required_bytes;
+       struct tt_local_entry *tt_local_entry = NULL;
+       struct tt_global_entry *tt_global_entry = NULL;
 
-       spin_lock_bh(&bat_priv->tt_lhash_lock);
        tt_local_entry = tt_local_hash_find(bat_priv, addr);
-       spin_unlock_bh(&bat_priv->tt_lhash_lock);
 
        if (tt_local_entry) {
                tt_local_entry->last_seen = jiffies;
-               return;
-       }
-
-       /* only announce as many hosts as possible in the batman-packet and
-          space in batman_packet->num_tt That also should give a limit to
-          MAC-flooding. */
-       required_bytes = (bat_priv->num_local_tt + 1) * ETH_ALEN;
-       required_bytes += BAT_PACKET_LEN;
-
-       if ((required_bytes > ETH_DATA_LEN) ||
-           (atomic_read(&bat_priv->aggregated_ogms) &&
-            required_bytes > MAX_AGGREGATION_BYTES) ||
-           (bat_priv->num_local_tt + 1 > 255)) {
-               bat_dbg(DBG_ROUTES, bat_priv,
-                       "Can't add new local tt entry (%pM): "
-                       "number of local tt entries exceeds packet size\n",
-                       addr);
-               return;
+               goto out;
        }
 
-       bat_dbg(DBG_ROUTES, bat_priv,
-               "Creating new local tt entry: %pM\n", addr);
-
-       tt_local_entry = kmalloc(sizeof(struct tt_local_entry), GFP_ATOMIC);
+       tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC);
        if (!tt_local_entry)
-               return;
+               goto out;
+
+       bat_dbg(DBG_TT, bat_priv,
+               "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
+               (uint8_t)atomic_read(&bat_priv->ttvn));
 
        memcpy(tt_local_entry->addr, addr, ETH_ALEN);
        tt_local_entry->last_seen = jiffies;
+       tt_local_entry->flags = NO_FLAGS;
+       atomic_set(&tt_local_entry->refcount, 2);
 
        /* the batman interface mac address should never be purged */
        if (compare_eth(addr, soft_iface->dev_addr))
-               tt_local_entry->never_purge = 1;
-       else
-               tt_local_entry->never_purge = 0;
+               tt_local_entry->flags |= TT_CLIENT_NOPURGE;
 
-       spin_lock_bh(&bat_priv->tt_lhash_lock);
+       tt_local_event(bat_priv, addr, tt_local_entry->flags);
+
+       /* The local entry has to be marked as NEW to avoid to send it in
+        * a full table response going out before the next ttvn increment
+        * (consistency check) */
+       tt_local_entry->flags |= TT_CLIENT_NEW;
 
        hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig,
                 tt_local_entry, &tt_local_entry->hash_entry);
-       bat_priv->num_local_tt++;
-       atomic_set(&bat_priv->tt_local_changed, 1);
-
-       spin_unlock_bh(&bat_priv->tt_lhash_lock);
 
        /* remove address from global hash if present */
-       spin_lock_bh(&bat_priv->tt_ghash_lock);
-
        tt_global_entry = tt_global_hash_find(bat_priv, addr);
 
+       /* Check whether it is a roaming! */
+       if (tt_global_entry) {
+               /* This node is probably going to update its tt table */
+               tt_global_entry->orig_node->tt_poss_change = true;
+               /* The global entry has to be marked as PENDING and has to be
+                * kept for consistency purpose */
+               tt_global_entry->flags |= TT_CLIENT_PENDING;
+               send_roam_adv(bat_priv, tt_global_entry->addr,
+                             tt_global_entry->orig_node);
+       }
+out:
+       if (tt_local_entry)
+               tt_local_entry_free_ref(tt_local_entry);
        if (tt_global_entry)
-               _tt_global_del_orig(bat_priv, tt_global_entry,
-                                    "local tt received");
-
-       spin_unlock_bh(&bat_priv->tt_ghash_lock);
+               tt_global_entry_free_ref(tt_global_entry);
 }
 
-int tt_local_fill_buffer(struct bat_priv *bat_priv,
-                         unsigned char *buff, int buff_len)
+int tt_changes_fill_buffer(struct bat_priv *bat_priv,
+                          unsigned char *buff, int buff_len)
 {
-       struct hashtable_t *hash = bat_priv->tt_local_hash;
-       struct tt_local_entry *tt_local_entry;
-       struct hlist_node *node;
-       struct hlist_head *head;
-       int i, count = 0;
-
-       spin_lock_bh(&bat_priv->tt_lhash_lock);
+       int count = 0, tot_changes = 0;
+       struct tt_change_node *entry, *safe;
 
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(tt_local_entry, node,
-                                        head, hash_entry) {
-                       if (buff_len < (count + 1) * ETH_ALEN)
-                               break;
+       if (buff_len > 0)
+               tot_changes = buff_len / tt_len(1);
 
-                       memcpy(buff + (count * ETH_ALEN), tt_local_entry->addr,
-                              ETH_ALEN);
+       spin_lock_bh(&bat_priv->tt_changes_list_lock);
+       atomic_set(&bat_priv->tt_local_changes, 0);
 
+       list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list,
+                       list) {
+               if (count < tot_changes) {
+                       memcpy(buff + tt_len(count),
+                              &entry->change, sizeof(struct tt_change));
                        count++;
                }
-               rcu_read_unlock();
+               list_del(&entry->list);
+               kfree(entry);
        }
+       spin_unlock_bh(&bat_priv->tt_changes_list_lock);
+
+       /* Keep the buffer for possible tt_request */
+       spin_lock_bh(&bat_priv->tt_buff_lock);
+       kfree(bat_priv->tt_buff);
+       bat_priv->tt_buff_len = 0;
+       bat_priv->tt_buff = NULL;
+       /* We check whether this new OGM has no changes due to size
+        * problems */
+       if (buff_len > 0) {
+               /**
+                * if kmalloc() fails we will reply with the full table
+                * instead of providing the diff
+                */
+               bat_priv->tt_buff = kmalloc(buff_len, GFP_ATOMIC);
+               if (bat_priv->tt_buff) {
+                       memcpy(bat_priv->tt_buff, buff, buff_len);
+                       bat_priv->tt_buff_len = buff_len;
+               }
+       }
+       spin_unlock_bh(&bat_priv->tt_buff_lock);
 
-       /* if we did not get all new local tts see you next time  ;-) */
-       if (count == bat_priv->num_local_tt)
-               atomic_set(&bat_priv->tt_local_changed, 0);
-
-       spin_unlock_bh(&bat_priv->tt_lhash_lock);
-       return count;
+       return tot_changes;
 }
 
 int tt_local_seq_print_text(struct seq_file *seq, void *offset)
@@ -261,10 +319,8 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
        }
 
        seq_printf(seq, "Locally retrieved addresses (from %s) "
-                  "announced via TT:\n",
-                  net_dev->name);
-
-       spin_lock_bh(&bat_priv->tt_lhash_lock);
+                  "announced via TT (TTVN: %u):\n",
+                  net_dev->name, (uint8_t)atomic_read(&bat_priv->ttvn));
 
        buf_size = 1;
        /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */
@@ -279,7 +335,6 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
 
        buff = kmalloc(buf_size, GFP_ATOMIC);
        if (!buff) {
-               spin_unlock_bh(&bat_priv->tt_lhash_lock);
                ret = -ENOMEM;
                goto out;
        }
@@ -299,8 +354,6 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
                rcu_read_unlock();
        }
 
-       spin_unlock_bh(&bat_priv->tt_lhash_lock);
-
        seq_printf(seq, "%s", buff);
        kfree(buff);
 out:
@@ -309,92 +362,109 @@ out:
        return ret;
 }
 
-static void _tt_local_del(struct hlist_node *node, void *arg)
-{
-       struct bat_priv *bat_priv = (struct bat_priv *)arg;
-       void *data = container_of(node, struct tt_local_entry, hash_entry);
-
-       kfree(data);
-       bat_priv->num_local_tt--;
-       atomic_set(&bat_priv->tt_local_changed, 1);
-}
-
-static void tt_local_del(struct bat_priv *bat_priv,
-                         struct tt_local_entry *tt_local_entry,
-                         char *message)
+static void tt_local_set_pending(struct bat_priv *bat_priv,
+                                struct tt_local_entry *tt_local_entry,
+                                uint16_t flags)
 {
-       bat_dbg(DBG_ROUTES, bat_priv, "Deleting local tt entry (%pM): %s\n",
-               tt_local_entry->addr, message);
+       tt_local_event(bat_priv, tt_local_entry->addr,
+                      tt_local_entry->flags | flags);
 
-       hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig,
-                   tt_local_entry->addr);
-       _tt_local_del(&tt_local_entry->hash_entry, bat_priv);
+       /* The local client has to be merked as "pending to be removed" but has
+        * to be kept in the table in order to send it in an full tables
+        * response issued before the net ttvn increment (consistency check) */
+       tt_local_entry->flags |= TT_CLIENT_PENDING;
 }
 
-void tt_local_remove(struct bat_priv *bat_priv,
-                     uint8_t *addr, char *message)
+void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
+                    const char *message, bool roaming)
 {
-       struct tt_local_entry *tt_local_entry;
-
-       spin_lock_bh(&bat_priv->tt_lhash_lock);
+       struct tt_local_entry *tt_local_entry = NULL;
 
        tt_local_entry = tt_local_hash_find(bat_priv, addr);
+       if (!tt_local_entry)
+               goto out;
 
-       if (tt_local_entry)
-               tt_local_del(bat_priv, tt_local_entry, message);
+       tt_local_set_pending(bat_priv, tt_local_entry, TT_CLIENT_DEL |
+                            (roaming ? TT_CLIENT_ROAM : NO_FLAGS));
 
-       spin_unlock_bh(&bat_priv->tt_lhash_lock);
+       bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) pending to be removed: "
+               "%s\n", tt_local_entry->addr, message);
+out:
+       if (tt_local_entry)
+               tt_local_entry_free_ref(tt_local_entry);
 }
 
-static void tt_local_purge(struct work_struct *work)
+static void tt_local_purge(struct bat_priv *bat_priv)
 {
-       struct delayed_work *delayed_work =
-               container_of(work, struct delayed_work, work);
-       struct bat_priv *bat_priv =
-               container_of(delayed_work, struct bat_priv, tt_work);
        struct hashtable_t *hash = bat_priv->tt_local_hash;
        struct tt_local_entry *tt_local_entry;
        struct hlist_node *node, *node_tmp;
        struct hlist_head *head;
-       unsigned long timeout;
+       spinlock_t *list_lock; /* protects write access to the hash lists */
        int i;
 
-       spin_lock_bh(&bat_priv->tt_lhash_lock);
-
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
+               list_lock = &hash->list_locks[i];
 
+               spin_lock_bh(list_lock);
                hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
                                          head, hash_entry) {
-                       if (tt_local_entry->never_purge)
+                       if (tt_local_entry->flags & TT_CLIENT_NOPURGE)
                                continue;
 
-                       timeout = tt_local_entry->last_seen;
-                       timeout += TT_LOCAL_TIMEOUT * HZ;
+                       /* entry already marked for deletion */
+                       if (tt_local_entry->flags & TT_CLIENT_PENDING)
+                               continue;
 
-                       if (time_before(jiffies, timeout))
+                       if (!is_out_of_time(tt_local_entry->last_seen,
+                                           TT_LOCAL_TIMEOUT * 1000))
                                continue;
 
-                       tt_local_del(bat_priv, tt_local_entry,
-                                     "address timed out");
+                       tt_local_set_pending(bat_priv, tt_local_entry,
+                                            TT_CLIENT_DEL);
+                       bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) "
+                               "pending to be removed: timed out\n",
+                               tt_local_entry->addr);
                }
+               spin_unlock_bh(list_lock);
        }
 
-       spin_unlock_bh(&bat_priv->tt_lhash_lock);
-       tt_local_start_timer(bat_priv);
 }
 
-void tt_local_free(struct bat_priv *bat_priv)
+static void tt_local_table_free(struct bat_priv *bat_priv)
 {
+       struct hashtable_t *hash;
+       spinlock_t *list_lock; /* protects write access to the hash lists */
+       struct tt_local_entry *tt_local_entry;
+       struct hlist_node *node, *node_tmp;
+       struct hlist_head *head;
+       int i;
+
        if (!bat_priv->tt_local_hash)
                return;
 
-       cancel_delayed_work_sync(&bat_priv->tt_work);
-       hash_delete(bat_priv->tt_local_hash, _tt_local_del, bat_priv);
+       hash = bat_priv->tt_local_hash;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+               list_lock = &hash->list_locks[i];
+
+               spin_lock_bh(list_lock);
+               hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
+                                         head, hash_entry) {
+                       hlist_del_rcu(node);
+                       tt_local_entry_free_ref(tt_local_entry);
+               }
+               spin_unlock_bh(list_lock);
+       }
+
+       hash_destroy(hash);
+
        bat_priv->tt_local_hash = NULL;
 }
 
-int tt_global_init(struct bat_priv *bat_priv)
+static int tt_global_init(struct bat_priv *bat_priv)
 {
        if (bat_priv->tt_global_hash)
                return 1;
@@ -407,74 +477,78 @@ int tt_global_init(struct bat_priv *bat_priv)
        return 1;
 }
 
-void tt_global_add_orig(struct bat_priv *bat_priv,
-                        struct orig_node *orig_node,
-                        unsigned char *tt_buff, int tt_buff_len)
+static void tt_changes_list_free(struct bat_priv *bat_priv)
 {
-       struct tt_global_entry *tt_global_entry;
-       struct tt_local_entry *tt_local_entry;
-       int tt_buff_count = 0;
-       unsigned char *tt_ptr;
-
-       while ((tt_buff_count + 1) * ETH_ALEN <= tt_buff_len) {
-               spin_lock_bh(&bat_priv->tt_ghash_lock);
-
-               tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN);
-               tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr);
-
-               if (!tt_global_entry) {
-                       spin_unlock_bh(&bat_priv->tt_ghash_lock);
+       struct tt_change_node *entry, *safe;
 
-                       tt_global_entry =
-                               kmalloc(sizeof(struct tt_global_entry),
-                                       GFP_ATOMIC);
+       spin_lock_bh(&bat_priv->tt_changes_list_lock);
 
-                       if (!tt_global_entry)
-                               break;
+       list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list,
+                                list) {
+               list_del(&entry->list);
+               kfree(entry);
+       }
 
-                       memcpy(tt_global_entry->addr, tt_ptr, ETH_ALEN);
+       atomic_set(&bat_priv->tt_local_changes, 0);
+       spin_unlock_bh(&bat_priv->tt_changes_list_lock);
+}
 
-                       bat_dbg(DBG_ROUTES, bat_priv,
-                               "Creating new global tt entry: "
-                               "%pM (via %pM)\n",
-                               tt_global_entry->addr, orig_node->orig);
+/* caller must hold orig_node refcount */
+int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
+                 const unsigned char *tt_addr, uint8_t ttvn, bool roaming)
+{
+       struct tt_global_entry *tt_global_entry;
+       struct orig_node *orig_node_tmp;
+       int ret = 0;
 
-                       spin_lock_bh(&bat_priv->tt_ghash_lock);
-                       hash_add(bat_priv->tt_global_hash, compare_gtt,
-                                choose_orig, tt_global_entry,
-                                &tt_global_entry->hash_entry);
+       tt_global_entry = tt_global_hash_find(bat_priv, tt_addr);
 
-               }
+       if (!tt_global_entry) {
+               tt_global_entry =
+                       kmalloc(sizeof(*tt_global_entry),
+                               GFP_ATOMIC);
+               if (!tt_global_entry)
+                       goto out;
 
+               memcpy(tt_global_entry->addr, tt_addr, ETH_ALEN);
+               /* Assign the new orig_node */
+               atomic_inc(&orig_node->refcount);
                tt_global_entry->orig_node = orig_node;
-               spin_unlock_bh(&bat_priv->tt_ghash_lock);
-
-               /* remove address from local hash if present */
-               spin_lock_bh(&bat_priv->tt_lhash_lock);
-
-               tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN);
-               tt_local_entry = tt_local_hash_find(bat_priv, tt_ptr);
-
-               if (tt_local_entry)
-                       tt_local_del(bat_priv, tt_local_entry,
-                                     "global tt received");
-
-               spin_unlock_bh(&bat_priv->tt_lhash_lock);
-
-               tt_buff_count++;
+               tt_global_entry->ttvn = ttvn;
+               tt_global_entry->flags = NO_FLAGS;
+               tt_global_entry->roam_at = 0;
+               atomic_set(&tt_global_entry->refcount, 2);
+
+               hash_add(bat_priv->tt_global_hash, compare_gtt,
+                        choose_orig, tt_global_entry,
+                        &tt_global_entry->hash_entry);
+               atomic_inc(&orig_node->tt_size);
+       } else {
+               if (tt_global_entry->orig_node != orig_node) {
+                       atomic_dec(&tt_global_entry->orig_node->tt_size);
+                       orig_node_tmp = tt_global_entry->orig_node;
+                       atomic_inc(&orig_node->refcount);
+                       tt_global_entry->orig_node = orig_node;
+                       orig_node_free_ref(orig_node_tmp);
+                       atomic_inc(&orig_node->tt_size);
+               }
+               tt_global_entry->ttvn = ttvn;
+               tt_global_entry->flags = NO_FLAGS;
+               tt_global_entry->roam_at = 0;
        }
 
-       /* initialize, and overwrite if malloc succeeds */
-       orig_node->tt_buff = NULL;
-       orig_node->tt_buff_len = 0;
+       bat_dbg(DBG_TT, bat_priv,
+               "Creating new global tt entry: %pM (via %pM)\n",
+               tt_global_entry->addr, orig_node->orig);
 
-       if (tt_buff_len > 0) {
-               orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);
-               if (orig_node->tt_buff) {
-                       memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);
-                       orig_node->tt_buff_len = tt_buff_len;
-               }
-       }
+       /* remove address from local hash if present */
+       tt_local_remove(bat_priv, tt_global_entry->addr,
+                       "global tt received", roaming);
+       ret = 1;
+out:
+       if (tt_global_entry)
+               tt_global_entry_free_ref(tt_global_entry);
+       return ret;
 }
 
 int tt_global_seq_print_text(struct seq_file *seq, void *offset)
@@ -508,26 +582,27 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
        seq_printf(seq,
                   "Globally announced TT entries received via the mesh %s\n",
                   net_dev->name);
-
-       spin_lock_bh(&bat_priv->tt_ghash_lock);
+       seq_printf(seq, "       %-13s %s       %-15s %s\n",
+                  "Client", "(TTVN)", "Originator", "(Curr TTVN)");
 
        buf_size = 1;
-       /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/
+       /* Estimate length for: " * xx:xx:xx:xx:xx:xx (ttvn) via
+        * xx:xx:xx:xx:xx:xx (cur_ttvn)\n"*/
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
 
                rcu_read_lock();
                __hlist_for_each_rcu(node, head)
-                       buf_size += 43;
+                       buf_size += 59;
                rcu_read_unlock();
        }
 
        buff = kmalloc(buf_size, GFP_ATOMIC);
        if (!buff) {
-               spin_unlock_bh(&bat_priv->tt_ghash_lock);
                ret = -ENOMEM;
                goto out;
        }
+
        buff[0] = '\0';
        pos = 0;
 
@@ -537,16 +612,18 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
                rcu_read_lock();
                hlist_for_each_entry_rcu(tt_global_entry, node,
                                         head, hash_entry) {
-                       pos += snprintf(buff + pos, 44,
-                                       " * %pM via %pM\n",
+                       pos += snprintf(buff + pos, 61,
+                                       " * %pM  (%3u) via %pM     (%3u)\n",
                                        tt_global_entry->addr,
-                                       tt_global_entry->orig_node->orig);
+                                       tt_global_entry->ttvn,
+                                       tt_global_entry->orig_node->orig,
+                                       (uint8_t) atomic_read(
+                                               &tt_global_entry->orig_node->
+                                               last_ttvn));
                }
                rcu_read_unlock();
        }
 
-       spin_unlock_bh(&bat_priv->tt_ghash_lock);
-
        seq_printf(seq, "%s", buff);
        kfree(buff);
 out:
@@ -555,84 +632,1091 @@ out:
        return ret;
 }
 
-static void _tt_global_del_orig(struct bat_priv *bat_priv,
-                                struct tt_global_entry *tt_global_entry,
-                                char *message)
+static void _tt_global_del(struct bat_priv *bat_priv,
+                          struct tt_global_entry *tt_global_entry,
+                          const char *message)
 {
-       bat_dbg(DBG_ROUTES, bat_priv,
+       if (!tt_global_entry)
+               goto out;
+
+       bat_dbg(DBG_TT, bat_priv,
                "Deleting global tt entry %pM (via %pM): %s\n",
                tt_global_entry->addr, tt_global_entry->orig_node->orig,
                message);
 
+       atomic_dec(&tt_global_entry->orig_node->tt_size);
+
        hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig,
                    tt_global_entry->addr);
-       kfree(tt_global_entry);
+out:
+       if (tt_global_entry)
+               tt_global_entry_free_ref(tt_global_entry);
 }
 
-void tt_global_del_orig(struct bat_priv *bat_priv,
-                        struct orig_node *orig_node, char *message)
+void tt_global_del(struct bat_priv *bat_priv,
+                  struct orig_node *orig_node, const unsigned char *addr,
+                  const char *message, bool roaming)
 {
-       struct tt_global_entry *tt_global_entry;
-       int tt_buff_count = 0;
-       unsigned char *tt_ptr;
+       struct tt_global_entry *tt_global_entry = NULL;
 
-       if (orig_node->tt_buff_len == 0)
-               return;
+       tt_global_entry = tt_global_hash_find(bat_priv, addr);
+       if (!tt_global_entry)
+               goto out;
 
-       spin_lock_bh(&bat_priv->tt_ghash_lock);
+       if (tt_global_entry->orig_node == orig_node) {
+               if (roaming) {
+                       tt_global_entry->flags |= TT_CLIENT_ROAM;
+                       tt_global_entry->roam_at = jiffies;
+                       goto out;
+               }
+               _tt_global_del(bat_priv, tt_global_entry, message);
+       }
+out:
+       if (tt_global_entry)
+               tt_global_entry_free_ref(tt_global_entry);
+}
 
-       while ((tt_buff_count + 1) * ETH_ALEN <= orig_node->tt_buff_len) {
-               tt_ptr = orig_node->tt_buff + (tt_buff_count * ETH_ALEN);
-               tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr);
+void tt_global_del_orig(struct bat_priv *bat_priv,
+                       struct orig_node *orig_node, const char *message)
+{
+       struct tt_global_entry *tt_global_entry;
+       int i;
+       struct hashtable_t *hash = bat_priv->tt_global_hash;
+       struct hlist_node *node, *safe;
+       struct hlist_head *head;
+       spinlock_t *list_lock; /* protects write access to the hash lists */
 
-               if ((tt_global_entry) &&
-                   (tt_global_entry->orig_node == orig_node))
-                       _tt_global_del_orig(bat_priv, tt_global_entry,
-                                            message);
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+               list_lock = &hash->list_locks[i];
 
-               tt_buff_count++;
+               spin_lock_bh(list_lock);
+               hlist_for_each_entry_safe(tt_global_entry, node, safe,
+                                        head, hash_entry) {
+                       if (tt_global_entry->orig_node == orig_node) {
+                               bat_dbg(DBG_TT, bat_priv,
+                                       "Deleting global tt entry %pM "
+                                       "(via %pM): originator time out\n",
+                                       tt_global_entry->addr,
+                                       tt_global_entry->orig_node->orig);
+                               hlist_del_rcu(node);
+                               tt_global_entry_free_ref(tt_global_entry);
+                       }
+               }
+               spin_unlock_bh(list_lock);
        }
-
-       spin_unlock_bh(&bat_priv->tt_ghash_lock);
-
-       orig_node->tt_buff_len = 0;
-       kfree(orig_node->tt_buff);
-       orig_node->tt_buff = NULL;
+       atomic_set(&orig_node->tt_size, 0);
 }
 
-static void tt_global_del(struct hlist_node *node, void *arg)
+static void tt_global_roam_purge(struct bat_priv *bat_priv)
 {
-       void *data = container_of(node, struct tt_global_entry, hash_entry);
+       struct hashtable_t *hash = bat_priv->tt_global_hash;
+       struct tt_global_entry *tt_global_entry;
+       struct hlist_node *node, *node_tmp;
+       struct hlist_head *head;
+       spinlock_t *list_lock; /* protects write access to the hash lists */
+       int i;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+               list_lock = &hash->list_locks[i];
+
+               spin_lock_bh(list_lock);
+               hlist_for_each_entry_safe(tt_global_entry, node, node_tmp,
+                                         head, hash_entry) {
+                       if (!(tt_global_entry->flags & TT_CLIENT_ROAM))
+                               continue;
+                       if (!is_out_of_time(tt_global_entry->roam_at,
+                                           TT_CLIENT_ROAM_TIMEOUT * 1000))
+                               continue;
+
+                       bat_dbg(DBG_TT, bat_priv, "Deleting global "
+                               "tt entry (%pM): Roaming timeout\n",
+                               tt_global_entry->addr);
+                       atomic_dec(&tt_global_entry->orig_node->tt_size);
+                       hlist_del_rcu(node);
+                       tt_global_entry_free_ref(tt_global_entry);
+               }
+               spin_unlock_bh(list_lock);
+       }
 
-       kfree(data);
 }
 
-void tt_global_free(struct bat_priv *bat_priv)
+static void tt_global_table_free(struct bat_priv *bat_priv)
 {
+       struct hashtable_t *hash;
+       spinlock_t *list_lock; /* protects write access to the hash lists */
+       struct tt_global_entry *tt_global_entry;
+       struct hlist_node *node, *node_tmp;
+       struct hlist_head *head;
+       int i;
+
        if (!bat_priv->tt_global_hash)
                return;
 
-       hash_delete(bat_priv->tt_global_hash, tt_global_del, NULL);
+       hash = bat_priv->tt_global_hash;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+               list_lock = &hash->list_locks[i];
+
+               spin_lock_bh(list_lock);
+               hlist_for_each_entry_safe(tt_global_entry, node, node_tmp,
+                                         head, hash_entry) {
+                       hlist_del_rcu(node);
+                       tt_global_entry_free_ref(tt_global_entry);
+               }
+               spin_unlock_bh(list_lock);
+       }
+
+       hash_destroy(hash);
+
        bat_priv->tt_global_hash = NULL;
 }
 
-struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr)
+struct orig_node *transtable_search(struct bat_priv *bat_priv,
+                                   const uint8_t *addr)
 {
        struct tt_global_entry *tt_global_entry;
        struct orig_node *orig_node = NULL;
 
-       spin_lock_bh(&bat_priv->tt_ghash_lock);
        tt_global_entry = tt_global_hash_find(bat_priv, addr);
 
        if (!tt_global_entry)
                goto out;
 
        if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount))
-               goto out;
+               goto free_tt;
+
+       /* A global client marked as PENDING has already moved from that
+        * originator */
+       if (tt_global_entry->flags & TT_CLIENT_PENDING)
+               goto free_tt;
 
        orig_node = tt_global_entry->orig_node;
 
+free_tt:
+       tt_global_entry_free_ref(tt_global_entry);
 out:
-       spin_unlock_bh(&bat_priv->tt_ghash_lock);
        return orig_node;
 }
+
+/* Calculates the checksum of the local table of a given orig_node */
+uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
+{
+       uint16_t total = 0, total_one;
+       struct hashtable_t *hash = bat_priv->tt_global_hash;
+       struct tt_global_entry *tt_global_entry;
+       struct hlist_node *node;
+       struct hlist_head *head;
+       int i, j;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(tt_global_entry, node,
+                                        head, hash_entry) {
+                       if (compare_eth(tt_global_entry->orig_node,
+                                       orig_node)) {
+                               /* Roaming clients are in the global table for
+                                * consistency only. They don't have to be
+                                * taken into account while computing the
+                                * global crc */
+                               if (tt_global_entry->flags & TT_CLIENT_ROAM)
+                                       continue;
+                               total_one = 0;
+                               for (j = 0; j < ETH_ALEN; j++)
+                                       total_one = crc16_byte(total_one,
+                                               tt_global_entry->addr[j]);
+                               total ^= total_one;
+                       }
+               }
+               rcu_read_unlock();
+       }
+
+       return total;
+}
+
+/* Calculates the checksum of the local table */
+uint16_t tt_local_crc(struct bat_priv *bat_priv)
+{
+       uint16_t total = 0, total_one;
+       struct hashtable_t *hash = bat_priv->tt_local_hash;
+       struct tt_local_entry *tt_local_entry;
+       struct hlist_node *node;
+       struct hlist_head *head;
+       int i, j;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(tt_local_entry, node,
+                                        head, hash_entry) {
+                       /* not yet committed clients have not to be taken into
+                        * account while computing the CRC */
+                       if (tt_local_entry->flags & TT_CLIENT_NEW)
+                               continue;
+                       total_one = 0;
+                       for (j = 0; j < ETH_ALEN; j++)
+                               total_one = crc16_byte(total_one,
+                                                  tt_local_entry->addr[j]);
+                       total ^= total_one;
+               }
+               rcu_read_unlock();
+       }
+
+       return total;
+}
+
+static void tt_req_list_free(struct bat_priv *bat_priv)
+{
+       struct tt_req_node *node, *safe;
+
+       spin_lock_bh(&bat_priv->tt_req_list_lock);
+
+       list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) {
+               list_del(&node->list);
+               kfree(node);
+       }
+
+       spin_unlock_bh(&bat_priv->tt_req_list_lock);
+}
+
+void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node,
+                        const unsigned char *tt_buff, uint8_t tt_num_changes)
+{
+       uint16_t tt_buff_len = tt_len(tt_num_changes);
+
+       /* Replace the old buffer only if I received something in the
+        * last OGM (the OGM could carry no changes) */
+       spin_lock_bh(&orig_node->tt_buff_lock);
+       if (tt_buff_len > 0) {
+               kfree(orig_node->tt_buff);
+               orig_node->tt_buff_len = 0;
+               orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);
+               if (orig_node->tt_buff) {
+                       memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);
+                       orig_node->tt_buff_len = tt_buff_len;
+               }
+       }
+       spin_unlock_bh(&orig_node->tt_buff_lock);
+}
+
+static void tt_req_purge(struct bat_priv *bat_priv)
+{
+       struct tt_req_node *node, *safe;
+
+       spin_lock_bh(&bat_priv->tt_req_list_lock);
+       list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) {
+               if (is_out_of_time(node->issued_at,
+                   TT_REQUEST_TIMEOUT * 1000)) {
+                       list_del(&node->list);
+                       kfree(node);
+               }
+       }
+       spin_unlock_bh(&bat_priv->tt_req_list_lock);
+}
+
+/* returns the pointer to the new tt_req_node struct if no request
+ * has already been issued for this orig_node, NULL otherwise */
+static struct tt_req_node *new_tt_req_node(struct bat_priv *bat_priv,
+                                         struct orig_node *orig_node)
+{
+       struct tt_req_node *tt_req_node_tmp, *tt_req_node = NULL;
+
+       spin_lock_bh(&bat_priv->tt_req_list_lock);
+       list_for_each_entry(tt_req_node_tmp, &bat_priv->tt_req_list, list) {
+               if (compare_eth(tt_req_node_tmp, orig_node) &&
+                   !is_out_of_time(tt_req_node_tmp->issued_at,
+                                   TT_REQUEST_TIMEOUT * 1000))
+                       goto unlock;
+       }
+
+       tt_req_node = kmalloc(sizeof(*tt_req_node), GFP_ATOMIC);
+       if (!tt_req_node)
+               goto unlock;
+
+       memcpy(tt_req_node->addr, orig_node->orig, ETH_ALEN);
+       tt_req_node->issued_at = jiffies;
+
+       list_add(&tt_req_node->list, &bat_priv->tt_req_list);
+unlock:
+       spin_unlock_bh(&bat_priv->tt_req_list_lock);
+       return tt_req_node;
+}
+
+/* data_ptr is useless here, but has to be kept to respect the prototype */
+static int tt_local_valid_entry(const void *entry_ptr, const void *data_ptr)
+{
+       const struct tt_local_entry *tt_local_entry = entry_ptr;
+
+       if (tt_local_entry->flags & TT_CLIENT_NEW)
+               return 0;
+       return 1;
+}
+
+static int tt_global_valid_entry(const void *entry_ptr, const void *data_ptr)
+{
+       const struct tt_global_entry *tt_global_entry = entry_ptr;
+       const struct orig_node *orig_node = data_ptr;
+
+       if (tt_global_entry->flags & TT_CLIENT_ROAM)
+               return 0;
+
+       return (tt_global_entry->orig_node == orig_node);
+}
+
+static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
+                                             struct hashtable_t *hash,
+                                             struct hard_iface *primary_if,
+                                             int (*valid_cb)(const void *,
+                                                             const void *),
+                                             void *cb_data)
+{
+       struct tt_local_entry *tt_local_entry;
+       struct tt_query_packet *tt_response;
+       struct tt_change *tt_change;
+       struct hlist_node *node;
+       struct hlist_head *head;
+       struct sk_buff *skb = NULL;
+       uint16_t tt_tot, tt_count;
+       ssize_t tt_query_size = sizeof(struct tt_query_packet);
+       int i;
+
+       if (tt_query_size + tt_len > primary_if->soft_iface->mtu) {
+               tt_len = primary_if->soft_iface->mtu - tt_query_size;
+               tt_len -= tt_len % sizeof(struct tt_change);
+       }
+       tt_tot = tt_len / sizeof(struct tt_change);
+
+       skb = dev_alloc_skb(tt_query_size + tt_len + ETH_HLEN);
+       if (!skb)
+               goto out;
+
+       skb_reserve(skb, ETH_HLEN);
+       tt_response = (struct tt_query_packet *)skb_put(skb,
+                                                    tt_query_size + tt_len);
+       tt_response->ttvn = ttvn;
+       tt_response->tt_data = htons(tt_tot);
+
+       tt_change = (struct tt_change *)(skb->data + tt_query_size);
+       tt_count = 0;
+
+       rcu_read_lock();
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               hlist_for_each_entry_rcu(tt_local_entry, node,
+                                        head, hash_entry) {
+                       if (tt_count == tt_tot)
+                               break;
+
+                       if ((valid_cb) && (!valid_cb(tt_local_entry, cb_data)))
+                               continue;
+
+                       memcpy(tt_change->addr, tt_local_entry->addr, ETH_ALEN);
+                       tt_change->flags = NO_FLAGS;
+
+                       tt_count++;
+                       tt_change++;
+               }
+       }
+       rcu_read_unlock();
+
+out:
+       return skb;
+}
+
+int send_tt_request(struct bat_priv *bat_priv, struct orig_node *dst_orig_node,
+                   uint8_t ttvn, uint16_t tt_crc, bool full_table)
+{
+       struct sk_buff *skb = NULL;
+       struct tt_query_packet *tt_request;
+       struct neigh_node *neigh_node = NULL;
+       struct hard_iface *primary_if;
+       struct tt_req_node *tt_req_node = NULL;
+       int ret = 1;
+
+       primary_if = primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
+
+       /* The new tt_req will be issued only if I'm not waiting for a
+        * reply from the same orig_node yet */
+       tt_req_node = new_tt_req_node(bat_priv, dst_orig_node);
+       if (!tt_req_node)
+               goto out;
+
+       skb = dev_alloc_skb(sizeof(struct tt_query_packet) + ETH_HLEN);
+       if (!skb)
+               goto out;
+
+       skb_reserve(skb, ETH_HLEN);
+
+       tt_request = (struct tt_query_packet *)skb_put(skb,
+                               sizeof(struct tt_query_packet));
+
+       tt_request->packet_type = BAT_TT_QUERY;
+       tt_request->version = COMPAT_VERSION;
+       memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN);
+       memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN);
+       tt_request->ttl = TTL;
+       tt_request->ttvn = ttvn;
+       tt_request->tt_data = tt_crc;
+       tt_request->flags = TT_REQUEST;
+
+       if (full_table)
+               tt_request->flags |= TT_FULL_TABLE;
+
+       neigh_node = orig_node_get_router(dst_orig_node);
+       if (!neigh_node)
+               goto out;
+
+       bat_dbg(DBG_TT, bat_priv, "Sending TT_REQUEST to %pM via %pM "
+               "[%c]\n", dst_orig_node->orig, neigh_node->addr,
+               (full_table ? 'F' : '.'));
+
+       send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+       ret = 0;
+
+out:
+       if (neigh_node)
+               neigh_node_free_ref(neigh_node);
+       if (primary_if)
+               hardif_free_ref(primary_if);
+       if (ret)
+               kfree_skb(skb);
+       if (ret && tt_req_node) {
+               spin_lock_bh(&bat_priv->tt_req_list_lock);
+               list_del(&tt_req_node->list);
+               spin_unlock_bh(&bat_priv->tt_req_list_lock);
+               kfree(tt_req_node);
+       }
+       return ret;
+}
+
+static bool send_other_tt_response(struct bat_priv *bat_priv,
+                                  struct tt_query_packet *tt_request)
+{
+       struct orig_node *req_dst_orig_node = NULL, *res_dst_orig_node = NULL;
+       struct neigh_node *neigh_node = NULL;
+       struct hard_iface *primary_if = NULL;
+       uint8_t orig_ttvn, req_ttvn, ttvn;
+       int ret = false;
+       unsigned char *tt_buff;
+       bool full_table;
+       uint16_t tt_len, tt_tot;
+       struct sk_buff *skb = NULL;
+       struct tt_query_packet *tt_response;
+
+       bat_dbg(DBG_TT, bat_priv,
+               "Received TT_REQUEST from %pM for "
+               "ttvn: %u (%pM) [%c]\n", tt_request->src,
+               tt_request->ttvn, tt_request->dst,
+               (tt_request->flags & TT_FULL_TABLE ? 'F' : '.'));
+
+       /* Let's get the orig node of the REAL destination */
+       req_dst_orig_node = get_orig_node(bat_priv, tt_request->dst);
+       if (!req_dst_orig_node)
+               goto out;
+
+       res_dst_orig_node = get_orig_node(bat_priv, tt_request->src);
+       if (!res_dst_orig_node)
+               goto out;
+
+       neigh_node = orig_node_get_router(res_dst_orig_node);
+       if (!neigh_node)
+               goto out;
+
+       primary_if = primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
+
+       orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
+       req_ttvn = tt_request->ttvn;
+
+       /* I have not the requested data */
+       if (orig_ttvn != req_ttvn ||
+           tt_request->tt_data != req_dst_orig_node->tt_crc)
+               goto out;
+
+       /* If it has explicitly been requested the full table */
+       if (tt_request->flags & TT_FULL_TABLE ||
+           !req_dst_orig_node->tt_buff)
+               full_table = true;
+       else
+               full_table = false;
+
+       /* In this version, fragmentation is not implemented, then
+        * I'll send only one packet with as much TT entries as I can */
+       if (!full_table) {
+               spin_lock_bh(&req_dst_orig_node->tt_buff_lock);
+               tt_len = req_dst_orig_node->tt_buff_len;
+               tt_tot = tt_len / sizeof(struct tt_change);
+
+               skb = dev_alloc_skb(sizeof(struct tt_query_packet) +
+                                   tt_len + ETH_HLEN);
+               if (!skb)
+                       goto unlock;
+
+               skb_reserve(skb, ETH_HLEN);
+               tt_response = (struct tt_query_packet *)skb_put(skb,
+                               sizeof(struct tt_query_packet) + tt_len);
+               tt_response->ttvn = req_ttvn;
+               tt_response->tt_data = htons(tt_tot);
+
+               tt_buff = skb->data + sizeof(struct tt_query_packet);
+               /* Copy the last orig_node's OGM buffer */
+               memcpy(tt_buff, req_dst_orig_node->tt_buff,
+                      req_dst_orig_node->tt_buff_len);
+
+               spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
+       } else {
+               tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size) *
+                                               sizeof(struct tt_change);
+               ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
+
+               skb = tt_response_fill_table(tt_len, ttvn,
+                                            bat_priv->tt_global_hash,
+                                            primary_if, tt_global_valid_entry,
+                                            req_dst_orig_node);
+               if (!skb)
+                       goto out;
+
+               tt_response = (struct tt_query_packet *)skb->data;
+       }
+
+       tt_response->packet_type = BAT_TT_QUERY;
+       tt_response->version = COMPAT_VERSION;
+       tt_response->ttl = TTL;
+       memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN);
+       memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
+       tt_response->flags = TT_RESPONSE;
+
+       if (full_table)
+               tt_response->flags |= TT_FULL_TABLE;
+
+       bat_dbg(DBG_TT, bat_priv,
+               "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n",
+               res_dst_orig_node->orig, neigh_node->addr,
+               req_dst_orig_node->orig, req_ttvn);
+
+       send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+       ret = true;
+       goto out;
+
+unlock:
+       spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
+
+out:
+       if (res_dst_orig_node)
+               orig_node_free_ref(res_dst_orig_node);
+       if (req_dst_orig_node)
+               orig_node_free_ref(req_dst_orig_node);
+       if (neigh_node)
+               neigh_node_free_ref(neigh_node);
+       if (primary_if)
+               hardif_free_ref(primary_if);
+       if (!ret)
+               kfree_skb(skb);
+       return ret;
+
+}
+static bool send_my_tt_response(struct bat_priv *bat_priv,
+                               struct tt_query_packet *tt_request)
+{
+       struct orig_node *orig_node = NULL;
+       struct neigh_node *neigh_node = NULL;
+       struct hard_iface *primary_if = NULL;
+       uint8_t my_ttvn, req_ttvn, ttvn;
+       int ret = false;
+       unsigned char *tt_buff;
+       bool full_table;
+       uint16_t tt_len, tt_tot;
+       struct sk_buff *skb = NULL;
+       struct tt_query_packet *tt_response;
+
+       bat_dbg(DBG_TT, bat_priv,
+               "Received TT_REQUEST from %pM for "
+               "ttvn: %u (me) [%c]\n", tt_request->src,
+               tt_request->ttvn,
+               (tt_request->flags & TT_FULL_TABLE ? 'F' : '.'));
+
+
+       my_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn);
+       req_ttvn = tt_request->ttvn;
+
+       orig_node = get_orig_node(bat_priv, tt_request->src);
+       if (!orig_node)
+               goto out;
+
+       neigh_node = orig_node_get_router(orig_node);
+       if (!neigh_node)
+               goto out;
+
+       primary_if = primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
+
+       /* If the full table has been explicitly requested or the gap
+        * is too big send the whole local translation table */
+       if (tt_request->flags & TT_FULL_TABLE || my_ttvn != req_ttvn ||
+           !bat_priv->tt_buff)
+               full_table = true;
+       else
+               full_table = false;
+
+       /* In this version, fragmentation is not implemented, then
+        * I'll send only one packet with as much TT entries as I can */
+       if (!full_table) {
+               spin_lock_bh(&bat_priv->tt_buff_lock);
+               tt_len = bat_priv->tt_buff_len;
+               tt_tot = tt_len / sizeof(struct tt_change);
+
+               skb = dev_alloc_skb(sizeof(struct tt_query_packet) +
+                                   tt_len + ETH_HLEN);
+               if (!skb)
+                       goto unlock;
+
+               skb_reserve(skb, ETH_HLEN);
+               tt_response = (struct tt_query_packet *)skb_put(skb,
+                               sizeof(struct tt_query_packet) + tt_len);
+               tt_response->ttvn = req_ttvn;
+               tt_response->tt_data = htons(tt_tot);
+
+               tt_buff = skb->data + sizeof(struct tt_query_packet);
+               memcpy(tt_buff, bat_priv->tt_buff,
+                      bat_priv->tt_buff_len);
+               spin_unlock_bh(&bat_priv->tt_buff_lock);
+       } else {
+               tt_len = (uint16_t)atomic_read(&bat_priv->num_local_tt) *
+                                               sizeof(struct tt_change);
+               ttvn = (uint8_t)atomic_read(&bat_priv->ttvn);
+
+               skb = tt_response_fill_table(tt_len, ttvn,
+                                            bat_priv->tt_local_hash,
+                                            primary_if, tt_local_valid_entry,
+                                            NULL);
+               if (!skb)
+                       goto out;
+
+               tt_response = (struct tt_query_packet *)skb->data;
+       }
+
+       tt_response->packet_type = BAT_TT_QUERY;
+       tt_response->version = COMPAT_VERSION;
+       tt_response->ttl = TTL;
+       memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN);
+       memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
+       tt_response->flags = TT_RESPONSE;
+
+       if (full_table)
+               tt_response->flags |= TT_FULL_TABLE;
+
+       bat_dbg(DBG_TT, bat_priv,
+               "Sending TT_RESPONSE to %pM via %pM [%c]\n",
+               orig_node->orig, neigh_node->addr,
+               (tt_response->flags & TT_FULL_TABLE ? 'F' : '.'));
+
+       send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+       ret = true;
+       goto out;
+
+unlock:
+       spin_unlock_bh(&bat_priv->tt_buff_lock);
+out:
+       if (orig_node)
+               orig_node_free_ref(orig_node);
+       if (neigh_node)
+               neigh_node_free_ref(neigh_node);
+       if (primary_if)
+               hardif_free_ref(primary_if);
+       if (!ret)
+               kfree_skb(skb);
+       /* This packet was for me, so it doesn't need to be re-routed */
+       return true;
+}
+
+bool send_tt_response(struct bat_priv *bat_priv,
+                     struct tt_query_packet *tt_request)
+{
+       if (is_my_mac(tt_request->dst))
+               return send_my_tt_response(bat_priv, tt_request);
+       else
+               return send_other_tt_response(bat_priv, tt_request);
+}
+
+static void _tt_update_changes(struct bat_priv *bat_priv,
+                              struct orig_node *orig_node,
+                              struct tt_change *tt_change,
+                              uint16_t tt_num_changes, uint8_t ttvn)
+{
+       int i;
+
+       for (i = 0; i < tt_num_changes; i++) {
+               if ((tt_change + i)->flags & TT_CLIENT_DEL)
+                       tt_global_del(bat_priv, orig_node,
+                                     (tt_change + i)->addr,
+                                     "tt removed by changes",
+                                     (tt_change + i)->flags & TT_CLIENT_ROAM);
+               else
+                       if (!tt_global_add(bat_priv, orig_node,
+                                          (tt_change + i)->addr, ttvn, false))
+                               /* In case of problem while storing a
+                                * global_entry, we stop the updating
+                                * procedure without committing the
+                                * ttvn change. This will avoid to send
+                                * corrupted data on tt_request
+                                */
+                               return;
+       }
+}
+
+static void tt_fill_gtable(struct bat_priv *bat_priv,
+                          struct tt_query_packet *tt_response)
+{
+       struct orig_node *orig_node = NULL;
+
+       orig_node = orig_hash_find(bat_priv, tt_response->src);
+       if (!orig_node)
+               goto out;
+
+       /* Purge the old table first.. */
+       tt_global_del_orig(bat_priv, orig_node, "Received full table");
+
+       _tt_update_changes(bat_priv, orig_node,
+                          (struct tt_change *)(tt_response + 1),
+                          tt_response->tt_data, tt_response->ttvn);
+
+       spin_lock_bh(&orig_node->tt_buff_lock);
+       kfree(orig_node->tt_buff);
+       orig_node->tt_buff_len = 0;
+       orig_node->tt_buff = NULL;
+       spin_unlock_bh(&orig_node->tt_buff_lock);
+
+       atomic_set(&orig_node->last_ttvn, tt_response->ttvn);
+
+out:
+       if (orig_node)
+               orig_node_free_ref(orig_node);
+}
+
+void tt_update_changes(struct bat_priv *bat_priv, struct orig_node *orig_node,
+                      uint16_t tt_num_changes, uint8_t ttvn,
+                      struct tt_change *tt_change)
+{
+       _tt_update_changes(bat_priv, orig_node, tt_change, tt_num_changes,
+                          ttvn);
+
+       tt_save_orig_buffer(bat_priv, orig_node, (unsigned char *)tt_change,
+                           tt_num_changes);
+       atomic_set(&orig_node->last_ttvn, ttvn);
+}
+
+bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr)
+{
+       struct tt_local_entry *tt_local_entry = NULL;
+       bool ret = false;
+
+       tt_local_entry = tt_local_hash_find(bat_priv, addr);
+       if (!tt_local_entry)
+               goto out;
+       /* Check if the client has been logically deleted (but is kept for
+        * consistency purpose) */
+       if (tt_local_entry->flags & TT_CLIENT_PENDING)
+               goto out;
+       ret = true;
+out:
+       if (tt_local_entry)
+               tt_local_entry_free_ref(tt_local_entry);
+       return ret;
+}
+
+void handle_tt_response(struct bat_priv *bat_priv,
+                       struct tt_query_packet *tt_response)
+{
+       struct tt_req_node *node, *safe;
+       struct orig_node *orig_node = NULL;
+
+       bat_dbg(DBG_TT, bat_priv, "Received TT_RESPONSE from %pM for "
+               "ttvn %d t_size: %d [%c]\n",
+               tt_response->src, tt_response->ttvn,
+               tt_response->tt_data,
+               (tt_response->flags & TT_FULL_TABLE ? 'F' : '.'));
+
+       orig_node = orig_hash_find(bat_priv, tt_response->src);
+       if (!orig_node)
+               goto out;
+
+       if (tt_response->flags & TT_FULL_TABLE)
+               tt_fill_gtable(bat_priv, tt_response);
+       else
+               tt_update_changes(bat_priv, orig_node, tt_response->tt_data,
+                                 tt_response->ttvn,
+                                 (struct tt_change *)(tt_response + 1));
+
+       /* Delete the tt_req_node from pending tt_requests list */
+       spin_lock_bh(&bat_priv->tt_req_list_lock);
+       list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) {
+               if (!compare_eth(node->addr, tt_response->src))
+                       continue;
+               list_del(&node->list);
+               kfree(node);
+       }
+       spin_unlock_bh(&bat_priv->tt_req_list_lock);
+
+       /* Recalculate the CRC for this orig_node and store it */
+       orig_node->tt_crc = tt_global_crc(bat_priv, orig_node);
+       /* Roaming phase is over: tables are in sync again. I can
+        * unset the flag */
+       orig_node->tt_poss_change = false;
+out:
+       if (orig_node)
+               orig_node_free_ref(orig_node);
+}
+
+int tt_init(struct bat_priv *bat_priv)
+{
+       if (!tt_local_init(bat_priv))
+               return 0;
+
+       if (!tt_global_init(bat_priv))
+               return 0;
+
+       tt_start_timer(bat_priv);
+
+       return 1;
+}
+
+static void tt_roam_list_free(struct bat_priv *bat_priv)
+{
+       struct tt_roam_node *node, *safe;
+
+       spin_lock_bh(&bat_priv->tt_roam_list_lock);
+
+       list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) {
+               list_del(&node->list);
+               kfree(node);
+       }
+
+       spin_unlock_bh(&bat_priv->tt_roam_list_lock);
+}
+
+static void tt_roam_purge(struct bat_priv *bat_priv)
+{
+       struct tt_roam_node *node, *safe;
+
+       spin_lock_bh(&bat_priv->tt_roam_list_lock);
+       list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) {
+               if (!is_out_of_time(node->first_time,
+                                   ROAMING_MAX_TIME * 1000))
+                       continue;
+
+               list_del(&node->list);
+               kfree(node);
+       }
+       spin_unlock_bh(&bat_priv->tt_roam_list_lock);
+}
+
+/* This function checks whether the client already reached the
+ * maximum number of possible roaming phases. In this case the ROAMING_ADV
+ * will not be sent.
+ *
+ * returns true if the ROAMING_ADV can be sent, false otherwise */
+static bool tt_check_roam_count(struct bat_priv *bat_priv,
+                               uint8_t *client)
+{
+       struct tt_roam_node *tt_roam_node;
+       bool ret = false;
+
+       spin_lock_bh(&bat_priv->tt_roam_list_lock);
+       /* The new tt_req will be issued only if I'm not waiting for a
+        * reply from the same orig_node yet */
+       list_for_each_entry(tt_roam_node, &bat_priv->tt_roam_list, list) {
+               if (!compare_eth(tt_roam_node->addr, client))
+                       continue;
+
+               if (is_out_of_time(tt_roam_node->first_time,
+                                  ROAMING_MAX_TIME * 1000))
+                       continue;
+
+               if (!atomic_dec_not_zero(&tt_roam_node->counter))
+                       /* Sorry, you roamed too many times! */
+                       goto unlock;
+               ret = true;
+               break;
+       }
+
+       if (!ret) {
+               tt_roam_node = kmalloc(sizeof(*tt_roam_node), GFP_ATOMIC);
+               if (!tt_roam_node)
+                       goto unlock;
+
+               tt_roam_node->first_time = jiffies;
+               atomic_set(&tt_roam_node->counter, ROAMING_MAX_COUNT - 1);
+               memcpy(tt_roam_node->addr, client, ETH_ALEN);
+
+               list_add(&tt_roam_node->list, &bat_priv->tt_roam_list);
+               ret = true;
+       }
+
+unlock:
+       spin_unlock_bh(&bat_priv->tt_roam_list_lock);
+       return ret;
+}
+
+void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
+                  struct orig_node *orig_node)
+{
+       struct neigh_node *neigh_node = NULL;
+       struct sk_buff *skb = NULL;
+       struct roam_adv_packet *roam_adv_packet;
+       int ret = 1;
+       struct hard_iface *primary_if;
+
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times */
+       if (!tt_check_roam_count(bat_priv, client))
+               goto out;
+
+       skb = dev_alloc_skb(sizeof(struct roam_adv_packet) + ETH_HLEN);
+       if (!skb)
+               goto out;
+
+       skb_reserve(skb, ETH_HLEN);
+
+       roam_adv_packet = (struct roam_adv_packet *)skb_put(skb,
+                                       sizeof(struct roam_adv_packet));
+
+       roam_adv_packet->packet_type = BAT_ROAM_ADV;
+       roam_adv_packet->version = COMPAT_VERSION;
+       roam_adv_packet->ttl = TTL;
+       primary_if = primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
+       memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
+       hardif_free_ref(primary_if);
+       memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN);
+       memcpy(roam_adv_packet->client, client, ETH_ALEN);
+
+       neigh_node = orig_node_get_router(orig_node);
+       if (!neigh_node)
+               goto out;
+
+       bat_dbg(DBG_TT, bat_priv,
+               "Sending ROAMING_ADV to %pM (client %pM) via %pM\n",
+               orig_node->orig, client, neigh_node->addr);
+
+       send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+       ret = 0;
+
+out:
+       if (neigh_node)
+               neigh_node_free_ref(neigh_node);
+       if (ret)
+               kfree_skb(skb);
+       return;
+}
+
+static void tt_purge(struct work_struct *work)
+{
+       struct delayed_work *delayed_work =
+               container_of(work, struct delayed_work, work);
+       struct bat_priv *bat_priv =
+               container_of(delayed_work, struct bat_priv, tt_work);
+
+       tt_local_purge(bat_priv);
+       tt_global_roam_purge(bat_priv);
+       tt_req_purge(bat_priv);
+       tt_roam_purge(bat_priv);
+
+       tt_start_timer(bat_priv);
+}
+
+void tt_free(struct bat_priv *bat_priv)
+{
+       cancel_delayed_work_sync(&bat_priv->tt_work);
+
+       tt_local_table_free(bat_priv);
+       tt_global_table_free(bat_priv);
+       tt_req_list_free(bat_priv);
+       tt_changes_list_free(bat_priv);
+       tt_roam_list_free(bat_priv);
+
+       kfree(bat_priv->tt_buff);
+}
+
+/* This function will reset the specified flags from all the entries in
+ * the given hash table and will increment num_local_tt for each involved
+ * entry */
+static void tt_local_reset_flags(struct bat_priv *bat_priv, uint16_t flags)
+{
+       int i;
+       struct hashtable_t *hash = bat_priv->tt_local_hash;
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct tt_local_entry *tt_local_entry;
+
+       if (!hash)
+               return;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(tt_local_entry, node,
+                                        head, hash_entry) {
+                       tt_local_entry->flags &= ~flags;
+                       atomic_inc(&bat_priv->num_local_tt);
+               }
+               rcu_read_unlock();
+       }
+
+}
+
+/* Purge out all the tt local entries marked with TT_CLIENT_PENDING */
+static void tt_local_purge_pending_clients(struct bat_priv *bat_priv)
+{
+       struct hashtable_t *hash = bat_priv->tt_local_hash;
+       struct tt_local_entry *tt_local_entry;
+       struct hlist_node *node, *node_tmp;
+       struct hlist_head *head;
+       spinlock_t *list_lock; /* protects write access to the hash lists */
+       int i;
+
+       if (!hash)
+               return;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+               list_lock = &hash->list_locks[i];
+
+               spin_lock_bh(list_lock);
+               hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
+                                         head, hash_entry) {
+                       if (!(tt_local_entry->flags & TT_CLIENT_PENDING))
+                               continue;
+
+                       bat_dbg(DBG_TT, bat_priv, "Deleting local tt entry "
+                               "(%pM): pending\n", tt_local_entry->addr);
+
+                       atomic_dec(&bat_priv->num_local_tt);
+                       hlist_del_rcu(node);
+                       tt_local_entry_free_ref(tt_local_entry);
+               }
+               spin_unlock_bh(list_lock);
+       }
+
+}
+
+void tt_commit_changes(struct bat_priv *bat_priv)
+{
+       tt_local_reset_flags(bat_priv, TT_CLIENT_NEW);
+       tt_local_purge_pending_clients(bat_priv);
+
+       /* Increment the TTVN only once per OGM interval */
+       atomic_inc(&bat_priv->ttvn);
+       bat_priv->tt_poss_change = false;
+}