blob: 5f1fcd5736332aa7db10137591d17344f4fedbe7 [file] [log] [blame]
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +00001/*
Sven Eckelmann64afe352011-01-27 10:38:15 +01002 * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +00003 *
4 * Marek Lindner, Simon Wunderlich
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA
19 *
20 */
21
22#include "main.h"
23#include "translation-table.h"
24#include "soft-interface.h"
Marek Lindner32ae9b22011-04-20 15:40:58 +020025#include "hard-interface.h"
Antonio Quartullia73105b2011-04-27 14:27:44 +020026#include "send.h"
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000027#include "hash.h"
28#include "originator.h"
Antonio Quartullia73105b2011-04-27 14:27:44 +020029#include "routing.h"
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000030
Antonio Quartullia73105b2011-04-27 14:27:44 +020031#include <linux/crc16.h>
32
33static void _tt_global_del(struct bat_priv *bat_priv,
34 struct tt_global_entry *tt_global_entry,
35 const char *message);
36static void tt_purge(struct work_struct *work);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000037
Marek Lindner7aadf882011-02-18 12:28:09 +000038/* returns 1 if they are the same mac addr */
Sven Eckelmann747e4222011-05-14 23:14:50 +020039static int compare_ltt(const struct hlist_node *node, const void *data2)
Marek Lindner7aadf882011-02-18 12:28:09 +000040{
Sven Eckelmann747e4222011-05-14 23:14:50 +020041 const void *data1 = container_of(node, struct tt_local_entry,
42 hash_entry);
Marek Lindner7aadf882011-02-18 12:28:09 +000043
44 return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
45}
46
47/* returns 1 if they are the same mac addr */
Sven Eckelmann747e4222011-05-14 23:14:50 +020048static int compare_gtt(const struct hlist_node *node, const void *data2)
Marek Lindner7aadf882011-02-18 12:28:09 +000049{
Sven Eckelmann747e4222011-05-14 23:14:50 +020050 const void *data1 = container_of(node, struct tt_global_entry,
51 hash_entry);
Marek Lindner7aadf882011-02-18 12:28:09 +000052
53 return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
54}
55
Antonio Quartullia73105b2011-04-27 14:27:44 +020056static void tt_start_timer(struct bat_priv *bat_priv)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000057{
Antonio Quartullia73105b2011-04-27 14:27:44 +020058 INIT_DELAYED_WORK(&bat_priv->tt_work, tt_purge);
59 queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work,
60 msecs_to_jiffies(5000));
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000061}
62
Antonio Quartulli2dafb492011-05-05 08:42:45 +020063static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,
Sven Eckelmann747e4222011-05-14 23:14:50 +020064 const void *data)
Marek Lindner7aadf882011-02-18 12:28:09 +000065{
Antonio Quartulli2dafb492011-05-05 08:42:45 +020066 struct hashtable_t *hash = bat_priv->tt_local_hash;
Marek Lindner7aadf882011-02-18 12:28:09 +000067 struct hlist_head *head;
68 struct hlist_node *node;
Antonio Quartulli2dafb492011-05-05 08:42:45 +020069 struct tt_local_entry *tt_local_entry, *tt_local_entry_tmp = NULL;
Marek Lindner7aadf882011-02-18 12:28:09 +000070 int index;
71
72 if (!hash)
73 return NULL;
74
75 index = choose_orig(data, hash->size);
76 head = &hash->table[index];
77
78 rcu_read_lock();
Antonio Quartulli2dafb492011-05-05 08:42:45 +020079 hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) {
80 if (!compare_eth(tt_local_entry, data))
Marek Lindner7aadf882011-02-18 12:28:09 +000081 continue;
82
Antonio Quartulli7683fdc2011-04-27 14:28:07 +020083 if (!atomic_inc_not_zero(&tt_local_entry->refcount))
84 continue;
85
Antonio Quartulli2dafb492011-05-05 08:42:45 +020086 tt_local_entry_tmp = tt_local_entry;
Marek Lindner7aadf882011-02-18 12:28:09 +000087 break;
88 }
89 rcu_read_unlock();
90
Antonio Quartulli2dafb492011-05-05 08:42:45 +020091 return tt_local_entry_tmp;
Marek Lindner7aadf882011-02-18 12:28:09 +000092}
93
Antonio Quartulli2dafb492011-05-05 08:42:45 +020094static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv,
Sven Eckelmann747e4222011-05-14 23:14:50 +020095 const void *data)
Marek Lindner7aadf882011-02-18 12:28:09 +000096{
Antonio Quartulli2dafb492011-05-05 08:42:45 +020097 struct hashtable_t *hash = bat_priv->tt_global_hash;
Marek Lindner7aadf882011-02-18 12:28:09 +000098 struct hlist_head *head;
99 struct hlist_node *node;
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200100 struct tt_global_entry *tt_global_entry;
101 struct tt_global_entry *tt_global_entry_tmp = NULL;
Marek Lindner7aadf882011-02-18 12:28:09 +0000102 int index;
103
104 if (!hash)
105 return NULL;
106
107 index = choose_orig(data, hash->size);
108 head = &hash->table[index];
109
110 rcu_read_lock();
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200111 hlist_for_each_entry_rcu(tt_global_entry, node, head, hash_entry) {
112 if (!compare_eth(tt_global_entry, data))
Marek Lindner7aadf882011-02-18 12:28:09 +0000113 continue;
114
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200115 if (!atomic_inc_not_zero(&tt_global_entry->refcount))
116 continue;
117
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200118 tt_global_entry_tmp = tt_global_entry;
Marek Lindner7aadf882011-02-18 12:28:09 +0000119 break;
120 }
121 rcu_read_unlock();
122
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200123 return tt_global_entry_tmp;
Marek Lindner7aadf882011-02-18 12:28:09 +0000124}
125
Antonio Quartullia73105b2011-04-27 14:27:44 +0200126static bool is_out_of_time(unsigned long starting_time, unsigned long timeout)
127{
128 unsigned long deadline;
129 deadline = starting_time + msecs_to_jiffies(timeout);
130
131 return time_after(jiffies, deadline);
132}
133
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200134static void tt_local_entry_free_ref(struct tt_local_entry *tt_local_entry)
135{
136 if (atomic_dec_and_test(&tt_local_entry->refcount))
137 kfree_rcu(tt_local_entry, rcu);
138}
139
140static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry)
141{
142 if (atomic_dec_and_test(&tt_global_entry->refcount))
143 kfree_rcu(tt_global_entry, rcu);
144}
145
Antonio Quartullia73105b2011-04-27 14:27:44 +0200146static void tt_local_event(struct bat_priv *bat_priv, uint8_t op,
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200147 const uint8_t *addr, bool roaming)
Antonio Quartullia73105b2011-04-27 14:27:44 +0200148{
149 struct tt_change_node *tt_change_node;
150
151 tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC);
152
153 if (!tt_change_node)
154 return;
155
156 tt_change_node->change.flags = op;
Antonio Quartullicc47f662011-04-27 14:27:57 +0200157 if (roaming)
158 tt_change_node->change.flags |= TT_CLIENT_ROAM;
159
Antonio Quartullia73105b2011-04-27 14:27:44 +0200160 memcpy(tt_change_node->change.addr, addr, ETH_ALEN);
161
162 spin_lock_bh(&bat_priv->tt_changes_list_lock);
163 /* track the change in the OGMinterval list */
164 list_add_tail(&tt_change_node->list, &bat_priv->tt_changes_list);
165 atomic_inc(&bat_priv->tt_local_changes);
166 spin_unlock_bh(&bat_priv->tt_changes_list_lock);
167
168 atomic_set(&bat_priv->tt_ogm_append_cnt, 0);
169}
170
171int tt_len(int changes_num)
172{
173 return changes_num * sizeof(struct tt_change);
174}
175
176static int tt_local_init(struct bat_priv *bat_priv)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000177{
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200178 if (bat_priv->tt_local_hash)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000179 return 1;
180
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200181 bat_priv->tt_local_hash = hash_new(1024);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000182
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200183 if (!bat_priv->tt_local_hash)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000184 return 0;
185
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000186 return 1;
187}
188
Sven Eckelmann747e4222011-05-14 23:14:50 +0200189void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000190{
191 struct bat_priv *bat_priv = netdev_priv(soft_iface);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200192 struct tt_local_entry *tt_local_entry = NULL;
193 struct tt_global_entry *tt_global_entry = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000194
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200195 tt_local_entry = tt_local_hash_find(bat_priv, addr);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000196
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200197 if (tt_local_entry) {
198 tt_local_entry->last_seen = jiffies;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200199 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000200 }
201
Sven Eckelmann704509b2011-05-14 23:14:54 +0200202 tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC);
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200203 if (!tt_local_entry)
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200204 goto out;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200205
Antonio Quartullicc47f662011-04-27 14:27:57 +0200206 tt_local_event(bat_priv, NO_FLAGS, addr, false);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200207
208 bat_dbg(DBG_TT, bat_priv,
209 "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
210 (uint8_t)atomic_read(&bat_priv->ttvn));
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000211
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200212 memcpy(tt_local_entry->addr, addr, ETH_ALEN);
213 tt_local_entry->last_seen = jiffies;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200214 atomic_set(&tt_local_entry->refcount, 2);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000215
216 /* the batman interface mac address should never be purged */
Marek Lindner39901e72011-02-18 12:28:08 +0000217 if (compare_eth(addr, soft_iface->dev_addr))
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200218 tt_local_entry->never_purge = 1;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000219 else
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200220 tt_local_entry->never_purge = 0;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000221
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200222 hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig,
223 tt_local_entry, &tt_local_entry->hash_entry);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200224
Antonio Quartullia73105b2011-04-27 14:27:44 +0200225 atomic_inc(&bat_priv->num_local_tt);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000226
227 /* remove address from global hash if present */
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200228 tt_global_entry = tt_global_hash_find(bat_priv, addr);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000229
Antonio Quartullicc47f662011-04-27 14:27:57 +0200230 /* Check whether it is a roaming! */
231 if (tt_global_entry) {
Antonio Quartullicc47f662011-04-27 14:27:57 +0200232 /* This node is probably going to update its tt table */
233 tt_global_entry->orig_node->tt_poss_change = true;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200234 _tt_global_del(bat_priv, tt_global_entry,
235 "local tt received");
Antonio Quartullicc47f662011-04-27 14:27:57 +0200236 send_roam_adv(bat_priv, tt_global_entry->addr,
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200237 tt_global_entry->orig_node);
238 }
239out:
240 if (tt_local_entry)
241 tt_local_entry_free_ref(tt_local_entry);
242 if (tt_global_entry)
243 tt_global_entry_free_ref(tt_global_entry);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000244}
245
Antonio Quartullia73105b2011-04-27 14:27:44 +0200246int tt_changes_fill_buffer(struct bat_priv *bat_priv,
247 unsigned char *buff, int buff_len)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000248{
Antonio Quartullia73105b2011-04-27 14:27:44 +0200249 int count = 0, tot_changes = 0;
250 struct tt_change_node *entry, *safe;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000251
Antonio Quartullia73105b2011-04-27 14:27:44 +0200252 if (buff_len > 0)
253 tot_changes = buff_len / tt_len(1);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000254
Antonio Quartullia73105b2011-04-27 14:27:44 +0200255 spin_lock_bh(&bat_priv->tt_changes_list_lock);
256 atomic_set(&bat_priv->tt_local_changes, 0);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000257
Antonio Quartullia73105b2011-04-27 14:27:44 +0200258 list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list,
259 list) {
260 if (count < tot_changes) {
261 memcpy(buff + tt_len(count),
262 &entry->change, sizeof(struct tt_change));
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000263 count++;
264 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200265 list_del(&entry->list);
266 kfree(entry);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000267 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200268 spin_unlock_bh(&bat_priv->tt_changes_list_lock);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000269
Antonio Quartullia73105b2011-04-27 14:27:44 +0200270 /* Keep the buffer for possible tt_request */
271 spin_lock_bh(&bat_priv->tt_buff_lock);
272 kfree(bat_priv->tt_buff);
273 bat_priv->tt_buff_len = 0;
274 bat_priv->tt_buff = NULL;
275 /* We check whether this new OGM has no changes due to size
276 * problems */
277 if (buff_len > 0) {
278 /**
279 * if kmalloc() fails we will reply with the full table
280 * instead of providing the diff
281 */
282 bat_priv->tt_buff = kmalloc(buff_len, GFP_ATOMIC);
283 if (bat_priv->tt_buff) {
284 memcpy(bat_priv->tt_buff, buff, buff_len);
285 bat_priv->tt_buff_len = buff_len;
286 }
287 }
288 spin_unlock_bh(&bat_priv->tt_buff_lock);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000289
Antonio Quartullia73105b2011-04-27 14:27:44 +0200290 return tot_changes;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000291}
292
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200293int tt_local_seq_print_text(struct seq_file *seq, void *offset)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000294{
295 struct net_device *net_dev = (struct net_device *)seq->private;
296 struct bat_priv *bat_priv = netdev_priv(net_dev);
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200297 struct hashtable_t *hash = bat_priv->tt_local_hash;
298 struct tt_local_entry *tt_local_entry;
Marek Lindner32ae9b22011-04-20 15:40:58 +0200299 struct hard_iface *primary_if;
Marek Lindner7aadf882011-02-18 12:28:09 +0000300 struct hlist_node *node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000301 struct hlist_head *head;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000302 size_t buf_size, pos;
303 char *buff;
Marek Lindner32ae9b22011-04-20 15:40:58 +0200304 int i, ret = 0;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000305
Marek Lindner32ae9b22011-04-20 15:40:58 +0200306 primary_if = primary_if_get_selected(bat_priv);
307 if (!primary_if) {
308 ret = seq_printf(seq, "BATMAN mesh %s disabled - "
309 "please specify interfaces to enable it\n",
310 net_dev->name);
311 goto out;
312 }
313
314 if (primary_if->if_status != IF_ACTIVE) {
315 ret = seq_printf(seq, "BATMAN mesh %s disabled - "
316 "primary interface not active\n",
317 net_dev->name);
318 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000319 }
320
321 seq_printf(seq, "Locally retrieved addresses (from %s) "
Antonio Quartullia73105b2011-04-27 14:27:44 +0200322 "announced via TT (TTVN: %u):\n",
323 net_dev->name, (uint8_t)atomic_read(&bat_priv->ttvn));
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000324
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000325 buf_size = 1;
326 /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */
327 for (i = 0; i < hash->size; i++) {
328 head = &hash->table[i];
329
Marek Lindner7aadf882011-02-18 12:28:09 +0000330 rcu_read_lock();
331 __hlist_for_each_rcu(node, head)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000332 buf_size += 21;
Marek Lindner7aadf882011-02-18 12:28:09 +0000333 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000334 }
335
336 buff = kmalloc(buf_size, GFP_ATOMIC);
337 if (!buff) {
Marek Lindner32ae9b22011-04-20 15:40:58 +0200338 ret = -ENOMEM;
339 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000340 }
Marek Lindner7aadf882011-02-18 12:28:09 +0000341
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000342 buff[0] = '\0';
343 pos = 0;
344
345 for (i = 0; i < hash->size; i++) {
346 head = &hash->table[i];
347
Marek Lindner7aadf882011-02-18 12:28:09 +0000348 rcu_read_lock();
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200349 hlist_for_each_entry_rcu(tt_local_entry, node,
Marek Lindner7aadf882011-02-18 12:28:09 +0000350 head, hash_entry) {
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000351 pos += snprintf(buff + pos, 22, " * %pM\n",
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200352 tt_local_entry->addr);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000353 }
Marek Lindner7aadf882011-02-18 12:28:09 +0000354 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000355 }
356
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000357 seq_printf(seq, "%s", buff);
358 kfree(buff);
Marek Lindner32ae9b22011-04-20 15:40:58 +0200359out:
360 if (primary_if)
361 hardif_free_ref(primary_if);
362 return ret;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000363}
364
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200365static void tt_local_del(struct bat_priv *bat_priv,
Sven Eckelmann747e4222011-05-14 23:14:50 +0200366 struct tt_local_entry *tt_local_entry,
367 const char *message)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000368{
Antonio Quartullia73105b2011-04-27 14:27:44 +0200369 bat_dbg(DBG_TT, bat_priv, "Deleting local tt entry (%pM): %s\n",
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200370 tt_local_entry->addr, message);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000371
Antonio Quartullia73105b2011-04-27 14:27:44 +0200372 atomic_dec(&bat_priv->num_local_tt);
373
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200374 hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig,
375 tt_local_entry->addr);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200376
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200377 tt_local_entry_free_ref(tt_local_entry);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000378}
379
Antonio Quartullia73105b2011-04-27 14:27:44 +0200380void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
Antonio Quartullicc47f662011-04-27 14:27:57 +0200381 const char *message, bool roaming)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000382{
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200383 struct tt_local_entry *tt_local_entry = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000384
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200385 tt_local_entry = tt_local_hash_find(bat_priv, addr);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000386
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200387 if (!tt_local_entry)
388 goto out;
389
390 tt_local_event(bat_priv, TT_CHANGE_DEL, tt_local_entry->addr, roaming);
391 tt_local_del(bat_priv, tt_local_entry, message);
392out:
393 if (tt_local_entry)
394 tt_local_entry_free_ref(tt_local_entry);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000395}
396
Antonio Quartullia73105b2011-04-27 14:27:44 +0200397static void tt_local_purge(struct bat_priv *bat_priv)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000398{
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200399 struct hashtable_t *hash = bat_priv->tt_local_hash;
400 struct tt_local_entry *tt_local_entry;
Marek Lindner7aadf882011-02-18 12:28:09 +0000401 struct hlist_node *node, *node_tmp;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000402 struct hlist_head *head;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200403 spinlock_t *list_lock; /* protects write access to the hash lists */
Marek Lindner7aadf882011-02-18 12:28:09 +0000404 int i;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000405
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000406 for (i = 0; i < hash->size; i++) {
407 head = &hash->table[i];
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200408 list_lock = &hash->list_locks[i];
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000409
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200410 spin_lock_bh(list_lock);
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200411 hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
Marek Lindner7aadf882011-02-18 12:28:09 +0000412 head, hash_entry) {
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200413 if (tt_local_entry->never_purge)
Marek Lindner7aadf882011-02-18 12:28:09 +0000414 continue;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000415
Antonio Quartullia73105b2011-04-27 14:27:44 +0200416 if (!is_out_of_time(tt_local_entry->last_seen,
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200417 TT_LOCAL_TIMEOUT * 1000))
Marek Lindner7aadf882011-02-18 12:28:09 +0000418 continue;
419
Antonio Quartullia73105b2011-04-27 14:27:44 +0200420 tt_local_event(bat_priv, TT_CHANGE_DEL,
Antonio Quartullicc47f662011-04-27 14:27:57 +0200421 tt_local_entry->addr, false);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200422 atomic_dec(&bat_priv->num_local_tt);
423 bat_dbg(DBG_TT, bat_priv, "Deleting local "
424 "tt entry (%pM): timed out\n",
425 tt_local_entry->addr);
426 hlist_del_rcu(node);
427 tt_local_entry_free_ref(tt_local_entry);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000428 }
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200429 spin_unlock_bh(list_lock);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000430 }
431
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000432}
433
Antonio Quartullia73105b2011-04-27 14:27:44 +0200434static void tt_local_table_free(struct bat_priv *bat_priv)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000435{
Antonio Quartullia73105b2011-04-27 14:27:44 +0200436 struct hashtable_t *hash;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200437 spinlock_t *list_lock; /* protects write access to the hash lists */
Antonio Quartullia73105b2011-04-27 14:27:44 +0200438 struct tt_local_entry *tt_local_entry;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200439 struct hlist_node *node, *node_tmp;
440 struct hlist_head *head;
441 int i;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200442
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200443 if (!bat_priv->tt_local_hash)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000444 return;
445
Antonio Quartullia73105b2011-04-27 14:27:44 +0200446 hash = bat_priv->tt_local_hash;
447
448 for (i = 0; i < hash->size; i++) {
449 head = &hash->table[i];
450 list_lock = &hash->list_locks[i];
451
452 spin_lock_bh(list_lock);
453 hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
454 head, hash_entry) {
455 hlist_del_rcu(node);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200456 tt_local_entry_free_ref(tt_local_entry);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200457 }
458 spin_unlock_bh(list_lock);
459 }
460
461 hash_destroy(hash);
462
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200463 bat_priv->tt_local_hash = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000464}
465
Antonio Quartullia73105b2011-04-27 14:27:44 +0200466static int tt_global_init(struct bat_priv *bat_priv)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000467{
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200468 if (bat_priv->tt_global_hash)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000469 return 1;
470
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200471 bat_priv->tt_global_hash = hash_new(1024);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000472
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200473 if (!bat_priv->tt_global_hash)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000474 return 0;
475
476 return 1;
477}
478
Antonio Quartullia73105b2011-04-27 14:27:44 +0200479static void tt_changes_list_free(struct bat_priv *bat_priv)
480{
481 struct tt_change_node *entry, *safe;
482
483 spin_lock_bh(&bat_priv->tt_changes_list_lock);
484
485 list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list,
486 list) {
487 list_del(&entry->list);
488 kfree(entry);
489 }
490
491 atomic_set(&bat_priv->tt_local_changes, 0);
492 spin_unlock_bh(&bat_priv->tt_changes_list_lock);
493}
494
495/* caller must hold orig_node refcount */
496int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
Antonio Quartullicc47f662011-04-27 14:27:57 +0200497 const unsigned char *tt_addr, uint8_t ttvn, bool roaming)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000498{
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200499 struct tt_global_entry *tt_global_entry;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200500 struct orig_node *orig_node_tmp;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200501 int ret = 0;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000502
Antonio Quartullia73105b2011-04-27 14:27:44 +0200503 tt_global_entry = tt_global_hash_find(bat_priv, tt_addr);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000504
Antonio Quartullia73105b2011-04-27 14:27:44 +0200505 if (!tt_global_entry) {
506 tt_global_entry =
507 kmalloc(sizeof(*tt_global_entry),
508 GFP_ATOMIC);
509 if (!tt_global_entry)
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200510 goto out;
511
Antonio Quartullia73105b2011-04-27 14:27:44 +0200512 memcpy(tt_global_entry->addr, tt_addr, ETH_ALEN);
513 /* Assign the new orig_node */
514 atomic_inc(&orig_node->refcount);
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200515 tt_global_entry->orig_node = orig_node;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200516 tt_global_entry->ttvn = ttvn;
Antonio Quartullicc47f662011-04-27 14:27:57 +0200517 tt_global_entry->flags = NO_FLAGS;
518 tt_global_entry->roam_at = 0;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200519 atomic_set(&tt_global_entry->refcount, 2);
520
Antonio Quartullia73105b2011-04-27 14:27:44 +0200521 hash_add(bat_priv->tt_global_hash, compare_gtt,
522 choose_orig, tt_global_entry,
523 &tt_global_entry->hash_entry);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200524 atomic_inc(&orig_node->tt_size);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200525 } else {
526 if (tt_global_entry->orig_node != orig_node) {
527 atomic_dec(&tt_global_entry->orig_node->tt_size);
528 orig_node_tmp = tt_global_entry->orig_node;
529 atomic_inc(&orig_node->refcount);
530 tt_global_entry->orig_node = orig_node;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200531 orig_node_free_ref(orig_node_tmp);
532 atomic_inc(&orig_node->tt_size);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000533 }
Antonio Quartullicc47f662011-04-27 14:27:57 +0200534 tt_global_entry->ttvn = ttvn;
535 tt_global_entry->flags = NO_FLAGS;
536 tt_global_entry->roam_at = 0;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000537 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200538
Antonio Quartullia73105b2011-04-27 14:27:44 +0200539 bat_dbg(DBG_TT, bat_priv,
540 "Creating new global tt entry: %pM (via %pM)\n",
541 tt_global_entry->addr, orig_node->orig);
542
543 /* remove address from local hash if present */
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200544 tt_local_remove(bat_priv, tt_global_entry->addr,
545 "global tt received", roaming);
546 ret = 1;
547out:
548 if (tt_global_entry)
549 tt_global_entry_free_ref(tt_global_entry);
550 return ret;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000551}
552
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200553int tt_global_seq_print_text(struct seq_file *seq, void *offset)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000554{
555 struct net_device *net_dev = (struct net_device *)seq->private;
556 struct bat_priv *bat_priv = netdev_priv(net_dev);
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200557 struct hashtable_t *hash = bat_priv->tt_global_hash;
558 struct tt_global_entry *tt_global_entry;
Marek Lindner32ae9b22011-04-20 15:40:58 +0200559 struct hard_iface *primary_if;
Marek Lindner7aadf882011-02-18 12:28:09 +0000560 struct hlist_node *node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000561 struct hlist_head *head;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000562 size_t buf_size, pos;
563 char *buff;
Marek Lindner32ae9b22011-04-20 15:40:58 +0200564 int i, ret = 0;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000565
Marek Lindner32ae9b22011-04-20 15:40:58 +0200566 primary_if = primary_if_get_selected(bat_priv);
567 if (!primary_if) {
568 ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
569 "specify interfaces to enable it\n",
570 net_dev->name);
571 goto out;
572 }
573
574 if (primary_if->if_status != IF_ACTIVE) {
575 ret = seq_printf(seq, "BATMAN mesh %s disabled - "
576 "primary interface not active\n",
577 net_dev->name);
578 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000579 }
580
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200581 seq_printf(seq,
582 "Globally announced TT entries received via the mesh %s\n",
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000583 net_dev->name);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200584 seq_printf(seq, " %-13s %s %-15s %s\n",
585 "Client", "(TTVN)", "Originator", "(Curr TTVN)");
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000586
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000587 buf_size = 1;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200588 /* Estimate length for: " * xx:xx:xx:xx:xx:xx (ttvn) via
589 * xx:xx:xx:xx:xx:xx (cur_ttvn)\n"*/
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000590 for (i = 0; i < hash->size; i++) {
591 head = &hash->table[i];
592
Marek Lindner7aadf882011-02-18 12:28:09 +0000593 rcu_read_lock();
594 __hlist_for_each_rcu(node, head)
Antonio Quartullia73105b2011-04-27 14:27:44 +0200595 buf_size += 59;
Marek Lindner7aadf882011-02-18 12:28:09 +0000596 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000597 }
598
599 buff = kmalloc(buf_size, GFP_ATOMIC);
600 if (!buff) {
Marek Lindner32ae9b22011-04-20 15:40:58 +0200601 ret = -ENOMEM;
602 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000603 }
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200604
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000605 buff[0] = '\0';
606 pos = 0;
607
608 for (i = 0; i < hash->size; i++) {
609 head = &hash->table[i];
610
Marek Lindner7aadf882011-02-18 12:28:09 +0000611 rcu_read_lock();
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200612 hlist_for_each_entry_rcu(tt_global_entry, node,
Marek Lindner7aadf882011-02-18 12:28:09 +0000613 head, hash_entry) {
Antonio Quartullia73105b2011-04-27 14:27:44 +0200614 pos += snprintf(buff + pos, 61,
615 " * %pM (%3u) via %pM (%3u)\n",
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200616 tt_global_entry->addr,
Antonio Quartullia73105b2011-04-27 14:27:44 +0200617 tt_global_entry->ttvn,
618 tt_global_entry->orig_node->orig,
619 (uint8_t) atomic_read(
620 &tt_global_entry->orig_node->
621 last_ttvn));
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000622 }
Marek Lindner7aadf882011-02-18 12:28:09 +0000623 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000624 }
625
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000626 seq_printf(seq, "%s", buff);
627 kfree(buff);
Marek Lindner32ae9b22011-04-20 15:40:58 +0200628out:
629 if (primary_if)
630 hardif_free_ref(primary_if);
631 return ret;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000632}
633
Antonio Quartullia73105b2011-04-27 14:27:44 +0200634static void _tt_global_del(struct bat_priv *bat_priv,
635 struct tt_global_entry *tt_global_entry,
636 const char *message)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000637{
Antonio Quartullia73105b2011-04-27 14:27:44 +0200638 if (!tt_global_entry)
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200639 goto out;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200640
641 bat_dbg(DBG_TT, bat_priv,
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200642 "Deleting global tt entry %pM (via %pM): %s\n",
643 tt_global_entry->addr, tt_global_entry->orig_node->orig,
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000644 message);
645
Antonio Quartullia73105b2011-04-27 14:27:44 +0200646 atomic_dec(&tt_global_entry->orig_node->tt_size);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200647
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200648 hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig,
649 tt_global_entry->addr);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200650out:
651 if (tt_global_entry)
652 tt_global_entry_free_ref(tt_global_entry);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000653}
654
Antonio Quartullia73105b2011-04-27 14:27:44 +0200655void tt_global_del(struct bat_priv *bat_priv,
656 struct orig_node *orig_node, const unsigned char *addr,
Antonio Quartullicc47f662011-04-27 14:27:57 +0200657 const char *message, bool roaming)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000658{
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200659 struct tt_global_entry *tt_global_entry = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000660
Antonio Quartullia73105b2011-04-27 14:27:44 +0200661 tt_global_entry = tt_global_hash_find(bat_priv, addr);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200662 if (!tt_global_entry)
663 goto out;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200664
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200665 if (tt_global_entry->orig_node == orig_node) {
Antonio Quartullicc47f662011-04-27 14:27:57 +0200666 if (roaming) {
667 tt_global_entry->flags |= TT_CLIENT_ROAM;
668 tt_global_entry->roam_at = jiffies;
669 goto out;
670 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200671 _tt_global_del(bat_priv, tt_global_entry, message);
672 }
Antonio Quartullicc47f662011-04-27 14:27:57 +0200673out:
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200674 if (tt_global_entry)
675 tt_global_entry_free_ref(tt_global_entry);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200676}
677
678void tt_global_del_orig(struct bat_priv *bat_priv,
679 struct orig_node *orig_node, const char *message)
680{
681 struct tt_global_entry *tt_global_entry;
682 int i;
683 struct hashtable_t *hash = bat_priv->tt_global_hash;
684 struct hlist_node *node, *safe;
685 struct hlist_head *head;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200686 spinlock_t *list_lock; /* protects write access to the hash lists */
Antonio Quartullia73105b2011-04-27 14:27:44 +0200687
Antonio Quartullia73105b2011-04-27 14:27:44 +0200688 for (i = 0; i < hash->size; i++) {
689 head = &hash->table[i];
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200690 list_lock = &hash->list_locks[i];
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000691
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200692 spin_lock_bh(list_lock);
Antonio Quartullia73105b2011-04-27 14:27:44 +0200693 hlist_for_each_entry_safe(tt_global_entry, node, safe,
694 head, hash_entry) {
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200695 if (tt_global_entry->orig_node == orig_node) {
696 bat_dbg(DBG_TT, bat_priv,
697 "Deleting global tt entry %pM "
698 "(via %pM): originator time out\n",
699 tt_global_entry->addr,
700 tt_global_entry->orig_node->orig);
701 hlist_del_rcu(node);
702 tt_global_entry_free_ref(tt_global_entry);
703 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200704 }
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200705 spin_unlock_bh(list_lock);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000706 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200707 atomic_set(&orig_node->tt_size, 0);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000708}
709
Antonio Quartullicc47f662011-04-27 14:27:57 +0200710static void tt_global_roam_purge(struct bat_priv *bat_priv)
711{
712 struct hashtable_t *hash = bat_priv->tt_global_hash;
713 struct tt_global_entry *tt_global_entry;
714 struct hlist_node *node, *node_tmp;
715 struct hlist_head *head;
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200716 spinlock_t *list_lock; /* protects write access to the hash lists */
Antonio Quartullicc47f662011-04-27 14:27:57 +0200717 int i;
718
Antonio Quartullicc47f662011-04-27 14:27:57 +0200719 for (i = 0; i < hash->size; i++) {
720 head = &hash->table[i];
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200721 list_lock = &hash->list_locks[i];
Antonio Quartullicc47f662011-04-27 14:27:57 +0200722
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200723 spin_lock_bh(list_lock);
Antonio Quartullicc47f662011-04-27 14:27:57 +0200724 hlist_for_each_entry_safe(tt_global_entry, node, node_tmp,
725 head, hash_entry) {
726 if (!(tt_global_entry->flags & TT_CLIENT_ROAM))
727 continue;
728 if (!is_out_of_time(tt_global_entry->roam_at,
729 TT_CLIENT_ROAM_TIMEOUT * 1000))
730 continue;
731
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200732 bat_dbg(DBG_TT, bat_priv, "Deleting global "
733 "tt entry (%pM): Roaming timeout\n",
734 tt_global_entry->addr);
735 atomic_dec(&tt_global_entry->orig_node->tt_size);
736 hlist_del_rcu(node);
737 tt_global_entry_free_ref(tt_global_entry);
Antonio Quartullicc47f662011-04-27 14:27:57 +0200738 }
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200739 spin_unlock_bh(list_lock);
Antonio Quartullicc47f662011-04-27 14:27:57 +0200740 }
741
Antonio Quartullicc47f662011-04-27 14:27:57 +0200742}
743
Antonio Quartullia73105b2011-04-27 14:27:44 +0200744static void tt_global_table_free(struct bat_priv *bat_priv)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000745{
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200746 struct hashtable_t *hash;
747 spinlock_t *list_lock; /* protects write access to the hash lists */
748 struct tt_global_entry *tt_global_entry;
749 struct hlist_node *node, *node_tmp;
750 struct hlist_head *head;
751 int i;
752
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200753 if (!bat_priv->tt_global_hash)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000754 return;
755
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200756 hash = bat_priv->tt_global_hash;
757
758 for (i = 0; i < hash->size; i++) {
759 head = &hash->table[i];
760 list_lock = &hash->list_locks[i];
761
762 spin_lock_bh(list_lock);
763 hlist_for_each_entry_safe(tt_global_entry, node, node_tmp,
764 head, hash_entry) {
765 hlist_del_rcu(node);
766 tt_global_entry_free_ref(tt_global_entry);
767 }
768 spin_unlock_bh(list_lock);
769 }
770
771 hash_destroy(hash);
772
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200773 bat_priv->tt_global_hash = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000774}
775
Sven Eckelmann747e4222011-05-14 23:14:50 +0200776struct orig_node *transtable_search(struct bat_priv *bat_priv,
777 const uint8_t *addr)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000778{
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200779 struct tt_global_entry *tt_global_entry;
Marek Lindner7b36e8e2011-02-18 12:28:10 +0000780 struct orig_node *orig_node = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000781
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200782 tt_global_entry = tt_global_hash_find(bat_priv, addr);
Marek Lindner7aadf882011-02-18 12:28:09 +0000783
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200784 if (!tt_global_entry)
Marek Lindner7b36e8e2011-02-18 12:28:10 +0000785 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000786
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200787 if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount))
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200788 goto free_tt;
Marek Lindner7b36e8e2011-02-18 12:28:10 +0000789
Antonio Quartulli2dafb492011-05-05 08:42:45 +0200790 orig_node = tt_global_entry->orig_node;
Marek Lindner7b36e8e2011-02-18 12:28:10 +0000791
Antonio Quartulli7683fdc2011-04-27 14:28:07 +0200792free_tt:
793 tt_global_entry_free_ref(tt_global_entry);
Marek Lindner7b36e8e2011-02-18 12:28:10 +0000794out:
Marek Lindner7b36e8e2011-02-18 12:28:10 +0000795 return orig_node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000796}
Antonio Quartullia73105b2011-04-27 14:27:44 +0200797
798/* Calculates the checksum of the local table of a given orig_node */
799uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
800{
801 uint16_t total = 0, total_one;
802 struct hashtable_t *hash = bat_priv->tt_global_hash;
803 struct tt_global_entry *tt_global_entry;
804 struct hlist_node *node;
805 struct hlist_head *head;
806 int i, j;
807
808 for (i = 0; i < hash->size; i++) {
809 head = &hash->table[i];
810
811 rcu_read_lock();
812 hlist_for_each_entry_rcu(tt_global_entry, node,
813 head, hash_entry) {
814 if (compare_eth(tt_global_entry->orig_node,
815 orig_node)) {
Antonio Quartullicc47f662011-04-27 14:27:57 +0200816 /* Roaming clients are in the global table for
817 * consistency only. They don't have to be
818 * taken into account while computing the
819 * global crc */
820 if (tt_global_entry->flags & TT_CLIENT_ROAM)
821 continue;
Antonio Quartullia73105b2011-04-27 14:27:44 +0200822 total_one = 0;
823 for (j = 0; j < ETH_ALEN; j++)
824 total_one = crc16_byte(total_one,
825 tt_global_entry->addr[j]);
826 total ^= total_one;
827 }
828 }
829 rcu_read_unlock();
830 }
831
832 return total;
833}
834
835/* Calculates the checksum of the local table */
836uint16_t tt_local_crc(struct bat_priv *bat_priv)
837{
838 uint16_t total = 0, total_one;
839 struct hashtable_t *hash = bat_priv->tt_local_hash;
840 struct tt_local_entry *tt_local_entry;
841 struct hlist_node *node;
842 struct hlist_head *head;
843 int i, j;
844
845 for (i = 0; i < hash->size; i++) {
846 head = &hash->table[i];
847
848 rcu_read_lock();
849 hlist_for_each_entry_rcu(tt_local_entry, node,
850 head, hash_entry) {
851 total_one = 0;
852 for (j = 0; j < ETH_ALEN; j++)
853 total_one = crc16_byte(total_one,
854 tt_local_entry->addr[j]);
855 total ^= total_one;
856 }
Antonio Quartullia73105b2011-04-27 14:27:44 +0200857 rcu_read_unlock();
858 }
859
860 return total;
861}
862
863static void tt_req_list_free(struct bat_priv *bat_priv)
864{
865 struct tt_req_node *node, *safe;
866
867 spin_lock_bh(&bat_priv->tt_req_list_lock);
868
869 list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) {
870 list_del(&node->list);
871 kfree(node);
872 }
873
874 spin_unlock_bh(&bat_priv->tt_req_list_lock);
875}
876
877void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node,
878 const unsigned char *tt_buff, uint8_t tt_num_changes)
879{
880 uint16_t tt_buff_len = tt_len(tt_num_changes);
881
882 /* Replace the old buffer only if I received something in the
883 * last OGM (the OGM could carry no changes) */
884 spin_lock_bh(&orig_node->tt_buff_lock);
885 if (tt_buff_len > 0) {
886 kfree(orig_node->tt_buff);
887 orig_node->tt_buff_len = 0;
888 orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);
889 if (orig_node->tt_buff) {
890 memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);
891 orig_node->tt_buff_len = tt_buff_len;
892 }
893 }
894 spin_unlock_bh(&orig_node->tt_buff_lock);
895}
896
897static void tt_req_purge(struct bat_priv *bat_priv)
898{
899 struct tt_req_node *node, *safe;
900
901 spin_lock_bh(&bat_priv->tt_req_list_lock);
902 list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) {
903 if (is_out_of_time(node->issued_at,
904 TT_REQUEST_TIMEOUT * 1000)) {
905 list_del(&node->list);
906 kfree(node);
907 }
908 }
909 spin_unlock_bh(&bat_priv->tt_req_list_lock);
910}
911
912/* returns the pointer to the new tt_req_node struct if no request
913 * has already been issued for this orig_node, NULL otherwise */
914static struct tt_req_node *new_tt_req_node(struct bat_priv *bat_priv,
915 struct orig_node *orig_node)
916{
917 struct tt_req_node *tt_req_node_tmp, *tt_req_node = NULL;
918
919 spin_lock_bh(&bat_priv->tt_req_list_lock);
920 list_for_each_entry(tt_req_node_tmp, &bat_priv->tt_req_list, list) {
921 if (compare_eth(tt_req_node_tmp, orig_node) &&
922 !is_out_of_time(tt_req_node_tmp->issued_at,
923 TT_REQUEST_TIMEOUT * 1000))
924 goto unlock;
925 }
926
927 tt_req_node = kmalloc(sizeof(*tt_req_node), GFP_ATOMIC);
928 if (!tt_req_node)
929 goto unlock;
930
931 memcpy(tt_req_node->addr, orig_node->orig, ETH_ALEN);
932 tt_req_node->issued_at = jiffies;
933
934 list_add(&tt_req_node->list, &bat_priv->tt_req_list);
935unlock:
936 spin_unlock_bh(&bat_priv->tt_req_list_lock);
937 return tt_req_node;
938}
939
940static int tt_global_valid_entry(const void *entry_ptr, const void *data_ptr)
941{
942 const struct tt_global_entry *tt_global_entry = entry_ptr;
943 const struct orig_node *orig_node = data_ptr;
944
Antonio Quartullicc47f662011-04-27 14:27:57 +0200945 if (tt_global_entry->flags & TT_CLIENT_ROAM)
946 return 0;
947
Antonio Quartullia73105b2011-04-27 14:27:44 +0200948 return (tt_global_entry->orig_node == orig_node);
949}
950
951static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
952 struct hashtable_t *hash,
953 struct hard_iface *primary_if,
954 int (*valid_cb)(const void *,
955 const void *),
956 void *cb_data)
957{
958 struct tt_local_entry *tt_local_entry;
959 struct tt_query_packet *tt_response;
960 struct tt_change *tt_change;
961 struct hlist_node *node;
962 struct hlist_head *head;
963 struct sk_buff *skb = NULL;
964 uint16_t tt_tot, tt_count;
965 ssize_t tt_query_size = sizeof(struct tt_query_packet);
966 int i;
967
968 if (tt_query_size + tt_len > primary_if->soft_iface->mtu) {
969 tt_len = primary_if->soft_iface->mtu - tt_query_size;
970 tt_len -= tt_len % sizeof(struct tt_change);
971 }
972 tt_tot = tt_len / sizeof(struct tt_change);
973
974 skb = dev_alloc_skb(tt_query_size + tt_len + ETH_HLEN);
975 if (!skb)
976 goto out;
977
978 skb_reserve(skb, ETH_HLEN);
979 tt_response = (struct tt_query_packet *)skb_put(skb,
980 tt_query_size + tt_len);
981 tt_response->ttvn = ttvn;
982 tt_response->tt_data = htons(tt_tot);
983
984 tt_change = (struct tt_change *)(skb->data + tt_query_size);
985 tt_count = 0;
986
987 rcu_read_lock();
988 for (i = 0; i < hash->size; i++) {
989 head = &hash->table[i];
990
991 hlist_for_each_entry_rcu(tt_local_entry, node,
992 head, hash_entry) {
993 if (tt_count == tt_tot)
994 break;
995
996 if ((valid_cb) && (!valid_cb(tt_local_entry, cb_data)))
997 continue;
998
999 memcpy(tt_change->addr, tt_local_entry->addr, ETH_ALEN);
1000 tt_change->flags = NO_FLAGS;
1001
1002 tt_count++;
1003 tt_change++;
1004 }
1005 }
1006 rcu_read_unlock();
1007
1008out:
1009 return skb;
1010}
1011
1012int send_tt_request(struct bat_priv *bat_priv, struct orig_node *dst_orig_node,
1013 uint8_t ttvn, uint16_t tt_crc, bool full_table)
1014{
1015 struct sk_buff *skb = NULL;
1016 struct tt_query_packet *tt_request;
1017 struct neigh_node *neigh_node = NULL;
1018 struct hard_iface *primary_if;
1019 struct tt_req_node *tt_req_node = NULL;
1020 int ret = 1;
1021
1022 primary_if = primary_if_get_selected(bat_priv);
1023 if (!primary_if)
1024 goto out;
1025
1026 /* The new tt_req will be issued only if I'm not waiting for a
1027 * reply from the same orig_node yet */
1028 tt_req_node = new_tt_req_node(bat_priv, dst_orig_node);
1029 if (!tt_req_node)
1030 goto out;
1031
1032 skb = dev_alloc_skb(sizeof(struct tt_query_packet) + ETH_HLEN);
1033 if (!skb)
1034 goto out;
1035
1036 skb_reserve(skb, ETH_HLEN);
1037
1038 tt_request = (struct tt_query_packet *)skb_put(skb,
1039 sizeof(struct tt_query_packet));
1040
1041 tt_request->packet_type = BAT_TT_QUERY;
1042 tt_request->version = COMPAT_VERSION;
1043 memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN);
1044 memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN);
1045 tt_request->ttl = TTL;
1046 tt_request->ttvn = ttvn;
1047 tt_request->tt_data = tt_crc;
1048 tt_request->flags = TT_REQUEST;
1049
1050 if (full_table)
1051 tt_request->flags |= TT_FULL_TABLE;
1052
1053 neigh_node = orig_node_get_router(dst_orig_node);
1054 if (!neigh_node)
1055 goto out;
1056
1057 bat_dbg(DBG_TT, bat_priv, "Sending TT_REQUEST to %pM via %pM "
1058 "[%c]\n", dst_orig_node->orig, neigh_node->addr,
1059 (full_table ? 'F' : '.'));
1060
1061 send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
1062 ret = 0;
1063
1064out:
1065 if (neigh_node)
1066 neigh_node_free_ref(neigh_node);
1067 if (primary_if)
1068 hardif_free_ref(primary_if);
1069 if (ret)
1070 kfree_skb(skb);
1071 if (ret && tt_req_node) {
1072 spin_lock_bh(&bat_priv->tt_req_list_lock);
1073 list_del(&tt_req_node->list);
1074 spin_unlock_bh(&bat_priv->tt_req_list_lock);
1075 kfree(tt_req_node);
1076 }
1077 return ret;
1078}
1079
1080static bool send_other_tt_response(struct bat_priv *bat_priv,
1081 struct tt_query_packet *tt_request)
1082{
1083 struct orig_node *req_dst_orig_node = NULL, *res_dst_orig_node = NULL;
1084 struct neigh_node *neigh_node = NULL;
1085 struct hard_iface *primary_if = NULL;
1086 uint8_t orig_ttvn, req_ttvn, ttvn;
1087 int ret = false;
1088 unsigned char *tt_buff;
1089 bool full_table;
1090 uint16_t tt_len, tt_tot;
1091 struct sk_buff *skb = NULL;
1092 struct tt_query_packet *tt_response;
1093
1094 bat_dbg(DBG_TT, bat_priv,
1095 "Received TT_REQUEST from %pM for "
1096 "ttvn: %u (%pM) [%c]\n", tt_request->src,
1097 tt_request->ttvn, tt_request->dst,
1098 (tt_request->flags & TT_FULL_TABLE ? 'F' : '.'));
1099
1100 /* Let's get the orig node of the REAL destination */
1101 req_dst_orig_node = get_orig_node(bat_priv, tt_request->dst);
1102 if (!req_dst_orig_node)
1103 goto out;
1104
1105 res_dst_orig_node = get_orig_node(bat_priv, tt_request->src);
1106 if (!res_dst_orig_node)
1107 goto out;
1108
1109 neigh_node = orig_node_get_router(res_dst_orig_node);
1110 if (!neigh_node)
1111 goto out;
1112
1113 primary_if = primary_if_get_selected(bat_priv);
1114 if (!primary_if)
1115 goto out;
1116
1117 orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
1118 req_ttvn = tt_request->ttvn;
1119
1120 /* I have not the requested data */
1121 if (orig_ttvn != req_ttvn ||
1122 tt_request->tt_data != req_dst_orig_node->tt_crc)
1123 goto out;
1124
1125 /* If it has explicitly been requested the full table */
1126 if (tt_request->flags & TT_FULL_TABLE ||
1127 !req_dst_orig_node->tt_buff)
1128 full_table = true;
1129 else
1130 full_table = false;
1131
1132 /* In this version, fragmentation is not implemented, then
1133 * I'll send only one packet with as much TT entries as I can */
1134 if (!full_table) {
1135 spin_lock_bh(&req_dst_orig_node->tt_buff_lock);
1136 tt_len = req_dst_orig_node->tt_buff_len;
1137 tt_tot = tt_len / sizeof(struct tt_change);
1138
1139 skb = dev_alloc_skb(sizeof(struct tt_query_packet) +
1140 tt_len + ETH_HLEN);
1141 if (!skb)
1142 goto unlock;
1143
1144 skb_reserve(skb, ETH_HLEN);
1145 tt_response = (struct tt_query_packet *)skb_put(skb,
1146 sizeof(struct tt_query_packet) + tt_len);
1147 tt_response->ttvn = req_ttvn;
1148 tt_response->tt_data = htons(tt_tot);
1149
1150 tt_buff = skb->data + sizeof(struct tt_query_packet);
1151 /* Copy the last orig_node's OGM buffer */
1152 memcpy(tt_buff, req_dst_orig_node->tt_buff,
1153 req_dst_orig_node->tt_buff_len);
1154
1155 spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
1156 } else {
1157 tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size) *
1158 sizeof(struct tt_change);
1159 ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
1160
1161 skb = tt_response_fill_table(tt_len, ttvn,
1162 bat_priv->tt_global_hash,
1163 primary_if, tt_global_valid_entry,
1164 req_dst_orig_node);
1165 if (!skb)
1166 goto out;
1167
1168 tt_response = (struct tt_query_packet *)skb->data;
1169 }
1170
1171 tt_response->packet_type = BAT_TT_QUERY;
1172 tt_response->version = COMPAT_VERSION;
1173 tt_response->ttl = TTL;
1174 memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN);
1175 memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
1176 tt_response->flags = TT_RESPONSE;
1177
1178 if (full_table)
1179 tt_response->flags |= TT_FULL_TABLE;
1180
1181 bat_dbg(DBG_TT, bat_priv,
1182 "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n",
1183 res_dst_orig_node->orig, neigh_node->addr,
1184 req_dst_orig_node->orig, req_ttvn);
1185
1186 send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
1187 ret = true;
1188 goto out;
1189
1190unlock:
1191 spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
1192
1193out:
1194 if (res_dst_orig_node)
1195 orig_node_free_ref(res_dst_orig_node);
1196 if (req_dst_orig_node)
1197 orig_node_free_ref(req_dst_orig_node);
1198 if (neigh_node)
1199 neigh_node_free_ref(neigh_node);
1200 if (primary_if)
1201 hardif_free_ref(primary_if);
1202 if (!ret)
1203 kfree_skb(skb);
1204 return ret;
1205
1206}
1207static bool send_my_tt_response(struct bat_priv *bat_priv,
1208 struct tt_query_packet *tt_request)
1209{
1210 struct orig_node *orig_node = NULL;
1211 struct neigh_node *neigh_node = NULL;
1212 struct hard_iface *primary_if = NULL;
1213 uint8_t my_ttvn, req_ttvn, ttvn;
1214 int ret = false;
1215 unsigned char *tt_buff;
1216 bool full_table;
1217 uint16_t tt_len, tt_tot;
1218 struct sk_buff *skb = NULL;
1219 struct tt_query_packet *tt_response;
1220
1221 bat_dbg(DBG_TT, bat_priv,
1222 "Received TT_REQUEST from %pM for "
1223 "ttvn: %u (me) [%c]\n", tt_request->src,
1224 tt_request->ttvn,
1225 (tt_request->flags & TT_FULL_TABLE ? 'F' : '.'));
1226
1227
1228 my_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn);
1229 req_ttvn = tt_request->ttvn;
1230
1231 orig_node = get_orig_node(bat_priv, tt_request->src);
1232 if (!orig_node)
1233 goto out;
1234
1235 neigh_node = orig_node_get_router(orig_node);
1236 if (!neigh_node)
1237 goto out;
1238
1239 primary_if = primary_if_get_selected(bat_priv);
1240 if (!primary_if)
1241 goto out;
1242
1243 /* If the full table has been explicitly requested or the gap
1244 * is too big send the whole local translation table */
1245 if (tt_request->flags & TT_FULL_TABLE || my_ttvn != req_ttvn ||
1246 !bat_priv->tt_buff)
1247 full_table = true;
1248 else
1249 full_table = false;
1250
1251 /* In this version, fragmentation is not implemented, then
1252 * I'll send only one packet with as much TT entries as I can */
1253 if (!full_table) {
1254 spin_lock_bh(&bat_priv->tt_buff_lock);
1255 tt_len = bat_priv->tt_buff_len;
1256 tt_tot = tt_len / sizeof(struct tt_change);
1257
1258 skb = dev_alloc_skb(sizeof(struct tt_query_packet) +
1259 tt_len + ETH_HLEN);
1260 if (!skb)
1261 goto unlock;
1262
1263 skb_reserve(skb, ETH_HLEN);
1264 tt_response = (struct tt_query_packet *)skb_put(skb,
1265 sizeof(struct tt_query_packet) + tt_len);
1266 tt_response->ttvn = req_ttvn;
1267 tt_response->tt_data = htons(tt_tot);
1268
1269 tt_buff = skb->data + sizeof(struct tt_query_packet);
1270 memcpy(tt_buff, bat_priv->tt_buff,
1271 bat_priv->tt_buff_len);
1272 spin_unlock_bh(&bat_priv->tt_buff_lock);
1273 } else {
1274 tt_len = (uint16_t)atomic_read(&bat_priv->num_local_tt) *
1275 sizeof(struct tt_change);
1276 ttvn = (uint8_t)atomic_read(&bat_priv->ttvn);
1277
1278 skb = tt_response_fill_table(tt_len, ttvn,
1279 bat_priv->tt_local_hash,
1280 primary_if, NULL, NULL);
1281 if (!skb)
1282 goto out;
1283
1284 tt_response = (struct tt_query_packet *)skb->data;
1285 }
1286
1287 tt_response->packet_type = BAT_TT_QUERY;
1288 tt_response->version = COMPAT_VERSION;
1289 tt_response->ttl = TTL;
1290 memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN);
1291 memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
1292 tt_response->flags = TT_RESPONSE;
1293
1294 if (full_table)
1295 tt_response->flags |= TT_FULL_TABLE;
1296
1297 bat_dbg(DBG_TT, bat_priv,
1298 "Sending TT_RESPONSE to %pM via %pM [%c]\n",
1299 orig_node->orig, neigh_node->addr,
1300 (tt_response->flags & TT_FULL_TABLE ? 'F' : '.'));
1301
1302 send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
1303 ret = true;
1304 goto out;
1305
1306unlock:
1307 spin_unlock_bh(&bat_priv->tt_buff_lock);
1308out:
1309 if (orig_node)
1310 orig_node_free_ref(orig_node);
1311 if (neigh_node)
1312 neigh_node_free_ref(neigh_node);
1313 if (primary_if)
1314 hardif_free_ref(primary_if);
1315 if (!ret)
1316 kfree_skb(skb);
1317 /* This packet was for me, so it doesn't need to be re-routed */
1318 return true;
1319}
1320
1321bool send_tt_response(struct bat_priv *bat_priv,
1322 struct tt_query_packet *tt_request)
1323{
1324 if (is_my_mac(tt_request->dst))
1325 return send_my_tt_response(bat_priv, tt_request);
1326 else
1327 return send_other_tt_response(bat_priv, tt_request);
1328}
1329
1330static void _tt_update_changes(struct bat_priv *bat_priv,
1331 struct orig_node *orig_node,
1332 struct tt_change *tt_change,
1333 uint16_t tt_num_changes, uint8_t ttvn)
1334{
1335 int i;
1336
1337 for (i = 0; i < tt_num_changes; i++) {
1338 if ((tt_change + i)->flags & TT_CHANGE_DEL)
1339 tt_global_del(bat_priv, orig_node,
1340 (tt_change + i)->addr,
Antonio Quartullicc47f662011-04-27 14:27:57 +02001341 "tt removed by changes",
1342 (tt_change + i)->flags & TT_CLIENT_ROAM);
Antonio Quartullia73105b2011-04-27 14:27:44 +02001343 else
1344 if (!tt_global_add(bat_priv, orig_node,
Antonio Quartullicc47f662011-04-27 14:27:57 +02001345 (tt_change + i)->addr, ttvn, false))
Antonio Quartullia73105b2011-04-27 14:27:44 +02001346 /* In case of problem while storing a
1347 * global_entry, we stop the updating
1348 * procedure without committing the
1349 * ttvn change. This will avoid to send
1350 * corrupted data on tt_request
1351 */
1352 return;
1353 }
1354}
1355
1356static void tt_fill_gtable(struct bat_priv *bat_priv,
1357 struct tt_query_packet *tt_response)
1358{
1359 struct orig_node *orig_node = NULL;
1360
1361 orig_node = orig_hash_find(bat_priv, tt_response->src);
1362 if (!orig_node)
1363 goto out;
1364
1365 /* Purge the old table first.. */
1366 tt_global_del_orig(bat_priv, orig_node, "Received full table");
1367
1368 _tt_update_changes(bat_priv, orig_node,
1369 (struct tt_change *)(tt_response + 1),
1370 tt_response->tt_data, tt_response->ttvn);
1371
1372 spin_lock_bh(&orig_node->tt_buff_lock);
1373 kfree(orig_node->tt_buff);
1374 orig_node->tt_buff_len = 0;
1375 orig_node->tt_buff = NULL;
1376 spin_unlock_bh(&orig_node->tt_buff_lock);
1377
1378 atomic_set(&orig_node->last_ttvn, tt_response->ttvn);
1379
1380out:
1381 if (orig_node)
1382 orig_node_free_ref(orig_node);
1383}
1384
1385void tt_update_changes(struct bat_priv *bat_priv, struct orig_node *orig_node,
1386 uint16_t tt_num_changes, uint8_t ttvn,
1387 struct tt_change *tt_change)
1388{
1389 _tt_update_changes(bat_priv, orig_node, tt_change, tt_num_changes,
1390 ttvn);
1391
1392 tt_save_orig_buffer(bat_priv, orig_node, (unsigned char *)tt_change,
1393 tt_num_changes);
1394 atomic_set(&orig_node->last_ttvn, ttvn);
1395}
1396
1397bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr)
1398{
Antonio Quartulli7683fdc2011-04-27 14:28:07 +02001399 struct tt_local_entry *tt_local_entry = NULL;
1400 bool ret = false;
Antonio Quartullia73105b2011-04-27 14:27:44 +02001401
Antonio Quartullia73105b2011-04-27 14:27:44 +02001402 tt_local_entry = tt_local_hash_find(bat_priv, addr);
Antonio Quartulli7683fdc2011-04-27 14:28:07 +02001403 if (!tt_local_entry)
1404 goto out;
1405 ret = true;
1406out:
Antonio Quartullia73105b2011-04-27 14:27:44 +02001407 if (tt_local_entry)
Antonio Quartulli7683fdc2011-04-27 14:28:07 +02001408 tt_local_entry_free_ref(tt_local_entry);
1409 return ret;
Antonio Quartullia73105b2011-04-27 14:27:44 +02001410}
1411
1412void handle_tt_response(struct bat_priv *bat_priv,
1413 struct tt_query_packet *tt_response)
1414{
1415 struct tt_req_node *node, *safe;
1416 struct orig_node *orig_node = NULL;
1417
1418 bat_dbg(DBG_TT, bat_priv, "Received TT_RESPONSE from %pM for "
1419 "ttvn %d t_size: %d [%c]\n",
1420 tt_response->src, tt_response->ttvn,
1421 tt_response->tt_data,
1422 (tt_response->flags & TT_FULL_TABLE ? 'F' : '.'));
1423
1424 orig_node = orig_hash_find(bat_priv, tt_response->src);
1425 if (!orig_node)
1426 goto out;
1427
1428 if (tt_response->flags & TT_FULL_TABLE)
1429 tt_fill_gtable(bat_priv, tt_response);
1430 else
1431 tt_update_changes(bat_priv, orig_node, tt_response->tt_data,
1432 tt_response->ttvn,
1433 (struct tt_change *)(tt_response + 1));
1434
1435 /* Delete the tt_req_node from pending tt_requests list */
1436 spin_lock_bh(&bat_priv->tt_req_list_lock);
1437 list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) {
1438 if (!compare_eth(node->addr, tt_response->src))
1439 continue;
1440 list_del(&node->list);
1441 kfree(node);
1442 }
1443 spin_unlock_bh(&bat_priv->tt_req_list_lock);
1444
1445 /* Recalculate the CRC for this orig_node and store it */
Antonio Quartullia73105b2011-04-27 14:27:44 +02001446 orig_node->tt_crc = tt_global_crc(bat_priv, orig_node);
Antonio Quartullicc47f662011-04-27 14:27:57 +02001447 /* Roaming phase is over: tables are in sync again. I can
1448 * unset the flag */
1449 orig_node->tt_poss_change = false;
Antonio Quartullia73105b2011-04-27 14:27:44 +02001450out:
1451 if (orig_node)
1452 orig_node_free_ref(orig_node);
1453}
1454
1455int tt_init(struct bat_priv *bat_priv)
1456{
1457 if (!tt_local_init(bat_priv))
1458 return 0;
1459
1460 if (!tt_global_init(bat_priv))
1461 return 0;
1462
1463 tt_start_timer(bat_priv);
1464
1465 return 1;
1466}
1467
Antonio Quartullicc47f662011-04-27 14:27:57 +02001468static void tt_roam_list_free(struct bat_priv *bat_priv)
Antonio Quartullia73105b2011-04-27 14:27:44 +02001469{
Antonio Quartullicc47f662011-04-27 14:27:57 +02001470 struct tt_roam_node *node, *safe;
Antonio Quartullia73105b2011-04-27 14:27:44 +02001471
Antonio Quartullicc47f662011-04-27 14:27:57 +02001472 spin_lock_bh(&bat_priv->tt_roam_list_lock);
Antonio Quartullia73105b2011-04-27 14:27:44 +02001473
Antonio Quartullicc47f662011-04-27 14:27:57 +02001474 list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) {
1475 list_del(&node->list);
1476 kfree(node);
1477 }
1478
1479 spin_unlock_bh(&bat_priv->tt_roam_list_lock);
1480}
1481
1482static void tt_roam_purge(struct bat_priv *bat_priv)
1483{
1484 struct tt_roam_node *node, *safe;
1485
1486 spin_lock_bh(&bat_priv->tt_roam_list_lock);
1487 list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) {
1488 if (!is_out_of_time(node->first_time,
1489 ROAMING_MAX_TIME * 1000))
1490 continue;
1491
1492 list_del(&node->list);
1493 kfree(node);
1494 }
1495 spin_unlock_bh(&bat_priv->tt_roam_list_lock);
1496}
1497
1498/* This function checks whether the client already reached the
1499 * maximum number of possible roaming phases. In this case the ROAMING_ADV
1500 * will not be sent.
1501 *
1502 * returns true if the ROAMING_ADV can be sent, false otherwise */
1503static bool tt_check_roam_count(struct bat_priv *bat_priv,
1504 uint8_t *client)
1505{
1506 struct tt_roam_node *tt_roam_node;
1507 bool ret = false;
1508
1509 spin_lock_bh(&bat_priv->tt_roam_list_lock);
1510 /* The new tt_req will be issued only if I'm not waiting for a
1511 * reply from the same orig_node yet */
1512 list_for_each_entry(tt_roam_node, &bat_priv->tt_roam_list, list) {
1513 if (!compare_eth(tt_roam_node->addr, client))
1514 continue;
1515
1516 if (is_out_of_time(tt_roam_node->first_time,
1517 ROAMING_MAX_TIME * 1000))
1518 continue;
1519
1520 if (!atomic_dec_not_zero(&tt_roam_node->counter))
1521 /* Sorry, you roamed too many times! */
1522 goto unlock;
1523 ret = true;
1524 break;
1525 }
1526
1527 if (!ret) {
1528 tt_roam_node = kmalloc(sizeof(*tt_roam_node), GFP_ATOMIC);
1529 if (!tt_roam_node)
1530 goto unlock;
1531
1532 tt_roam_node->first_time = jiffies;
1533 atomic_set(&tt_roam_node->counter, ROAMING_MAX_COUNT - 1);
1534 memcpy(tt_roam_node->addr, client, ETH_ALEN);
1535
1536 list_add(&tt_roam_node->list, &bat_priv->tt_roam_list);
1537 ret = true;
1538 }
1539
1540unlock:
1541 spin_unlock_bh(&bat_priv->tt_roam_list_lock);
1542 return ret;
1543}
1544
1545void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
1546 struct orig_node *orig_node)
1547{
1548 struct neigh_node *neigh_node = NULL;
1549 struct sk_buff *skb = NULL;
1550 struct roam_adv_packet *roam_adv_packet;
1551 int ret = 1;
1552 struct hard_iface *primary_if;
1553
1554 /* before going on we have to check whether the client has
1555 * already roamed to us too many times */
1556 if (!tt_check_roam_count(bat_priv, client))
1557 goto out;
1558
1559 skb = dev_alloc_skb(sizeof(struct roam_adv_packet) + ETH_HLEN);
1560 if (!skb)
1561 goto out;
1562
1563 skb_reserve(skb, ETH_HLEN);
1564
1565 roam_adv_packet = (struct roam_adv_packet *)skb_put(skb,
1566 sizeof(struct roam_adv_packet));
1567
1568 roam_adv_packet->packet_type = BAT_ROAM_ADV;
1569 roam_adv_packet->version = COMPAT_VERSION;
1570 roam_adv_packet->ttl = TTL;
1571 primary_if = primary_if_get_selected(bat_priv);
1572 if (!primary_if)
1573 goto out;
1574 memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
1575 hardif_free_ref(primary_if);
1576 memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN);
1577 memcpy(roam_adv_packet->client, client, ETH_ALEN);
1578
1579 neigh_node = orig_node_get_router(orig_node);
1580 if (!neigh_node)
1581 goto out;
1582
1583 bat_dbg(DBG_TT, bat_priv,
1584 "Sending ROAMING_ADV to %pM (client %pM) via %pM\n",
1585 orig_node->orig, client, neigh_node->addr);
1586
1587 send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
1588 ret = 0;
1589
1590out:
1591 if (neigh_node)
1592 neigh_node_free_ref(neigh_node);
1593 if (ret)
1594 kfree_skb(skb);
1595 return;
Antonio Quartullia73105b2011-04-27 14:27:44 +02001596}
1597
1598static void tt_purge(struct work_struct *work)
1599{
1600 struct delayed_work *delayed_work =
1601 container_of(work, struct delayed_work, work);
1602 struct bat_priv *bat_priv =
1603 container_of(delayed_work, struct bat_priv, tt_work);
1604
1605 tt_local_purge(bat_priv);
Antonio Quartullicc47f662011-04-27 14:27:57 +02001606 tt_global_roam_purge(bat_priv);
Antonio Quartullia73105b2011-04-27 14:27:44 +02001607 tt_req_purge(bat_priv);
Antonio Quartullicc47f662011-04-27 14:27:57 +02001608 tt_roam_purge(bat_priv);
Antonio Quartullia73105b2011-04-27 14:27:44 +02001609
1610 tt_start_timer(bat_priv);
1611}
Antonio Quartullicc47f662011-04-27 14:27:57 +02001612
1613void tt_free(struct bat_priv *bat_priv)
1614{
1615 cancel_delayed_work_sync(&bat_priv->tt_work);
1616
1617 tt_local_table_free(bat_priv);
1618 tt_global_table_free(bat_priv);
1619 tt_req_list_free(bat_priv);
1620 tt_changes_list_free(bat_priv);
1621 tt_roam_list_free(bat_priv);
1622
1623 kfree(bat_priv->tt_buff);
1624}