]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/staging/batman-adv/translation-table.c
Merge branch 'for-2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[mirror_ubuntu-artful-kernel.git] / drivers / staging / batman-adv / translation-table.c
CommitLineData
5beef3c9 1/*
9b6d10b7 2 * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors:
5beef3c9
AL
3 *
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"
5beef3c9
AL
24#include "soft-interface.h"
25#include "types.h"
26#include "hash.h"
5beef3c9 27
42fa1b92 28static void hna_local_purge(struct work_struct *work);
6a0e9fa8
ML
29static void _hna_global_del_orig(struct bat_priv *bat_priv,
30 struct hna_global_entry *hna_global_entry,
42fa1b92 31 char *message);
5beef3c9 32
8c70f138 33static void hna_local_start_timer(struct bat_priv *bat_priv)
5beef3c9 34{
8c70f138
ML
35 INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge);
36 queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ);
5beef3c9
AL
37}
38
8c70f138 39int hna_local_init(struct bat_priv *bat_priv)
5beef3c9 40{
8c70f138 41 if (bat_priv->hna_local_hash)
5beef3c9
AL
42 return 1;
43
8c70f138 44 bat_priv->hna_local_hash = hash_new(128, compare_orig, choose_orig);
5beef3c9 45
8c70f138 46 if (!bat_priv->hna_local_hash)
5beef3c9
AL
47 return 0;
48
8c70f138
ML
49 atomic_set(&bat_priv->hna_local_changed, 0);
50 hna_local_start_timer(bat_priv);
5beef3c9
AL
51
52 return 1;
53}
54
6a0e9fa8 55void hna_local_add(struct net_device *soft_iface, uint8_t *addr)
5beef3c9 56{
6a0e9fa8 57 struct bat_priv *bat_priv = netdev_priv(soft_iface);
5beef3c9
AL
58 struct hna_local_entry *hna_local_entry;
59 struct hna_global_entry *hna_global_entry;
60 struct hashtable_t *swaphash;
5beef3c9 61 unsigned long flags;
24fb009b 62 int required_bytes;
5beef3c9 63
8c70f138 64 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
5beef3c9 65 hna_local_entry =
8c70f138
ML
66 ((struct hna_local_entry *)hash_find(bat_priv->hna_local_hash,
67 addr));
68 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
5beef3c9 69
8c70f138 70 if (hna_local_entry) {
5beef3c9
AL
71 hna_local_entry->last_seen = jiffies;
72 return;
73 }
74
5beef3c9
AL
75 /* only announce as many hosts as possible in the batman-packet and
76 space in batman_packet->num_hna That also should give a limit to
77 MAC-flooding. */
24fb009b
ML
78 required_bytes = (bat_priv->num_local_hna + 1) * ETH_ALEN;
79 required_bytes += BAT_PACKET_LEN;
80
81 if ((required_bytes > ETH_DATA_LEN) ||
82 (atomic_read(&bat_priv->aggregation_enabled) &&
83 required_bytes > MAX_AGGREGATION_BYTES) ||
8c70f138 84 (bat_priv->num_local_hna + 1 > 255)) {
84ec0864 85 bat_dbg(DBG_ROUTES, bat_priv,
6d45d8df
SE
86 "Can't add new local hna entry (%pM): "
87 "number of local hna entries exceeds packet size\n",
88 addr);
5beef3c9
AL
89 return;
90 }
91
84ec0864
ML
92 bat_dbg(DBG_ROUTES, bat_priv,
93 "Creating new local hna entry: %pM\n", addr);
5beef3c9
AL
94
95 hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC);
96 if (!hna_local_entry)
97 return;
98
99 memcpy(hna_local_entry->addr, addr, ETH_ALEN);
100 hna_local_entry->last_seen = jiffies;
101
102 /* the batman interface mac address should never be purged */
6a0e9fa8 103 if (compare_orig(addr, soft_iface->dev_addr))
5beef3c9
AL
104 hna_local_entry->never_purge = 1;
105 else
106 hna_local_entry->never_purge = 0;
107
8c70f138 108 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
5beef3c9 109
8c70f138
ML
110 hash_add(bat_priv->hna_local_hash, hna_local_entry);
111 bat_priv->num_local_hna++;
112 atomic_set(&bat_priv->hna_local_changed, 1);
5beef3c9 113
8c70f138
ML
114 if (bat_priv->hna_local_hash->elements * 4 >
115 bat_priv->hna_local_hash->size) {
116 swaphash = hash_resize(bat_priv->hna_local_hash,
117 bat_priv->hna_local_hash->size * 2);
5beef3c9 118
8c70f138 119 if (!swaphash)
c1641862 120 pr_err("Couldn't resize local hna hash table\n");
5beef3c9 121 else
8c70f138 122 bat_priv->hna_local_hash = swaphash;
5beef3c9
AL
123 }
124
8c70f138 125 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
5beef3c9
AL
126
127 /* remove address from global hash if present */
8c70f138 128 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
5beef3c9 129
8c70f138
ML
130 hna_global_entry = ((struct hna_global_entry *)
131 hash_find(bat_priv->hna_global_hash, addr));
5beef3c9 132
8c70f138 133 if (hna_global_entry)
6a0e9fa8
ML
134 _hna_global_del_orig(bat_priv, hna_global_entry,
135 "local hna received");
5beef3c9 136
8c70f138 137 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
5beef3c9
AL
138}
139
8c70f138
ML
140int hna_local_fill_buffer(struct bat_priv *bat_priv,
141 unsigned char *buff, int buff_len)
5beef3c9
AL
142{
143 struct hna_local_entry *hna_local_entry;
b6c35976 144 HASHIT(hashit);
5beef3c9
AL
145 int i = 0;
146 unsigned long flags;
147
8c70f138 148 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
5beef3c9 149
8c70f138 150 while (hash_iterate(bat_priv->hna_local_hash, &hashit)) {
5beef3c9
AL
151
152 if (buff_len < (i + 1) * ETH_ALEN)
153 break;
154
b6c35976 155 hna_local_entry = hashit.bucket->data;
5beef3c9
AL
156 memcpy(buff + (i * ETH_ALEN), hna_local_entry->addr, ETH_ALEN);
157
158 i++;
159 }
160
161 /* if we did not get all new local hnas see you next time ;-) */
8c70f138
ML
162 if (i == bat_priv->num_local_hna)
163 atomic_set(&bat_priv->hna_local_changed, 0);
5beef3c9 164
8c70f138 165 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
5beef3c9
AL
166 return i;
167}
168
4caecbc0 169int hna_local_seq_print_text(struct seq_file *seq, void *offset)
5beef3c9 170{
4caecbc0 171 struct net_device *net_dev = (struct net_device *)seq->private;
208e13e4 172 struct bat_priv *bat_priv = netdev_priv(net_dev);
5beef3c9 173 struct hna_local_entry *hna_local_entry;
b6c35976 174 HASHIT(hashit);
4caecbc0 175 HASHIT(hashit_count);
5beef3c9 176 unsigned long flags;
4caecbc0
SE
177 size_t buf_size, pos;
178 char *buff;
47fdf097 179
208e13e4 180 if (!bat_priv->primary_if) {
4caecbc0
SE
181 return seq_printf(seq, "BATMAN mesh %s disabled - "
182 "please specify interfaces to enable it\n",
183 net_dev->name);
14741240 184 }
14741240 185
4caecbc0
SE
186 seq_printf(seq, "Locally retrieved addresses (from %s) "
187 "announced via HNA:\n",
188 net_dev->name);
5beef3c9 189
8c70f138 190 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
5beef3c9 191
4caecbc0
SE
192 buf_size = 1;
193 /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */
8c70f138 194 while (hash_iterate(bat_priv->hna_local_hash, &hashit_count))
4caecbc0 195 buf_size += 21;
5beef3c9 196
4caecbc0
SE
197 buff = kmalloc(buf_size, GFP_ATOMIC);
198 if (!buff) {
8c70f138 199 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
4caecbc0
SE
200 return -ENOMEM;
201 }
202 buff[0] = '\0';
203 pos = 0;
47fdf097 204
8c70f138 205 while (hash_iterate(bat_priv->hna_local_hash, &hashit)) {
b6c35976 206 hna_local_entry = hashit.bucket->data;
5beef3c9 207
516c9a77
JP
208 pos += snprintf(buff + pos, 22, " * %pM\n",
209 hna_local_entry->addr);
5beef3c9
AL
210 }
211
8c70f138 212 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
4caecbc0
SE
213
214 seq_printf(seq, "%s", buff);
215 kfree(buff);
216 return 0;
5beef3c9
AL
217}
218
6a0e9fa8 219static void _hna_local_del(void *data, void *arg)
5beef3c9 220{
8c70f138
ML
221 struct bat_priv *bat_priv = (struct bat_priv *)arg;
222
5beef3c9 223 kfree(data);
8c70f138
ML
224 bat_priv->num_local_hna--;
225 atomic_set(&bat_priv->hna_local_changed, 1);
5beef3c9
AL
226}
227
6a0e9fa8
ML
228static void hna_local_del(struct bat_priv *bat_priv,
229 struct hna_local_entry *hna_local_entry,
5beef3c9
AL
230 char *message)
231{
84ec0864 232 bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n",
b9b27e4e 233 hna_local_entry->addr, message);
5beef3c9 234
8c70f138 235 hash_remove(bat_priv->hna_local_hash, hna_local_entry->addr);
6a0e9fa8 236 _hna_local_del(hna_local_entry, bat_priv);
5beef3c9
AL
237}
238
6a0e9fa8
ML
239void hna_local_remove(struct bat_priv *bat_priv,
240 uint8_t *addr, char *message)
a9c2910a
AL
241{
242 struct hna_local_entry *hna_local_entry;
243 unsigned long flags;
244
8c70f138 245 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
a9c2910a
AL
246
247 hna_local_entry = (struct hna_local_entry *)
8c70f138 248 hash_find(bat_priv->hna_local_hash, addr);
a9c2910a 249 if (hna_local_entry)
6a0e9fa8 250 hna_local_del(bat_priv, hna_local_entry, message);
a9c2910a 251
8c70f138 252 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
a9c2910a
AL
253}
254
42fa1b92 255static void hna_local_purge(struct work_struct *work)
5beef3c9 256{
8c70f138
ML
257 struct delayed_work *delayed_work =
258 container_of(work, struct delayed_work, work);
259 struct bat_priv *bat_priv =
260 container_of(delayed_work, struct bat_priv, hna_work);
5beef3c9 261 struct hna_local_entry *hna_local_entry;
b6c35976 262 HASHIT(hashit);
5beef3c9
AL
263 unsigned long flags;
264 unsigned long timeout;
265
8c70f138 266 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
5beef3c9 267
8c70f138 268 while (hash_iterate(bat_priv->hna_local_hash, &hashit)) {
b6c35976 269 hna_local_entry = hashit.bucket->data;
5beef3c9 270
b6be4535 271 timeout = hna_local_entry->last_seen + LOCAL_HNA_TIMEOUT * HZ;
8c70f138
ML
272
273 if ((!hna_local_entry->never_purge) &&
5beef3c9 274 time_after(jiffies, timeout))
6a0e9fa8 275 hna_local_del(bat_priv, hna_local_entry,
8c70f138 276 "address timed out");
5beef3c9
AL
277 }
278
8c70f138
ML
279 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
280 hna_local_start_timer(bat_priv);
5beef3c9
AL
281}
282
8c70f138 283void hna_local_free(struct bat_priv *bat_priv)
5beef3c9 284{
8c70f138 285 if (!bat_priv->hna_local_hash)
5beef3c9
AL
286 return;
287
8c70f138
ML
288 cancel_delayed_work_sync(&bat_priv->hna_work);
289 hash_delete(bat_priv->hna_local_hash, _hna_local_del, bat_priv);
290 bat_priv->hna_local_hash = NULL;
5beef3c9
AL
291}
292
8c70f138 293int hna_global_init(struct bat_priv *bat_priv)
5beef3c9 294{
8c70f138 295 if (bat_priv->hna_global_hash)
5beef3c9
AL
296 return 1;
297
8c70f138 298 bat_priv->hna_global_hash = hash_new(128, compare_orig, choose_orig);
5beef3c9 299
8c70f138 300 if (!bat_priv->hna_global_hash)
5beef3c9
AL
301 return 0;
302
303 return 1;
304}
305
6a0e9fa8
ML
306void hna_global_add_orig(struct bat_priv *bat_priv,
307 struct orig_node *orig_node,
5beef3c9
AL
308 unsigned char *hna_buff, int hna_buff_len)
309{
310 struct hna_global_entry *hna_global_entry;
311 struct hna_local_entry *hna_local_entry;
312 struct hashtable_t *swaphash;
5beef3c9
AL
313 int hna_buff_count = 0;
314 unsigned long flags;
315 unsigned char *hna_ptr;
316
5beef3c9 317 while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) {
8c70f138 318 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
5beef3c9
AL
319
320 hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
321 hna_global_entry = (struct hna_global_entry *)
8c70f138 322 hash_find(bat_priv->hna_global_hash, hna_ptr);
5beef3c9 323
8c70f138
ML
324 if (!hna_global_entry) {
325 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock,
326 flags);
5beef3c9
AL
327
328 hna_global_entry =
329 kmalloc(sizeof(struct hna_global_entry),
330 GFP_ATOMIC);
331
332 if (!hna_global_entry)
333 break;
334
335 memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN);
336
84ec0864 337 bat_dbg(DBG_ROUTES, bat_priv,
6d45d8df
SE
338 "Creating new global hna entry: "
339 "%pM (via %pM)\n",
b9b27e4e 340 hna_global_entry->addr, orig_node->orig);
5beef3c9 341
8c70f138
ML
342 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
343 hash_add(bat_priv->hna_global_hash, hna_global_entry);
5beef3c9
AL
344
345 }
346
347 hna_global_entry->orig_node = orig_node;
8c70f138 348 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
5beef3c9
AL
349
350 /* remove address from local hash if present */
8c70f138 351 spin_lock_irqsave(&bat_priv->hna_lhash_lock, flags);
5beef3c9
AL
352
353 hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
354 hna_local_entry = (struct hna_local_entry *)
8c70f138 355 hash_find(bat_priv->hna_local_hash, hna_ptr);
5beef3c9 356
8c70f138 357 if (hna_local_entry)
6a0e9fa8
ML
358 hna_local_del(bat_priv, hna_local_entry,
359 "global hna received");
5beef3c9 360
8c70f138 361 spin_unlock_irqrestore(&bat_priv->hna_lhash_lock, flags);
5beef3c9
AL
362
363 hna_buff_count++;
364 }
365
c4bf05d3
SW
366 /* initialize, and overwrite if malloc succeeds */
367 orig_node->hna_buff = NULL;
368 orig_node->hna_buff_len = 0;
369
370 if (hna_buff_len > 0) {
371 orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC);
372 if (orig_node->hna_buff) {
373 memcpy(orig_node->hna_buff, hna_buff, hna_buff_len);
374 orig_node->hna_buff_len = hna_buff_len;
375 }
5beef3c9
AL
376 }
377
8c70f138 378 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
5beef3c9 379
8c70f138
ML
380 if (bat_priv->hna_global_hash->elements * 4 >
381 bat_priv->hna_global_hash->size) {
382 swaphash = hash_resize(bat_priv->hna_global_hash,
383 bat_priv->hna_global_hash->size * 2);
5beef3c9 384
8c70f138 385 if (!swaphash)
c1641862 386 pr_err("Couldn't resize global hna hash table\n");
5beef3c9 387 else
8c70f138 388 bat_priv->hna_global_hash = swaphash;
5beef3c9
AL
389 }
390
8c70f138 391 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
5beef3c9
AL
392}
393
4caecbc0 394int hna_global_seq_print_text(struct seq_file *seq, void *offset)
5beef3c9 395{
4caecbc0 396 struct net_device *net_dev = (struct net_device *)seq->private;
208e13e4 397 struct bat_priv *bat_priv = netdev_priv(net_dev);
5beef3c9 398 struct hna_global_entry *hna_global_entry;
b6c35976 399 HASHIT(hashit);
4caecbc0 400 HASHIT(hashit_count);
5beef3c9 401 unsigned long flags;
4caecbc0
SE
402 size_t buf_size, pos;
403 char *buff;
47fdf097 404
208e13e4 405 if (!bat_priv->primary_if) {
4caecbc0
SE
406 return seq_printf(seq, "BATMAN mesh %s disabled - "
407 "please specify interfaces to enable it\n",
408 net_dev->name);
14741240 409 }
14741240 410
1bcb164e 411 seq_printf(seq, "Globally announced HNAs received via the mesh %s\n",
4caecbc0 412 net_dev->name);
5beef3c9 413
8c70f138 414 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
5beef3c9 415
4caecbc0
SE
416 buf_size = 1;
417 /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/
8c70f138 418 while (hash_iterate(bat_priv->hna_global_hash, &hashit_count))
4caecbc0 419 buf_size += 43;
5beef3c9 420
4caecbc0
SE
421 buff = kmalloc(buf_size, GFP_ATOMIC);
422 if (!buff) {
8c70f138 423 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
4caecbc0
SE
424 return -ENOMEM;
425 }
426 buff[0] = '\0';
427 pos = 0;
47fdf097 428
8c70f138 429 while (hash_iterate(bat_priv->hna_global_hash, &hashit)) {
b6c35976 430 hna_global_entry = hashit.bucket->data;
5beef3c9 431
4caecbc0 432 pos += snprintf(buff + pos, 44,
516c9a77
JP
433 " * %pM via %pM\n", hna_global_entry->addr,
434 hna_global_entry->orig_node->orig);
5beef3c9
AL
435 }
436
8c70f138 437 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
4caecbc0
SE
438
439 seq_printf(seq, "%s", buff);
440 kfree(buff);
441 return 0;
5beef3c9
AL
442}
443
6a0e9fa8
ML
444static void _hna_global_del_orig(struct bat_priv *bat_priv,
445 struct hna_global_entry *hna_global_entry,
42fa1b92 446 char *message)
5beef3c9 447{
84ec0864
ML
448 bat_dbg(DBG_ROUTES, bat_priv,
449 "Deleting global hna entry %pM (via %pM): %s\n",
b9b27e4e
AL
450 hna_global_entry->addr, hna_global_entry->orig_node->orig,
451 message);
5beef3c9 452
8c70f138 453 hash_remove(bat_priv->hna_global_hash, hna_global_entry->addr);
5beef3c9
AL
454 kfree(hna_global_entry);
455}
456
6a0e9fa8
ML
457void hna_global_del_orig(struct bat_priv *bat_priv,
458 struct orig_node *orig_node, char *message)
5beef3c9
AL
459{
460 struct hna_global_entry *hna_global_entry;
461 int hna_buff_count = 0;
462 unsigned long flags;
463 unsigned char *hna_ptr;
464
465 if (orig_node->hna_buff_len == 0)
466 return;
467
8c70f138 468 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
5beef3c9
AL
469
470 while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) {
471 hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN);
472 hna_global_entry = (struct hna_global_entry *)
8c70f138 473 hash_find(bat_priv->hna_global_hash, hna_ptr);
5beef3c9 474
8c70f138 475 if ((hna_global_entry) &&
5beef3c9 476 (hna_global_entry->orig_node == orig_node))
6a0e9fa8
ML
477 _hna_global_del_orig(bat_priv, hna_global_entry,
478 message);
5beef3c9
AL
479
480 hna_buff_count++;
481 }
482
8c70f138 483 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
5beef3c9
AL
484
485 orig_node->hna_buff_len = 0;
486 kfree(orig_node->hna_buff);
487 orig_node->hna_buff = NULL;
488}
489
6a0e9fa8 490static void hna_global_del(void *data, void *arg)
5beef3c9
AL
491{
492 kfree(data);
493}
494
8c70f138 495void hna_global_free(struct bat_priv *bat_priv)
5beef3c9 496{
8c70f138 497 if (!bat_priv->hna_global_hash)
5beef3c9
AL
498 return;
499
8c70f138
ML
500 hash_delete(bat_priv->hna_global_hash, hna_global_del, NULL);
501 bat_priv->hna_global_hash = NULL;
5beef3c9
AL
502}
503
8c70f138 504struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr)
5beef3c9
AL
505{
506 struct hna_global_entry *hna_global_entry;
507 unsigned long flags;
508
8c70f138 509 spin_lock_irqsave(&bat_priv->hna_ghash_lock, flags);
5beef3c9 510 hna_global_entry = (struct hna_global_entry *)
8c70f138
ML
511 hash_find(bat_priv->hna_global_hash, addr);
512 spin_unlock_irqrestore(&bat_priv->hna_ghash_lock, flags);
5beef3c9 513
8c70f138 514 if (!hna_global_entry)
5beef3c9
AL
515 return NULL;
516
517 return hna_global_entry->orig_node;
518}