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