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