Path: blob/master/net/batman-adv/translation-table.c
15109 views
/*1* Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:2*3* Marek Lindner, Simon Wunderlich4*5* This program is free software; you can redistribute it and/or6* modify it under the terms of version 2 of the GNU General Public7* License as published by the Free Software Foundation.8*9* This program is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* General Public License for more details.13*14* You should have received a copy of the GNU General Public License15* along with this program; if not, write to the Free Software16* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*19*/2021#include "main.h"22#include "translation-table.h"23#include "soft-interface.h"24#include "hard-interface.h"25#include "hash.h"26#include "originator.h"2728static void tt_local_purge(struct work_struct *work);29static void _tt_global_del_orig(struct bat_priv *bat_priv,30struct tt_global_entry *tt_global_entry,31char *message);3233/* returns 1 if they are the same mac addr */34static int compare_ltt(struct hlist_node *node, void *data2)35{36void *data1 = container_of(node, struct tt_local_entry, hash_entry);3738return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);39}4041/* returns 1 if they are the same mac addr */42static int compare_gtt(struct hlist_node *node, void *data2)43{44void *data1 = container_of(node, struct tt_global_entry, hash_entry);4546return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);47}4849static void tt_local_start_timer(struct bat_priv *bat_priv)50{51INIT_DELAYED_WORK(&bat_priv->tt_work, tt_local_purge);52queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, 10 * HZ);53}5455static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,56void *data)57{58struct hashtable_t *hash = bat_priv->tt_local_hash;59struct hlist_head *head;60struct hlist_node *node;61struct tt_local_entry *tt_local_entry, *tt_local_entry_tmp = NULL;62int index;6364if (!hash)65return NULL;6667index = choose_orig(data, hash->size);68head = &hash->table[index];6970rcu_read_lock();71hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) {72if (!compare_eth(tt_local_entry, data))73continue;7475tt_local_entry_tmp = tt_local_entry;76break;77}78rcu_read_unlock();7980return tt_local_entry_tmp;81}8283static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv,84void *data)85{86struct hashtable_t *hash = bat_priv->tt_global_hash;87struct hlist_head *head;88struct hlist_node *node;89struct tt_global_entry *tt_global_entry;90struct tt_global_entry *tt_global_entry_tmp = NULL;91int index;9293if (!hash)94return NULL;9596index = choose_orig(data, hash->size);97head = &hash->table[index];9899rcu_read_lock();100hlist_for_each_entry_rcu(tt_global_entry, node, head, hash_entry) {101if (!compare_eth(tt_global_entry, data))102continue;103104tt_global_entry_tmp = tt_global_entry;105break;106}107rcu_read_unlock();108109return tt_global_entry_tmp;110}111112int tt_local_init(struct bat_priv *bat_priv)113{114if (bat_priv->tt_local_hash)115return 1;116117bat_priv->tt_local_hash = hash_new(1024);118119if (!bat_priv->tt_local_hash)120return 0;121122atomic_set(&bat_priv->tt_local_changed, 0);123tt_local_start_timer(bat_priv);124125return 1;126}127128void tt_local_add(struct net_device *soft_iface, uint8_t *addr)129{130struct bat_priv *bat_priv = netdev_priv(soft_iface);131struct tt_local_entry *tt_local_entry;132struct tt_global_entry *tt_global_entry;133int required_bytes;134135spin_lock_bh(&bat_priv->tt_lhash_lock);136tt_local_entry = tt_local_hash_find(bat_priv, addr);137spin_unlock_bh(&bat_priv->tt_lhash_lock);138139if (tt_local_entry) {140tt_local_entry->last_seen = jiffies;141return;142}143144/* only announce as many hosts as possible in the batman-packet and145space in batman_packet->num_tt That also should give a limit to146MAC-flooding. */147required_bytes = (bat_priv->num_local_tt + 1) * ETH_ALEN;148required_bytes += BAT_PACKET_LEN;149150if ((required_bytes > ETH_DATA_LEN) ||151(atomic_read(&bat_priv->aggregated_ogms) &&152required_bytes > MAX_AGGREGATION_BYTES) ||153(bat_priv->num_local_tt + 1 > 255)) {154bat_dbg(DBG_ROUTES, bat_priv,155"Can't add new local tt entry (%pM): "156"number of local tt entries exceeds packet size\n",157addr);158return;159}160161bat_dbg(DBG_ROUTES, bat_priv,162"Creating new local tt entry: %pM\n", addr);163164tt_local_entry = kmalloc(sizeof(struct tt_local_entry), GFP_ATOMIC);165if (!tt_local_entry)166return;167168memcpy(tt_local_entry->addr, addr, ETH_ALEN);169tt_local_entry->last_seen = jiffies;170171/* the batman interface mac address should never be purged */172if (compare_eth(addr, soft_iface->dev_addr))173tt_local_entry->never_purge = 1;174else175tt_local_entry->never_purge = 0;176177spin_lock_bh(&bat_priv->tt_lhash_lock);178179hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig,180tt_local_entry, &tt_local_entry->hash_entry);181bat_priv->num_local_tt++;182atomic_set(&bat_priv->tt_local_changed, 1);183184spin_unlock_bh(&bat_priv->tt_lhash_lock);185186/* remove address from global hash if present */187spin_lock_bh(&bat_priv->tt_ghash_lock);188189tt_global_entry = tt_global_hash_find(bat_priv, addr);190191if (tt_global_entry)192_tt_global_del_orig(bat_priv, tt_global_entry,193"local tt received");194195spin_unlock_bh(&bat_priv->tt_ghash_lock);196}197198int tt_local_fill_buffer(struct bat_priv *bat_priv,199unsigned char *buff, int buff_len)200{201struct hashtable_t *hash = bat_priv->tt_local_hash;202struct tt_local_entry *tt_local_entry;203struct hlist_node *node;204struct hlist_head *head;205int i, count = 0;206207spin_lock_bh(&bat_priv->tt_lhash_lock);208209for (i = 0; i < hash->size; i++) {210head = &hash->table[i];211212rcu_read_lock();213hlist_for_each_entry_rcu(tt_local_entry, node,214head, hash_entry) {215if (buff_len < (count + 1) * ETH_ALEN)216break;217218memcpy(buff + (count * ETH_ALEN), tt_local_entry->addr,219ETH_ALEN);220221count++;222}223rcu_read_unlock();224}225226/* if we did not get all new local tts see you next time ;-) */227if (count == bat_priv->num_local_tt)228atomic_set(&bat_priv->tt_local_changed, 0);229230spin_unlock_bh(&bat_priv->tt_lhash_lock);231return count;232}233234int tt_local_seq_print_text(struct seq_file *seq, void *offset)235{236struct net_device *net_dev = (struct net_device *)seq->private;237struct bat_priv *bat_priv = netdev_priv(net_dev);238struct hashtable_t *hash = bat_priv->tt_local_hash;239struct tt_local_entry *tt_local_entry;240struct hard_iface *primary_if;241struct hlist_node *node;242struct hlist_head *head;243size_t buf_size, pos;244char *buff;245int i, ret = 0;246247primary_if = primary_if_get_selected(bat_priv);248if (!primary_if) {249ret = seq_printf(seq, "BATMAN mesh %s disabled - "250"please specify interfaces to enable it\n",251net_dev->name);252goto out;253}254255if (primary_if->if_status != IF_ACTIVE) {256ret = seq_printf(seq, "BATMAN mesh %s disabled - "257"primary interface not active\n",258net_dev->name);259goto out;260}261262seq_printf(seq, "Locally retrieved addresses (from %s) "263"announced via TT:\n",264net_dev->name);265266spin_lock_bh(&bat_priv->tt_lhash_lock);267268buf_size = 1;269/* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */270for (i = 0; i < hash->size; i++) {271head = &hash->table[i];272273rcu_read_lock();274__hlist_for_each_rcu(node, head)275buf_size += 21;276rcu_read_unlock();277}278279buff = kmalloc(buf_size, GFP_ATOMIC);280if (!buff) {281spin_unlock_bh(&bat_priv->tt_lhash_lock);282ret = -ENOMEM;283goto out;284}285286buff[0] = '\0';287pos = 0;288289for (i = 0; i < hash->size; i++) {290head = &hash->table[i];291292rcu_read_lock();293hlist_for_each_entry_rcu(tt_local_entry, node,294head, hash_entry) {295pos += snprintf(buff + pos, 22, " * %pM\n",296tt_local_entry->addr);297}298rcu_read_unlock();299}300301spin_unlock_bh(&bat_priv->tt_lhash_lock);302303seq_printf(seq, "%s", buff);304kfree(buff);305out:306if (primary_if)307hardif_free_ref(primary_if);308return ret;309}310311static void _tt_local_del(struct hlist_node *node, void *arg)312{313struct bat_priv *bat_priv = (struct bat_priv *)arg;314void *data = container_of(node, struct tt_local_entry, hash_entry);315316kfree(data);317bat_priv->num_local_tt--;318atomic_set(&bat_priv->tt_local_changed, 1);319}320321static void tt_local_del(struct bat_priv *bat_priv,322struct tt_local_entry *tt_local_entry,323char *message)324{325bat_dbg(DBG_ROUTES, bat_priv, "Deleting local tt entry (%pM): %s\n",326tt_local_entry->addr, message);327328hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig,329tt_local_entry->addr);330_tt_local_del(&tt_local_entry->hash_entry, bat_priv);331}332333void tt_local_remove(struct bat_priv *bat_priv,334uint8_t *addr, char *message)335{336struct tt_local_entry *tt_local_entry;337338spin_lock_bh(&bat_priv->tt_lhash_lock);339340tt_local_entry = tt_local_hash_find(bat_priv, addr);341342if (tt_local_entry)343tt_local_del(bat_priv, tt_local_entry, message);344345spin_unlock_bh(&bat_priv->tt_lhash_lock);346}347348static void tt_local_purge(struct work_struct *work)349{350struct delayed_work *delayed_work =351container_of(work, struct delayed_work, work);352struct bat_priv *bat_priv =353container_of(delayed_work, struct bat_priv, tt_work);354struct hashtable_t *hash = bat_priv->tt_local_hash;355struct tt_local_entry *tt_local_entry;356struct hlist_node *node, *node_tmp;357struct hlist_head *head;358unsigned long timeout;359int i;360361spin_lock_bh(&bat_priv->tt_lhash_lock);362363for (i = 0; i < hash->size; i++) {364head = &hash->table[i];365366hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,367head, hash_entry) {368if (tt_local_entry->never_purge)369continue;370371timeout = tt_local_entry->last_seen;372timeout += TT_LOCAL_TIMEOUT * HZ;373374if (time_before(jiffies, timeout))375continue;376377tt_local_del(bat_priv, tt_local_entry,378"address timed out");379}380}381382spin_unlock_bh(&bat_priv->tt_lhash_lock);383tt_local_start_timer(bat_priv);384}385386void tt_local_free(struct bat_priv *bat_priv)387{388if (!bat_priv->tt_local_hash)389return;390391cancel_delayed_work_sync(&bat_priv->tt_work);392hash_delete(bat_priv->tt_local_hash, _tt_local_del, bat_priv);393bat_priv->tt_local_hash = NULL;394}395396int tt_global_init(struct bat_priv *bat_priv)397{398if (bat_priv->tt_global_hash)399return 1;400401bat_priv->tt_global_hash = hash_new(1024);402403if (!bat_priv->tt_global_hash)404return 0;405406return 1;407}408409void tt_global_add_orig(struct bat_priv *bat_priv,410struct orig_node *orig_node,411unsigned char *tt_buff, int tt_buff_len)412{413struct tt_global_entry *tt_global_entry;414struct tt_local_entry *tt_local_entry;415int tt_buff_count = 0;416unsigned char *tt_ptr;417418while ((tt_buff_count + 1) * ETH_ALEN <= tt_buff_len) {419spin_lock_bh(&bat_priv->tt_ghash_lock);420421tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN);422tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr);423424if (!tt_global_entry) {425spin_unlock_bh(&bat_priv->tt_ghash_lock);426427tt_global_entry =428kmalloc(sizeof(struct tt_global_entry),429GFP_ATOMIC);430431if (!tt_global_entry)432break;433434memcpy(tt_global_entry->addr, tt_ptr, ETH_ALEN);435436bat_dbg(DBG_ROUTES, bat_priv,437"Creating new global tt entry: "438"%pM (via %pM)\n",439tt_global_entry->addr, orig_node->orig);440441spin_lock_bh(&bat_priv->tt_ghash_lock);442hash_add(bat_priv->tt_global_hash, compare_gtt,443choose_orig, tt_global_entry,444&tt_global_entry->hash_entry);445446}447448tt_global_entry->orig_node = orig_node;449spin_unlock_bh(&bat_priv->tt_ghash_lock);450451/* remove address from local hash if present */452spin_lock_bh(&bat_priv->tt_lhash_lock);453454tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN);455tt_local_entry = tt_local_hash_find(bat_priv, tt_ptr);456457if (tt_local_entry)458tt_local_del(bat_priv, tt_local_entry,459"global tt received");460461spin_unlock_bh(&bat_priv->tt_lhash_lock);462463tt_buff_count++;464}465466/* initialize, and overwrite if malloc succeeds */467orig_node->tt_buff = NULL;468orig_node->tt_buff_len = 0;469470if (tt_buff_len > 0) {471orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);472if (orig_node->tt_buff) {473memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);474orig_node->tt_buff_len = tt_buff_len;475}476}477}478479int tt_global_seq_print_text(struct seq_file *seq, void *offset)480{481struct net_device *net_dev = (struct net_device *)seq->private;482struct bat_priv *bat_priv = netdev_priv(net_dev);483struct hashtable_t *hash = bat_priv->tt_global_hash;484struct tt_global_entry *tt_global_entry;485struct hard_iface *primary_if;486struct hlist_node *node;487struct hlist_head *head;488size_t buf_size, pos;489char *buff;490int i, ret = 0;491492primary_if = primary_if_get_selected(bat_priv);493if (!primary_if) {494ret = seq_printf(seq, "BATMAN mesh %s disabled - please "495"specify interfaces to enable it\n",496net_dev->name);497goto out;498}499500if (primary_if->if_status != IF_ACTIVE) {501ret = seq_printf(seq, "BATMAN mesh %s disabled - "502"primary interface not active\n",503net_dev->name);504goto out;505}506507seq_printf(seq,508"Globally announced TT entries received via the mesh %s\n",509net_dev->name);510511spin_lock_bh(&bat_priv->tt_ghash_lock);512513buf_size = 1;514/* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/515for (i = 0; i < hash->size; i++) {516head = &hash->table[i];517518rcu_read_lock();519__hlist_for_each_rcu(node, head)520buf_size += 43;521rcu_read_unlock();522}523524buff = kmalloc(buf_size, GFP_ATOMIC);525if (!buff) {526spin_unlock_bh(&bat_priv->tt_ghash_lock);527ret = -ENOMEM;528goto out;529}530buff[0] = '\0';531pos = 0;532533for (i = 0; i < hash->size; i++) {534head = &hash->table[i];535536rcu_read_lock();537hlist_for_each_entry_rcu(tt_global_entry, node,538head, hash_entry) {539pos += snprintf(buff + pos, 44,540" * %pM via %pM\n",541tt_global_entry->addr,542tt_global_entry->orig_node->orig);543}544rcu_read_unlock();545}546547spin_unlock_bh(&bat_priv->tt_ghash_lock);548549seq_printf(seq, "%s", buff);550kfree(buff);551out:552if (primary_if)553hardif_free_ref(primary_if);554return ret;555}556557static void _tt_global_del_orig(struct bat_priv *bat_priv,558struct tt_global_entry *tt_global_entry,559char *message)560{561bat_dbg(DBG_ROUTES, bat_priv,562"Deleting global tt entry %pM (via %pM): %s\n",563tt_global_entry->addr, tt_global_entry->orig_node->orig,564message);565566hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig,567tt_global_entry->addr);568kfree(tt_global_entry);569}570571void tt_global_del_orig(struct bat_priv *bat_priv,572struct orig_node *orig_node, char *message)573{574struct tt_global_entry *tt_global_entry;575int tt_buff_count = 0;576unsigned char *tt_ptr;577578if (orig_node->tt_buff_len == 0)579return;580581spin_lock_bh(&bat_priv->tt_ghash_lock);582583while ((tt_buff_count + 1) * ETH_ALEN <= orig_node->tt_buff_len) {584tt_ptr = orig_node->tt_buff + (tt_buff_count * ETH_ALEN);585tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr);586587if ((tt_global_entry) &&588(tt_global_entry->orig_node == orig_node))589_tt_global_del_orig(bat_priv, tt_global_entry,590message);591592tt_buff_count++;593}594595spin_unlock_bh(&bat_priv->tt_ghash_lock);596597orig_node->tt_buff_len = 0;598kfree(orig_node->tt_buff);599orig_node->tt_buff = NULL;600}601602static void tt_global_del(struct hlist_node *node, void *arg)603{604void *data = container_of(node, struct tt_global_entry, hash_entry);605606kfree(data);607}608609void tt_global_free(struct bat_priv *bat_priv)610{611if (!bat_priv->tt_global_hash)612return;613614hash_delete(bat_priv->tt_global_hash, tt_global_del, NULL);615bat_priv->tt_global_hash = NULL;616}617618struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr)619{620struct tt_global_entry *tt_global_entry;621struct orig_node *orig_node = NULL;622623spin_lock_bh(&bat_priv->tt_ghash_lock);624tt_global_entry = tt_global_hash_find(bat_priv, addr);625626if (!tt_global_entry)627goto out;628629if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount))630goto out;631632orig_node = tt_global_entry->orig_node;633634out:635spin_unlock_bh(&bat_priv->tt_ghash_lock);636return orig_node;637}638639640