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