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"
48static void handle_periodic_timer(void *ptr);
49static struct ctimer periodic_timer;
50static uint8_t initialized = 0;
51static 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 */
59static uint8_t used_map[NBR_TABLE_MAX_NEIGHBORS];
60/* For each neighbor, a map of the tables that lock the neighbor */
61static 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 */
65static struct nbr_table *all_tables[MAX_NUM_TABLES];
66/* The current number of tables */
67static unsigned num_tables;
68
69/* The neighbor address table */
70MEMB(neighbor_addr_mem, nbr_table_key_t, NBR_TABLE_MAX_NEIGHBORS);
71LIST(nbr_table_keys);
72
73/*---------------------------------------------------------------------------*/
74static void remove_key(nbr_table_key_t *key, bool do_free);
75/*---------------------------------------------------------------------------*/
76/* Get a key from a neighbor index */
77static nbr_table_key_t *
78key_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 */
84static nbr_table_item_t *
85item_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 */
91static int
92index_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 */
98static int
99index_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 */
105static nbr_table_item_t *
106item_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 */
112static nbr_table_key_t *
113key_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 */
119static int
120index_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 */
139static int
140nbr_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 */
153static int
154nbr_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/*---------------------------------------------------------------------------*/
172static void
173remove_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/*---------------------------------------------------------------------------*/
196int
197nbr_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/*---------------------------------------------------------------------------*/
209static int
210used_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/*---------------------------------------------------------------------------*/
227const linkaddr_t *
228nbr_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/*---------------------------------------------------------------------------*/
233bool
234nbr_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/*---------------------------------------------------------------------------*/
242static const linkaddr_t *
243select_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/*---------------------------------------------------------------------------*/
271static bool
272entry_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/*---------------------------------------------------------------------------*/
298bool
299nbr_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/*---------------------------------------------------------------------------*/
305static nbr_table_key_t *
306nbr_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 }
317 nbr_table_key_t *to_be_removed = key_from_index(index_from_lladdr(to_be_removed_lladdr));
318 if(to_be_removed == NULL) {
319 return NULL;
320 }
321 /* Reuse to_be_removed item's spot */
322 remove_key(to_be_removed, false);
323 return to_be_removed;
324 }
325}
326/*---------------------------------------------------------------------------*/
327/* Register a new neighbor table. To be used at initialization by modules
328 * using a neighbor table */
329int
330nbr_table_register(nbr_table_t *table, nbr_table_callback *callback)
331{
332#if DEBUG
333 if(!initialized) {
334 initialized = 1;
335 /* schedule a debug printout per minute */
336 ctimer_set(&periodic_timer, CLOCK_SECOND * 60, handle_periodic_timer, NULL);
337 }
338#endif
339
340 if(nbr_table_is_registered(table)) {
341 /* Table already registered, just update callback */
342 table->callback = callback;
343 return 1;
344 }
345
346 if(num_tables < MAX_NUM_TABLES) {
347 table->index = num_tables++;
348 table->callback = callback;
349 all_tables[table->index] = table;
350 return 1;
351 } else {
352 /* Maximum number of tables exceeded */
353 return 0;
354 }
355}
356/*---------------------------------------------------------------------------*/
357/* Test whether a specified table has been registered or not */
358int
359nbr_table_is_registered(const nbr_table_t *table)
360{
361 if(table != NULL && table->index >= 0 && table->index < MAX_NUM_TABLES
362 && all_tables[table->index] == table) {
363 return 1;
364 }
365 return 0;
366}
367/*---------------------------------------------------------------------------*/
368/* Returns the first item of the current table */
369nbr_table_item_t *
370nbr_table_head(const nbr_table_t *table)
371{
372 /* Get item from first key */
373 nbr_table_item_t *item = item_from_key(table, list_head(nbr_table_keys));
374 /* Item is the first neighbor, now check is it is in the current table */
375 if(nbr_get_bit(used_map, table, item)) {
376 return item;
377 } else {
378 return nbr_table_next(table, item);
379 }
380}
381/*---------------------------------------------------------------------------*/
382/* Iterates over the current table */
383nbr_table_item_t *
384nbr_table_next(const nbr_table_t *table, nbr_table_item_t *item)
385{
386 do {
387 void *key = key_from_item(table, item);
388 key = list_item_next(key);
389 /* Loop until the next item is in the current table */
390 item = item_from_key(table, key);
391 } while(item && !nbr_get_bit(used_map, table, item));
392 return item;
393}
394/*---------------------------------------------------------------------------*/
395/* Add a neighbor indexed with its link-layer address */
396nbr_table_item_t *
397nbr_table_add_lladdr(const nbr_table_t *table, const linkaddr_t *lladdr,
398 nbr_table_reason_t reason, const void *data)
399{
400 int index;
401 nbr_table_item_t *item;
402 nbr_table_key_t *key;
403 const linkaddr_t *to_be_removed;
404
405 if(table == NULL) {
406 return NULL;
407 }
408
409 /* Allow lladdr-free insertion, useful e.g. for IPv6 ND.
410 * Only one such entry is possible at a time, indexed by linkaddr_null. */
411 if(lladdr == NULL) {
412 lladdr = &linkaddr_null;
413 }
414
415 if(!entry_is_allowed(table, lladdr, reason, data, &to_be_removed)) {
416 return NULL;
417 }
418
419 if((index = index_from_lladdr(lladdr)) == -1) {
420 /* Neighbor not yet in table, let's try to allocate one */
421 key = nbr_table_allocate(reason, data, to_be_removed);
422
423 /* No space available for new entry. Should never happen as entry_is_allowed
424 * already checks we can add. */
425 if(key == NULL) {
426 return NULL;
427 }
428
429 /* Add neighbor to list */
430 list_add(nbr_table_keys, key);
431
432 /* Get index from newly allocated neighbor */
433 index = index_from_key(key);
434
435 /* Set link-layer address */
436 linkaddr_copy(&key->lladdr, lladdr);
437 }
438
439 /* Get item in the current table */
440 item = item_from_index(table, index);
441
442 /* Initialize item data and set "used" bit */
443 memset(item, 0, table->item_size);
444 nbr_set_bit(used_map, table, item, 1);
445
446#if DEBUG
447 print_table();
448#endif
449 return item;
450}
451/*---------------------------------------------------------------------------*/
452/* Get an item from its link-layer address */
453void *
454nbr_table_get_from_lladdr(const nbr_table_t *table, const linkaddr_t *lladdr)
455{
456 void *item = item_from_index(table, index_from_lladdr(lladdr));
457 return nbr_get_bit(used_map, table, item) ? item : NULL;
458}
459/*---------------------------------------------------------------------------*/
460/* Removes a neighbor from the current table (unset "used" bit) */
461int
462nbr_table_remove(const nbr_table_t *table, const void *item)
463{
464 int ret = nbr_set_bit(used_map, table, item, 0);
465 nbr_set_bit(locked_map, table, item, 0);
466 return ret;
467}
468/*---------------------------------------------------------------------------*/
469/* Lock a neighbor for the current table (set "locked" bit) */
470int
471nbr_table_lock(const nbr_table_t *table, const void *item)
472{
473#if DEBUG
474 int i = index_from_item(table, item);
475 PRINTF("*** Lock %d\n", i);
476#endif
477 return nbr_set_bit(locked_map, table, item, 1);
478}
479/*---------------------------------------------------------------------------*/
480/* Release the lock on a neighbor for the current table (unset "locked" bit) */
481int
482nbr_table_unlock(const nbr_table_t *table, const void *item)
483{
484#if DEBUG
485 int i = index_from_item(table, item);
486 PRINTF("*** Unlock %d\n", i);
487#endif
488 return nbr_set_bit(locked_map, table, item, 0);
489}
490/*---------------------------------------------------------------------------*/
491/* Get link-layer address of an item */
492linkaddr_t *
493nbr_table_get_lladdr(const nbr_table_t *table, const void *item)
494{
495 nbr_table_key_t *key = key_from_item(table, item);
496 return key != NULL ? &key->lladdr : NULL;
497}
498/*---------------------------------------------------------------------------*/
499void
500nbr_table_clear(void)
501{
502 nbr_table_key_t *k;
503 /* Delete until nothing left */
504 while((k = list_head(nbr_table_keys))) {
505 remove_key(k, true);
506 }
507}
508/*---------------------------------------------------------------------------*/
509nbr_table_key_t *
510nbr_table_key_head(void)
511{
512 return list_head(nbr_table_keys);
513}
514/*---------------------------------------------------------------------------*/
515nbr_table_key_t *
516nbr_table_key_next(const nbr_table_key_t *key)
517{
518 return list_item_next(key);
519}
520/*---------------------------------------------------------------------------*/
521#if DEBUG
522static void
523print_table()
524{
525 int i, j;
526 /* Printout all neighbors and which tables they are used in */
527 PRINTF("NBR TABLE:\n");
528 for(i = 0; i < NBR_TABLE_MAX_NEIGHBORS; i++) {
529 if(used_map[i] > 0) {
530 PRINTF(" %02d %02d",i , key_from_index(i)->lladdr.u8[LINKADDR_SIZE - 1]);
531 for(j = 0; j < num_tables; j++) {
532 PRINTF(" [%d:%d]", (used_map[i] & (1 << j)) != 0,
533 (locked_map[i] & (1 << j)) != 0);
534 }
535 PRINTF("\n");
536 }
537 }
538}
539/*---------------------------------------------------------------------------*/
540static void
541handle_periodic_timer(void *ptr)
542{
543 print_table();
544 ctimer_reset(&periodic_timer);
545}
546#endif
Header file for the callback timer.
static volatile uint64_t count
Num.
Definition: clock.c:50
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
void ctimer_reset(struct ctimer *c)
Reset a callback timer with the same interval as was previously set.
Definition: ctimer.c:125
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a link-layer address.
Definition: linkaddr.c:63
bool linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two link-layer addresses.
Definition: linkaddr.c:69
const linkaddr_t linkaddr_null
The null link-layer address.
#define LIST(name)
Declare a linked list.
Definition: list.h:89
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:89
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition: list.c:152
void * list_item_next(const void *item)
Get the next item following this item.
Definition: list.c:203
void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition: list.c:63
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:78
size_t memb_numfree(struct memb *m)
Count free memory blocks.
Definition: memb.c:108
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:91
Linked list manipulation routines.
Memory block allocation routines.
A set of debugging macros for the IP stack.