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