Contiki-NG
nbr-table.c
1 /*
2  * Copyright (c) 2013, Swedish Institute of Computer Science
3  * Copyright (c) 2010, Vrije Universiteit Brussel
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the Institute nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *
31  * Authors: Simon Duquennoy <simonduq@sics.se>
32  * Joris Borms <joris.borms@vub.ac.be>
33  */
34 
35 #include "contiki.h"
36 
37 #include <stddef.h>
38 #include <string.h>
39 #include "lib/memb.h"
40 #include "lib/list.h"
41 #include "net/nbr-table.h"
42 
43 #define DEBUG DEBUG_NONE
44 #include "net/ipv6/uip-debug.h"
45 
46 #if DEBUG
47 #include "sys/ctimer.h"
48 static void handle_periodic_timer(void *ptr);
49 static struct ctimer periodic_timer;
50 static uint8_t initialized = 0;
51 static void print_table();
52 #define PRINTF(...) printf(__VA_ARGS__)
53 #else
54 #define PRINTF(...)
55 #endif
56 
57 /* For each neighbor, a map of the tables that use the neighbor.
58  * As we are using uint8_t, we have a maximum of 8 tables in the system */
59 static uint8_t used_map[NBR_TABLE_MAX_NEIGHBORS];
60 /* For each neighbor, a map of the tables that lock the neighbor */
61 static uint8_t locked_map[NBR_TABLE_MAX_NEIGHBORS];
62 /* The maximum number of tables */
63 #define MAX_NUM_TABLES 8
64 /* A list of pointers to tables in use */
65 static struct nbr_table *all_tables[MAX_NUM_TABLES];
66 /* The current number of tables */
67 static unsigned num_tables;
68 
69 /* The neighbor address table */
70 MEMB(neighbor_addr_mem, nbr_table_key_t, NBR_TABLE_MAX_NEIGHBORS);
71 LIST(nbr_table_keys);
72 
73 /*---------------------------------------------------------------------------*/
74 static void remove_key(nbr_table_key_t *key, bool do_free);
75 /*---------------------------------------------------------------------------*/
76 /* Get a key from a neighbor index */
77 static nbr_table_key_t *
78 key_from_index(int index)
79 {
80  return index != -1 ? &((nbr_table_key_t *)neighbor_addr_mem.mem)[index] : NULL;
81 }
82 /*---------------------------------------------------------------------------*/
83 /* Get an item from its neighbor index */
84 static nbr_table_item_t *
85 item_from_index(nbr_table_t *table, int index)
86 {
87  return table != NULL && index != -1 ? (char *)table->data + index * table->item_size : NULL;
88 }
89 /*---------------------------------------------------------------------------*/
90 /* Get the neighbor index of an item */
91 static int
92 index_from_key(nbr_table_key_t *key)
93 {
94  return key != NULL ? key - (nbr_table_key_t *)neighbor_addr_mem.mem : -1;
95 }
96 /*---------------------------------------------------------------------------*/
97 /* Get the neighbor index of an item */
98 static int
99 index_from_item(nbr_table_t *table, const nbr_table_item_t *item)
100 {
101  return table != NULL && item != NULL ? ((int)((char *)item - (char *)table->data)) / table->item_size : -1;
102 }
103 /*---------------------------------------------------------------------------*/
104 /* Get an item from its key */
105 static nbr_table_item_t *
106 item_from_key(nbr_table_t *table, nbr_table_key_t *key)
107 {
108  return item_from_index(table, index_from_key(key));
109 }
110 /*---------------------------------------------------------------------------*/
111 /* Get the key af an item */
112 static nbr_table_key_t *
113 key_from_item(nbr_table_t *table, const nbr_table_item_t *item)
114 {
115  return key_from_index(index_from_item(table, item));
116 }
117 /*---------------------------------------------------------------------------*/
118 /* Get the index of a neighbor from its link-layer address */
119 static int
120 index_from_lladdr(const linkaddr_t *lladdr)
121 {
122  nbr_table_key_t *key;
123  /* Allow lladdr-free insertion, useful e.g. for IPv6 ND.
124  * Only one such entry is possible at a time, indexed by linkaddr_null. */
125  if(lladdr == NULL) {
126  lladdr = &linkaddr_null;
127  }
128  key = list_head(nbr_table_keys);
129  while(key != NULL) {
130  if(lladdr && linkaddr_cmp(lladdr, &key->lladdr)) {
131  return index_from_key(key);
132  }
133  key = list_item_next(key);
134  }
135  return -1;
136 }
137 /*---------------------------------------------------------------------------*/
138 /* Get bit from "used" or "locked" bitmap */
139 static int
140 nbr_get_bit(uint8_t *bitmap, nbr_table_t *table, nbr_table_item_t *item)
141 {
142  int item_index = index_from_item(table, item);
143  if(table != NULL && item_index != -1) {
144  return (bitmap[item_index] & (1 << table->index)) != 0;
145  } else {
146  return 0;
147  }
148  return 0;
149 }
150 /*---------------------------------------------------------------------------*/
151 /* Set bit in "used" or "locked" bitmap */
152 static int
153 nbr_set_bit(uint8_t *bitmap, nbr_table_t *table, nbr_table_item_t *item, int value)
154 {
155  int item_index = index_from_item(table, item);
156 
157  if(table != NULL && item_index != -1) {
158  if(value) {
159  bitmap[item_index] |= 1 << table->index;
160  } else {
161  bitmap[item_index] &= ~(1 << table->index);
162  }
163  return 1;
164  } else {
165  return 0;
166  }
167  return 0;
168 }
169 /*---------------------------------------------------------------------------*/
170 static void
171 remove_key(nbr_table_key_t *key, bool do_free)
172 {
173  int i;
174  for(i = 0; i < MAX_NUM_TABLES; i++) {
175  if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
176  /* Call table callback for each table that uses this item */
177  nbr_table_item_t *removed_item = item_from_key(all_tables[i], key);
178  if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
179  all_tables[i]->callback(removed_item);
180  }
181  }
182  }
183  /* Empty used and locked map */
184  used_map[index_from_key(key)] = 0;
185  locked_map[index_from_key(key)] = 0;
186  /* Remove neighbor from list */
187  list_remove(nbr_table_keys, key);
188  if(do_free) {
189  /* Release the memory */
190  memb_free(&neighbor_addr_mem, key);
191  }
192 }
193 /*---------------------------------------------------------------------------*/
194 int
195 nbr_table_count_entries(void)
196 {
197  int i;
198  int count = 0;
199  for(i = 0; i < NBR_TABLE_MAX_NEIGHBORS; i++) {
200  if(used_map[i] > 0) {
201  count++;
202  }
203  }
204  return count;
205 }
206 /*---------------------------------------------------------------------------*/
207 static int
208 used_count(const linkaddr_t *lladdr)
209 {
210  int item_index = index_from_lladdr(lladdr);
211  int used_count = 0;
212  if(item_index != -1) {
213  int used = used_map[item_index];
214  /* Count how many tables are using this item */
215  while(used != 0) {
216  if((used & 1) == 1) {
217  used_count++;
218  }
219  used >>= 1;
220  }
221  }
222  return used_count;
223 }
224 /*---------------------------------------------------------------------------*/
225 const linkaddr_t *
226 nbr_table_gc_get_worst(const linkaddr_t *lladdr1, const linkaddr_t *lladdr2)
227 {
228  return used_count(lladdr2) < used_count(lladdr1) ? lladdr2 : lladdr1;
229 }
230 /*---------------------------------------------------------------------------*/
231 bool
232 nbr_table_can_accept_new(const linkaddr_t *new, const linkaddr_t *candidate_for_removal,
233  nbr_table_reason_t reason, void *data)
234 {
235  /* Default behavior: if full, always replace worst entry. */
236  return true;
237 }
238 /*---------------------------------------------------------------------------*/
239 static const linkaddr_t *
240 select_for_removal(const linkaddr_t *new, nbr_table_reason_t reason, void *data)
241 {
242  nbr_table_key_t *k;
243  const linkaddr_t *worst_lladdr = NULL;
244 
245  /* No more space, try to free a neighbor */
246  for(k = list_head(nbr_table_keys); k != NULL; k = list_item_next(k)) {
247  int item_index = index_from_key(k);
248  int locked = locked_map[item_index];
249  /* Never delete a locked item */
250  if(!locked) {
251  if(worst_lladdr == NULL) {
252  worst_lladdr = &k->lladdr;
253  } else {
254  worst_lladdr = NBR_TABLE_GC_GET_WORST(worst_lladdr, &k->lladdr);
255  }
256  }
257  }
258 
259  /* Finally compare against current candidate for insertion */
260  if(worst_lladdr != NULL && NBR_TABLE_CAN_ACCEPT_NEW(new, worst_lladdr, reason, data)) {
261  return worst_lladdr;
262  } else {
263  return NULL;
264  }
265 }
266 /*---------------------------------------------------------------------------*/
267 static bool
268 entry_is_allowed(nbr_table_t *table, const linkaddr_t *lladdr,
269  nbr_table_reason_t reason, void *data,
270  const linkaddr_t **to_be_removed_ptr)
271 {
272  bool ret;
273  const linkaddr_t *to_be_removed = NULL;
274 
275  if(nbr_table_get_from_lladdr(table, lladdr) != NULL) {
276  /* Already present in the given table */
277  ret = true;
278  } else {
279  if(index_from_lladdr(lladdr) != -1
280  || memb_numfree(&neighbor_addr_mem) > 0) {
281  /* lladdr already present globally, or there is space for a new lladdr,
282  * check if entry can be added */
283  ret = NBR_TABLE_CAN_ACCEPT_NEW(lladdr, NULL, reason, data);
284  } else {
285  ret = (to_be_removed = select_for_removal(lladdr, reason, data)) != NULL;
286  }
287  }
288  if(to_be_removed_ptr != NULL) {
289  *to_be_removed_ptr = to_be_removed;
290  }
291  return ret;
292 }
293 /*---------------------------------------------------------------------------*/
294 bool
295 nbr_table_entry_is_allowed(nbr_table_t *table, const linkaddr_t *lladdr,
296  nbr_table_reason_t reason, void *data)
297 {
298  return entry_is_allowed(table, lladdr, reason, data, NULL);
299 }
300 /*---------------------------------------------------------------------------*/
301 static nbr_table_key_t *
302 nbr_table_allocate(nbr_table_reason_t reason, void *data, const linkaddr_t *to_be_removed_lladdr)
303 {
304  nbr_table_key_t *new = memb_alloc(&neighbor_addr_mem);
305  if(new != NULL) {
306  return new;
307  } else {
308  if(to_be_removed_lladdr == NULL) {
309  /* No candidate for GC, allocation fails */
310  return NULL;
311  } else {
312  nbr_table_key_t *to_be_removed = key_from_index(index_from_lladdr(to_be_removed_lladdr));
313  /* Reuse to_be_removed item's spot */
314  remove_key(to_be_removed, false);
315  return to_be_removed;
316  }
317  }
318 }
319 /*---------------------------------------------------------------------------*/
320 /* Register a new neighbor table. To be used at initialization by modules
321  * using a neighbor table */
322 int
323 nbr_table_register(nbr_table_t *table, nbr_table_callback *callback)
324 {
325 #if DEBUG
326  if(!initialized) {
327  initialized = 1;
328  /* schedule a debug printout per minute */
329  ctimer_set(&periodic_timer, CLOCK_SECOND * 60, handle_periodic_timer, NULL);
330  }
331 #endif
332 
333  if(nbr_table_is_registered(table)) {
334  /* Table already registered, just update callback */
335  table->callback = callback;
336  return 1;
337  }
338 
339  if(num_tables < MAX_NUM_TABLES) {
340  table->index = num_tables++;
341  table->callback = callback;
342  all_tables[table->index] = table;
343  return 1;
344  } else {
345  /* Maximum number of tables exceeded */
346  return 0;
347  }
348 }
349 /*---------------------------------------------------------------------------*/
350 /* Test whether a specified table has been registered or not */
351 int
352 nbr_table_is_registered(nbr_table_t *table)
353 {
354  if(table != NULL && table->index >= 0 && table->index < MAX_NUM_TABLES
355  && all_tables[table->index] == table) {
356  return 1;
357  }
358  return 0;
359 }
360 /*---------------------------------------------------------------------------*/
361 /* Returns the first item of the current table */
362 nbr_table_item_t *
363 nbr_table_head(nbr_table_t *table)
364 {
365  /* Get item from first key */
366  nbr_table_item_t *item = item_from_key(table, list_head(nbr_table_keys));
367  /* Item is the first neighbor, now check is it is in the current table */
368  if(nbr_get_bit(used_map, table, item)) {
369  return item;
370  } else {
371  return nbr_table_next(table, item);
372  }
373 }
374 /*---------------------------------------------------------------------------*/
375 /* Iterates over the current table */
376 nbr_table_item_t *
377 nbr_table_next(nbr_table_t *table, nbr_table_item_t *item)
378 {
379  do {
380  void *key = key_from_item(table, item);
381  key = list_item_next(key);
382  /* Loop until the next item is in the current table */
383  item = item_from_key(table, key);
384  } while(item && !nbr_get_bit(used_map, table, item));
385  return item;
386 }
387 /*---------------------------------------------------------------------------*/
388 /* Add a neighbor indexed with its link-layer address */
389 nbr_table_item_t *
390 nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data)
391 {
392  int index;
393  nbr_table_item_t *item;
394  nbr_table_key_t *key;
395  const linkaddr_t *to_be_removed;
396 
397  if(table == NULL) {
398  return NULL;
399  }
400 
401  /* Allow lladdr-free insertion, useful e.g. for IPv6 ND.
402  * Only one such entry is possible at a time, indexed by linkaddr_null. */
403  if(lladdr == NULL) {
404  lladdr = &linkaddr_null;
405  }
406 
407  if(!entry_is_allowed(table, lladdr, reason, data, &to_be_removed)) {
408  return NULL;
409  }
410 
411  if((index = index_from_lladdr(lladdr)) == -1) {
412  /* Neighbor not yet in table, let's try to allocate one */
413  key = nbr_table_allocate(reason, data, to_be_removed);
414 
415  /* No space available for new entry. Should never happen as entry_is_allowed
416  * already checks we can add. */
417  if(key == NULL) {
418  return NULL;
419  }
420 
421  /* Add neighbor to list */
422  list_add(nbr_table_keys, key);
423 
424  /* Get index from newly allocated neighbor */
425  index = index_from_key(key);
426 
427  /* Set link-layer address */
428  linkaddr_copy(&key->lladdr, lladdr);
429  }
430 
431  /* Get item in the current table */
432  item = item_from_index(table, index);
433 
434  /* Initialize item data and set "used" bit */
435  memset(item, 0, table->item_size);
436  nbr_set_bit(used_map, table, item, 1);
437 
438 #if DEBUG
439  print_table();
440 #endif
441  return item;
442 }
443 /*---------------------------------------------------------------------------*/
444 /* Get an item from its link-layer address */
445 void *
446 nbr_table_get_from_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
447 {
448  void *item = item_from_index(table, index_from_lladdr(lladdr));
449  return nbr_get_bit(used_map, table, item) ? item : NULL;
450 }
451 /*---------------------------------------------------------------------------*/
452 /* Removes a neighbor from the current table (unset "used" bit) */
453 int
454 nbr_table_remove(nbr_table_t *table, void *item)
455 {
456  int ret = nbr_set_bit(used_map, table, item, 0);
457  nbr_set_bit(locked_map, table, item, 0);
458  return ret;
459 }
460 /*---------------------------------------------------------------------------*/
461 /* Lock a neighbor for the current table (set "locked" bit) */
462 int
463 nbr_table_lock(nbr_table_t *table, void *item)
464 {
465 #if DEBUG
466  int i = index_from_item(table, item);
467  PRINTF("*** Lock %d\n", i);
468 #endif
469  return nbr_set_bit(locked_map, table, item, 1);
470 }
471 /*---------------------------------------------------------------------------*/
472 /* Release the lock on a neighbor for the current table (unset "locked" bit) */
473 int
474 nbr_table_unlock(nbr_table_t *table, void *item)
475 {
476 #if DEBUG
477  int i = index_from_item(table, item);
478  PRINTF("*** Unlock %d\n", i);
479 #endif
480  return nbr_set_bit(locked_map, table, item, 0);
481 }
482 /*---------------------------------------------------------------------------*/
483 /* Get link-layer address of an item */
484 linkaddr_t *
485 nbr_table_get_lladdr(nbr_table_t *table, const void *item)
486 {
487  nbr_table_key_t *key = key_from_item(table, item);
488  return key != NULL ? &key->lladdr : NULL;
489 }
490 /*---------------------------------------------------------------------------*/
491 void
492 nbr_table_clear(void)
493 {
494  nbr_table_key_t *k;
495  /* Delete until nothing left */
496  while((k = list_head(nbr_table_keys))) {
497  remove_key(k, true);
498  }
499 }
500 /*---------------------------------------------------------------------------*/
501 nbr_table_key_t *
502 nbr_table_key_head(void)
503 {
504  return list_head(nbr_table_keys);
505 }
506 /*---------------------------------------------------------------------------*/
507 nbr_table_key_t *
508 nbr_table_key_next(nbr_table_key_t *key)
509 {
510  return list_item_next(key);
511 }
512 /*---------------------------------------------------------------------------*/
513 #if DEBUG
514 static void
515 print_table()
516 {
517  int i, j;
518  /* Printout all neighbors and which tables they are used in */
519  PRINTF("NBR TABLE:\n");
520  for(i = 0; i < NBR_TABLE_MAX_NEIGHBORS; i++) {
521  if(used_map[i] > 0) {
522  PRINTF(" %02d %02d",i , key_from_index(i)->lladdr.u8[LINKADDR_SIZE - 1]);
523  for(j = 0; j < num_tables; j++) {
524  PRINTF(" [%d:%d]", (used_map[i] & (1 << j)) != 0,
525  (locked_map[i] & (1 << j)) != 0);
526  }
527  PRINTF("\n");
528  }
529  }
530 }
531 /*---------------------------------------------------------------------------*/
532 static void
533 handle_periodic_timer(void *ptr)
534 {
535  print_table();
536  ctimer_reset(&periodic_timer);
537 }
538 #endif
static volatile uint64_t count
Num.
Definition: clock.c:50
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:78
A set of debugging macros for the IP stack
void ctimer_reset(struct ctimer *c)
Reset a callback timer with the same interval as was previously set.
Definition: ctimer.c:125
const linkaddr_t linkaddr_null
The null link-layer address.
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
Header file for the callback timer
Linked list manipulation routines.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
Memory block allocation routines.
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a link-layer address.
Definition: linkaddr.c:63
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two link-layer addresses.
Definition: linkaddr.c:69
#define LIST(name)
Declare a linked list.
Definition: list.h:89
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
int memb_numfree(struct memb *m)
Count free memory blocks.
Definition: memb.c:108
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:90