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(const 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(const 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(const 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(const nbr_table_t *table, const 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(const 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(const uint8_t *bitmap, const nbr_table_t *table,
141  const nbr_table_item_t *item)
142 {
143  int item_index = index_from_item(table, item);
144  if(table != NULL && item_index != -1) {
145  return (bitmap[item_index] & (1 << table->index)) != 0;
146  } else {
147  return 0;
148  }
149  return 0;
150 }
151 /*---------------------------------------------------------------------------*/
152 /* Set bit in "used" or "locked" bitmap */
153 static int
154 nbr_set_bit(uint8_t *bitmap, const nbr_table_t *table,
155  const nbr_table_item_t *item, int value)
156 {
157  int item_index = index_from_item(table, item);
158 
159  if(table != NULL && item_index != -1) {
160  if(value) {
161  bitmap[item_index] |= 1 << table->index;
162  } else {
163  bitmap[item_index] &= ~(1 << table->index);
164  }
165  return 1;
166  } else {
167  return 0;
168  }
169  return 0;
170 }
171 /*---------------------------------------------------------------------------*/
172 static void
173 remove_key(nbr_table_key_t *key, bool do_free)
174 {
175  int i;
176  for(i = 0; i < MAX_NUM_TABLES; i++) {
177  if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
178  /* Call table callback for each table that uses this item */
179  nbr_table_item_t *removed_item = item_from_key(all_tables[i], key);
180  if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
181  all_tables[i]->callback(removed_item);
182  }
183  }
184  }
185  /* Empty used and locked map */
186  used_map[index_from_key(key)] = 0;
187  locked_map[index_from_key(key)] = 0;
188  /* Remove neighbor from list */
189  list_remove(nbr_table_keys, key);
190  if(do_free) {
191  /* Release the memory */
192  memb_free(&neighbor_addr_mem, key);
193  }
194 }
195 /*---------------------------------------------------------------------------*/
196 int
197 nbr_table_count_entries(void)
198 {
199  int i;
200  int count = 0;
201  for(i = 0; i < NBR_TABLE_MAX_NEIGHBORS; i++) {
202  if(used_map[i] > 0) {
203  count++;
204  }
205  }
206  return count;
207 }
208 /*---------------------------------------------------------------------------*/
209 static int
210 used_count(const linkaddr_t *lladdr)
211 {
212  int item_index = index_from_lladdr(lladdr);
213  int used_count = 0;
214  if(item_index != -1) {
215  int used = used_map[item_index];
216  /* Count how many tables are using this item */
217  while(used != 0) {
218  if((used & 1) == 1) {
219  used_count++;
220  }
221  used >>= 1;
222  }
223  }
224  return used_count;
225 }
226 /*---------------------------------------------------------------------------*/
227 const linkaddr_t *
228 nbr_table_gc_get_worst(const linkaddr_t *lladdr1, const linkaddr_t *lladdr2)
229 {
230  return used_count(lladdr2) < used_count(lladdr1) ? lladdr2 : lladdr1;
231 }
232 /*---------------------------------------------------------------------------*/
233 bool
234 nbr_table_can_accept_new(const linkaddr_t *new,
235  const linkaddr_t *candidate_for_removal,
236  nbr_table_reason_t reason, const void *data)
237 {
238  /* Default behavior: if full, always replace worst entry. */
239  return true;
240 }
241 /*---------------------------------------------------------------------------*/
242 static const linkaddr_t *
243 select_for_removal(const linkaddr_t *new, nbr_table_reason_t reason,
244  const void *data)
245 {
246  nbr_table_key_t *k;
247  const linkaddr_t *worst_lladdr = NULL;
248 
249  /* No more space, try to free a neighbor */
250  for(k = list_head(nbr_table_keys); k != NULL; k = list_item_next(k)) {
251  int item_index = index_from_key(k);
252  int locked = locked_map[item_index];
253  /* Never delete a locked item */
254  if(!locked) {
255  if(worst_lladdr == NULL) {
256  worst_lladdr = &k->lladdr;
257  } else {
258  worst_lladdr = NBR_TABLE_GC_GET_WORST(worst_lladdr, &k->lladdr);
259  }
260  }
261  }
262 
263  /* Finally compare against current candidate for insertion */
264  if(worst_lladdr != NULL && NBR_TABLE_CAN_ACCEPT_NEW(new, worst_lladdr, reason, data)) {
265  return worst_lladdr;
266  } else {
267  return NULL;
268  }
269 }
270 /*---------------------------------------------------------------------------*/
271 static bool
272 entry_is_allowed(const nbr_table_t *table, const linkaddr_t *lladdr,
273  nbr_table_reason_t reason, const void *data,
274  const linkaddr_t **to_be_removed_ptr)
275 {
276  bool ret;
277  const linkaddr_t *to_be_removed = NULL;
278 
279  if(nbr_table_get_from_lladdr(table, lladdr) != NULL) {
280  /* Already present in the given table */
281  ret = true;
282  } else {
283  if(index_from_lladdr(lladdr) != -1
284  || memb_numfree(&neighbor_addr_mem) > 0) {
285  /* lladdr already present globally, or there is space for a new lladdr,
286  * check if entry can be added */
287  ret = NBR_TABLE_CAN_ACCEPT_NEW(lladdr, NULL, reason, data);
288  } else {
289  ret = (to_be_removed = select_for_removal(lladdr, reason, data)) != NULL;
290  }
291  }
292  if(to_be_removed_ptr != NULL) {
293  *to_be_removed_ptr = to_be_removed;
294  }
295  return ret;
296 }
297 /*---------------------------------------------------------------------------*/
298 bool
299 nbr_table_entry_is_allowed(const nbr_table_t *table, const linkaddr_t *lladdr,
300  nbr_table_reason_t reason, const void *data)
301 {
302  return entry_is_allowed(table, lladdr, reason, data, NULL);
303 }
304 /*---------------------------------------------------------------------------*/
305 static nbr_table_key_t *
306 nbr_table_allocate(nbr_table_reason_t reason, const void *data,
307  const linkaddr_t *to_be_removed_lladdr)
308 {
309  nbr_table_key_t *new = memb_alloc(&neighbor_addr_mem);
310  if(new != NULL) {
311  return new;
312  } else {
313  if(to_be_removed_lladdr == NULL) {
314  /* No candidate for GC, allocation fails */
315  return NULL;
316  } else {
317  nbr_table_key_t *to_be_removed = key_from_index(index_from_lladdr(to_be_removed_lladdr));
318  /* Reuse to_be_removed item's spot */
319  remove_key(to_be_removed, false);
320  return to_be_removed;
321  }
322  }
323 }
324 /*---------------------------------------------------------------------------*/
325 /* Register a new neighbor table. To be used at initialization by modules
326  * using a neighbor table */
327 int
328 nbr_table_register(nbr_table_t *table, nbr_table_callback *callback)
329 {
330 #if DEBUG
331  if(!initialized) {
332  initialized = 1;
333  /* schedule a debug printout per minute */
334  ctimer_set(&periodic_timer, CLOCK_SECOND * 60, handle_periodic_timer, NULL);
335  }
336 #endif
337 
338  if(nbr_table_is_registered(table)) {
339  /* Table already registered, just update callback */
340  table->callback = callback;
341  return 1;
342  }
343 
344  if(num_tables < MAX_NUM_TABLES) {
345  table->index = num_tables++;
346  table->callback = callback;
347  all_tables[table->index] = table;
348  return 1;
349  } else {
350  /* Maximum number of tables exceeded */
351  return 0;
352  }
353 }
354 /*---------------------------------------------------------------------------*/
355 /* Test whether a specified table has been registered or not */
356 int
357 nbr_table_is_registered(const nbr_table_t *table)
358 {
359  if(table != NULL && table->index >= 0 && table->index < MAX_NUM_TABLES
360  && all_tables[table->index] == table) {
361  return 1;
362  }
363  return 0;
364 }
365 /*---------------------------------------------------------------------------*/
366 /* Returns the first item of the current table */
367 nbr_table_item_t *
368 nbr_table_head(const nbr_table_t *table)
369 {
370  /* Get item from first key */
371  nbr_table_item_t *item = item_from_key(table, list_head(nbr_table_keys));
372  /* Item is the first neighbor, now check is it is in the current table */
373  if(nbr_get_bit(used_map, table, item)) {
374  return item;
375  } else {
376  return nbr_table_next(table, item);
377  }
378 }
379 /*---------------------------------------------------------------------------*/
380 /* Iterates over the current table */
381 nbr_table_item_t *
382 nbr_table_next(const nbr_table_t *table, nbr_table_item_t *item)
383 {
384  do {
385  void *key = key_from_item(table, item);
386  key = list_item_next(key);
387  /* Loop until the next item is in the current table */
388  item = item_from_key(table, key);
389  } while(item && !nbr_get_bit(used_map, table, item));
390  return item;
391 }
392 /*---------------------------------------------------------------------------*/
393 /* Add a neighbor indexed with its link-layer address */
394 nbr_table_item_t *
395 nbr_table_add_lladdr(const nbr_table_t *table, const linkaddr_t *lladdr,
396  nbr_table_reason_t reason, const void *data)
397 {
398  int index;
399  nbr_table_item_t *item;
400  nbr_table_key_t *key;
401  const linkaddr_t *to_be_removed;
402 
403  if(table == NULL) {
404  return NULL;
405  }
406 
407  /* Allow lladdr-free insertion, useful e.g. for IPv6 ND.
408  * Only one such entry is possible at a time, indexed by linkaddr_null. */
409  if(lladdr == NULL) {
410  lladdr = &linkaddr_null;
411  }
412 
413  if(!entry_is_allowed(table, lladdr, reason, data, &to_be_removed)) {
414  return NULL;
415  }
416 
417  if((index = index_from_lladdr(lladdr)) == -1) {
418  /* Neighbor not yet in table, let's try to allocate one */
419  key = nbr_table_allocate(reason, data, to_be_removed);
420 
421  /* No space available for new entry. Should never happen as entry_is_allowed
422  * already checks we can add. */
423  if(key == NULL) {
424  return NULL;
425  }
426 
427  /* Add neighbor to list */
428  list_add(nbr_table_keys, key);
429 
430  /* Get index from newly allocated neighbor */
431  index = index_from_key(key);
432 
433  /* Set link-layer address */
434  linkaddr_copy(&key->lladdr, lladdr);
435  }
436 
437  /* Get item in the current table */
438  item = item_from_index(table, index);
439 
440  /* Initialize item data and set "used" bit */
441  memset(item, 0, table->item_size);
442  nbr_set_bit(used_map, table, item, 1);
443 
444 #if DEBUG
445  print_table();
446 #endif
447  return item;
448 }
449 /*---------------------------------------------------------------------------*/
450 /* Get an item from its link-layer address */
451 void *
452 nbr_table_get_from_lladdr(const nbr_table_t *table, const linkaddr_t *lladdr)
453 {
454  void *item = item_from_index(table, index_from_lladdr(lladdr));
455  return nbr_get_bit(used_map, table, item) ? item : NULL;
456 }
457 /*---------------------------------------------------------------------------*/
458 /* Removes a neighbor from the current table (unset "used" bit) */
459 int
460 nbr_table_remove(const nbr_table_t *table, const void *item)
461 {
462  int ret = nbr_set_bit(used_map, table, item, 0);
463  nbr_set_bit(locked_map, table, item, 0);
464  return ret;
465 }
466 /*---------------------------------------------------------------------------*/
467 /* Lock a neighbor for the current table (set "locked" bit) */
468 int
469 nbr_table_lock(const nbr_table_t *table, const void *item)
470 {
471 #if DEBUG
472  int i = index_from_item(table, item);
473  PRINTF("*** Lock %d\n", i);
474 #endif
475  return nbr_set_bit(locked_map, table, item, 1);
476 }
477 /*---------------------------------------------------------------------------*/
478 /* Release the lock on a neighbor for the current table (unset "locked" bit) */
479 int
480 nbr_table_unlock(const nbr_table_t *table, const void *item)
481 {
482 #if DEBUG
483  int i = index_from_item(table, item);
484  PRINTF("*** Unlock %d\n", i);
485 #endif
486  return nbr_set_bit(locked_map, table, item, 0);
487 }
488 /*---------------------------------------------------------------------------*/
489 /* Get link-layer address of an item */
490 linkaddr_t *
491 nbr_table_get_lladdr(const nbr_table_t *table, const void *item)
492 {
493  nbr_table_key_t *key = key_from_item(table, item);
494  return key != NULL ? &key->lladdr : NULL;
495 }
496 /*---------------------------------------------------------------------------*/
497 void
498 nbr_table_clear(void)
499 {
500  nbr_table_key_t *k;
501  /* Delete until nothing left */
502  while((k = list_head(nbr_table_keys))) {
503  remove_key(k, true);
504  }
505 }
506 /*---------------------------------------------------------------------------*/
507 nbr_table_key_t *
508 nbr_table_key_head(void)
509 {
510  return list_head(nbr_table_keys);
511 }
512 /*---------------------------------------------------------------------------*/
513 nbr_table_key_t *
514 nbr_table_key_next(const nbr_table_key_t *key)
515 {
516  return list_item_next(key);
517 }
518 /*---------------------------------------------------------------------------*/
519 #if DEBUG
520 static void
521 print_table()
522 {
523  int i, j;
524  /* Printout all neighbors and which tables they are used in */
525  PRINTF("NBR TABLE:\n");
526  for(i = 0; i < NBR_TABLE_MAX_NEIGHBORS; i++) {
527  if(used_map[i] > 0) {
528  PRINTF(" %02d %02d",i , key_from_index(i)->lladdr.u8[LINKADDR_SIZE - 1]);
529  for(j = 0; j < num_tables; j++) {
530  PRINTF(" [%d:%d]", (used_map[i] & (1 << j)) != 0,
531  (locked_map[i] & (1 << j)) != 0);
532  }
533  PRINTF("\n");
534  }
535  }
536 }
537 /*---------------------------------------------------------------------------*/
538 static void
539 handle_periodic_timer(void *ptr)
540 {
541  print_table();
542  ctimer_reset(&periodic_timer);
543 }
544 #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(const list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition: list.c:237
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
void * list_item_next(const void *item)
Get the next item following this item.
Definition: list.c:322
int memb_numfree(struct memb *m)
Count free memory blocks.
Definition: memb.c:108
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:90