diff --git a/Makefile.dep b/Makefile.dep index 730597589b..1aff898994 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -124,3 +124,9 @@ endif ifneq (,$(filter defaulttransceiver,$(USEMODULE))) FEATURES_REQUIRED += transceiver endif + +ifneq (,$(filter nhdp,$(USEMODULE))) + USEMODULE += vtimer + USEMODULE += oonf_common + USEMODULE += oonf_rfc5444 +endif diff --git a/sys/Makefile b/sys/Makefile index 603c7725ee..b13309d055 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -83,6 +83,9 @@ endif ifneq (,$(filter trickle,$(USEMODULE))) DIRS += trickle endif +ifneq (,$(filter nhdp,$(USEMODULE))) + DIRS += net/routing/nhdp +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE}))) diff --git a/sys/Makefile.include b/sys/Makefile.include index 25e4fca899..de8b66de52 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -38,6 +38,10 @@ ifneq (,$(filter rpl,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/routing/rpl endif +ifneq (,$(filter nhdp,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include + USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/routing/nhdp +endif ifneq (,$(filter ieee802154,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include endif diff --git a/sys/net/routing/nhdp/Makefile b/sys/net/routing/nhdp/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/routing/nhdp/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/routing/nhdp/iib_table.c b/sys/net/routing/nhdp/iib_table.c new file mode 100644 index 0000000000..366d24166c --- /dev/null +++ b/sys/net/routing/nhdp/iib_table.c @@ -0,0 +1,732 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Interface Information Base implementation for NHDP + * + * @author Fabian Nack + * + * @} + */ + +#include "mutex.h" +#include "timex.h" +#include "vtimer.h" +#include "utlist.h" +#include "kernel_types.h" + +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_writer.h" + +#include "iib_table.h" +#include "nhdp_address.h" +#include "nhdp_writer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* Internal variables */ +static mutex_t mtx_iib_access = MUTEX_INIT; +static iib_base_entry_t *iib_base_entry_head = NULL; + +/* Internal function prototypes */ +static void rem_link_set_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry); +static void cleanup_link_sets(nhdp_addr_entry_t *rem_list); +static iib_link_set_entry_t *add_default_link_set_entry(iib_base_entry_t *base_entry, timex_t *now, + uint64_t val_time); +static void reset_link_set_entry(iib_link_set_entry_t *ls_entry, timex_t *now, uint64_t val_time); +static iib_link_set_entry_t *update_link_set(iib_base_entry_t *base_entry, nib_entry_t *nb_elt, + nhdp_addr_entry_t *send_list, timex_t *now, + uint64_t val_time, uint8_t sym, uint8_t lost); +static void release_link_tuple_addresses(iib_link_set_entry_t *ls_entry); + +static int update_two_hop_set(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry, + nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list, + timex_t *now, uint64_t val_time); +static uint8_t rem_exst_th_entries(iib_base_entry_t *base_entry, iib_two_hop_set_entry_t *ts_elt, + nhdp_addr_entry_t *th_sym_list); +static uint8_t rem_non_sym_th_entries(iib_base_entry_t *base_entry, + iib_two_hop_set_entry_t *ts_elt, + nhdp_addr_entry_t *th_rem_list); +static int add_two_hop_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry, + nhdp_addr_t *th_addr, timex_t *now, uint64_t val_time); +static void rem_two_hop_entry(iib_base_entry_t *base_entry, iib_two_hop_set_entry_t *th_entry); + +static void wr_update_ls_status(iib_base_entry_t *base_entry, + iib_link_set_entry_t *ls_elt, timex_t *now); +static void update_nb_tuple_symmetry(iib_base_entry_t *base_entry, + iib_link_set_entry_t *ls_entry, timex_t *now); +static void rem_not_heard_nb_tuple(iib_link_set_entry_t *ls_entry, timex_t *now); + +static inline timex_t get_max_timex(timex_t time_one, timex_t time_two); +static iib_link_tuple_status_t get_tuple_status(iib_link_set_entry_t *ls_entry, timex_t *now); + + +/*---------------------------------------------------------------------------* + * Interface Information Base API * + *---------------------------------------------------------------------------*/ + +int iib_register_if(kernel_pid_t pid) +{ + iib_base_entry_t *new_entry = (iib_base_entry_t *) malloc(sizeof(iib_base_entry_t)); + + if (!new_entry) { + /* Insufficient memory */ + return -1; + } + + new_entry->if_pid = pid; + new_entry->link_set_head = NULL; + new_entry->two_hop_set_head = NULL; + LL_PREPEND(iib_base_entry_head, new_entry); + + return 0; +} + +int iib_process_hello(kernel_pid_t if_pid, nib_entry_t *nb_elt, nhdp_addr_entry_t *send_list, + nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list, + nhdp_addr_entry_t *rem_list, uint64_t validity_time, uint8_t is_sym_nb, + uint8_t is_lost) +{ + iib_base_entry_t *base_elt; + timex_t now; + + mutex_lock(&mtx_iib_access); + + /* Remove link tuple addresses that are included in the Removed Addr List */ + cleanup_link_sets(rem_list); + + LL_FOREACH(iib_base_entry_head, base_elt) { + /* Find the link set and two hop set for the interface */ + if (base_elt->if_pid == if_pid) { + break; + } + } + + if (base_elt) { + vtimer_now(&now); + + /* Create a new link tuple for the neighbor that originated the hello */ + iib_link_set_entry_t *ls_entry = update_link_set(base_elt, nb_elt, send_list, + &now, validity_time, is_sym_nb, is_lost); + + /* Create new two hop tuples for signaled symmetric neighbors */ + if (ls_entry) { + update_two_hop_set(base_elt, ls_entry, th_sym_list, th_rem_list, &now, validity_time); + } + } + + mutex_unlock(&mtx_iib_access); + + return 0; +} + +void iib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr) +{ + iib_base_entry_t *base_elt; + iib_link_set_entry_t *ls_elt; + nhdp_addr_entry_t *addr_elt; + timex_t now; + + mutex_lock(&mtx_iib_access); + + vtimer_now(&now); + + /* Before adding addresses first update the status of all link tuples */ + iib_update_lt_status(&now); + + /* Add all addresses of Link Tuples of the given interface's Link Set to the current HELLO */ + LL_FOREACH(iib_base_entry_head, base_elt) { + if (base_elt->if_pid == if_pid) { + LL_FOREACH(base_elt->link_set_head, ls_elt) { + if (ls_elt->last_status != IIB_LT_STATUS_PENDING) { + /* Exclude addresses from tuples with L_STATUS = PENDING */ + LL_FOREACH(ls_elt->address_list_head, addr_elt) { + if (!NHDP_ADDR_TMP_IN_ANY(addr_elt->address)) { + /* Add address to the writers next packet */ + switch (ls_elt->last_status) { + case IIB_LT_STATUS_SYM: + nhdp_writer_add_addr(wr, addr_elt->address, + RFC5444_ADDRTLV_LINK_STATUS, + RFC5444_LINKSTATUS_SYMMETRIC); + addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_SYM; + break; + + case IIB_LT_STATUS_HEARD: + nhdp_writer_add_addr(wr, addr_elt->address, + RFC5444_ADDRTLV_LINK_STATUS, + RFC5444_LINKSTATUS_HEARD); + addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_ANY; + break; + + case IIB_LT_STATUS_UNKNOWN: + /* Fall through */ + + case IIB_LT_STATUS_LOST: + nhdp_writer_add_addr(wr, addr_elt->address, + RFC5444_ADDRTLV_LINK_STATUS, + RFC5444_LINKSTATUS_LOST); + addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_ANY; + break; + + case IIB_LT_STATUS_PENDING: + /* Pending link tuples are not included */ + break; + + default: + /* Should not happen */ + DEBUGF("[WARNING] Unknown link tuple status\n"); + break; + } + } + } + } + } + /* IF's link set found */ + break; + } + } + + mutex_unlock(&mtx_iib_access); +} + +void iib_update_lt_status(timex_t *now) +{ + iib_base_entry_t *base_elt; + iib_link_set_entry_t *ls_elt, *ls_tmp; + + LL_FOREACH(iib_base_entry_head, base_elt) { + LL_FOREACH_SAFE(base_elt->link_set_head, ls_elt, ls_tmp) { + wr_update_ls_status(base_elt, ls_elt, now); + } + } +} + +void iib_propagate_nb_entry_change(nib_entry_t *old_entry, nib_entry_t *new_entry) +{ + iib_base_entry_t *base_elt; + iib_link_set_entry_t *ls_elt; + LL_FOREACH(iib_base_entry_head, base_elt) { + LL_FOREACH(base_elt->link_set_head, ls_elt) { + if (ls_elt->nb_elt == old_entry) { + ls_elt->nb_elt = new_entry; + } + } + } +} + + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +/** + * Remove addresses included in the Removed Address List from all existing Link Tuples + */ +static void cleanup_link_sets(nhdp_addr_entry_t *rem_list) +{ + /* Check whether the Removed Address List is not empty */ + if (!rem_list) { + return; + } + + /* Loop through all link sets */ + iib_base_entry_t *base_elt; + LL_FOREACH(iib_base_entry_head, base_elt) { + /* Loop through all link tuples of the link set */ + iib_link_set_entry_t *ls_elt, *ls_tmp; + LL_FOREACH_SAFE(base_elt->link_set_head, ls_elt, ls_tmp) { + /* Loop through all addresses of the link tuples */ + nhdp_addr_entry_t *lt_elt; + LL_FOREACH(ls_elt->address_list_head, lt_elt) { + /* Loop through all addresses of the Removed Addr List */ + nhdp_addr_entry_t *rem_elt; + LL_FOREACH(rem_list, rem_elt) { + /* Remove link tuple address if included in the Removed Addr List */ + if (lt_elt->address == rem_elt->address) { + /* Addresses are equal (same NHDP address db entry) */ + LL_DELETE(ls_elt->address_list_head, lt_elt); + nhdp_free_addr_entry(lt_elt); + break; + } + } + } + + /* Remove link tuples with empty address list */ + if (!ls_elt->address_list_head) { + if (ls_elt->last_status == IIB_LT_STATUS_SYM) { + /* Remove all two hop entries for the corresponding link tuple */ + iib_two_hop_set_entry_t *th_elt, *th_tmp; + LL_FOREACH_SAFE(base_elt->two_hop_set_head, th_elt, th_tmp) { + if (th_elt->ls_elt == ls_elt) { + rem_two_hop_entry(base_elt, th_elt); + } + } + } + + rem_link_set_entry(base_elt, ls_elt); + } + } + } +} + +/** + * Update the Link Set for the receiving interface during HELLO message processing + */ +static iib_link_set_entry_t *update_link_set(iib_base_entry_t *base_entry, nib_entry_t *nb_elt, + nhdp_addr_entry_t *send_list, timex_t *now, + uint64_t val_time, uint8_t sym, uint8_t lost) +{ + iib_link_set_entry_t *ls_elt, *ls_tmp; + iib_link_set_entry_t *matching_lt = NULL; + nhdp_addr_entry_t *lt_elt, *send_elt; + timex_t v_time, l_hold; + uint8_t matches = 0; + + /* Loop through every link tuple of the interface to update the link set */ + LL_FOREACH_SAFE(base_entry->link_set_head, ls_elt, ls_tmp) { + /* Loop through all addresses of the link tuple */ + LL_FOREACH(ls_elt->address_list_head, lt_elt) { + /* Loop through all addresses of the Sending Addr List */ + LL_FOREACH(send_list, send_elt) { + /* If link tuple address matches a sending addr we found a fitting tuple */ + if (lt_elt->address == send_elt->address) { + /* Addresses are equal (same NHDP address db entry) */ + matches++; + + if (matches > 1) { + /* Multiple matching link tuples, delete the previous one */ + if (matching_lt->last_status == IIB_LT_STATUS_SYM) { + update_nb_tuple_symmetry(base_entry, matching_lt, now); + } + + rem_link_set_entry(base_entry, matching_lt); + } + + matching_lt = ls_elt; + break; + } + + if (matching_lt == ls_elt) { + /* This link tuple is already detected as matching */ + break; + } + } + } + } + + if (matches > 1) { + /* Multiple matching link tuples, reset the last one for reuse */ + if (matching_lt->last_status == IIB_LT_STATUS_SYM) { + update_nb_tuple_symmetry(base_entry, matching_lt, now); + } + + reset_link_set_entry(matching_lt, now, val_time); + } + else if (matches == 1) { + /* A single matching link tuple, only release the address list */ + release_link_tuple_addresses(matching_lt); + } + else { + /* No single matching link tuple existant, create a new one */ + matching_lt = add_default_link_set_entry(base_entry, now, val_time); + + if (!matching_lt) { + /* Insufficient memory */ + return NULL; + } + } + + v_time = timex_from_uint64(val_time * MS_IN_USEC); + l_hold = timex_from_uint64(((uint64_t)NHDP_L_HOLD_TIME_MS) * MS_IN_USEC); + + /* Set Sending Address List as this tuples address list */ + matching_lt->address_list_head = nhdp_generate_new_addr_list(send_list); + + if (!matching_lt->address_list_head) { + /* Insufficient memory */ + rem_link_set_entry(base_entry, matching_lt); + return NULL; + } + + matching_lt->nb_elt = nb_elt; + + /* Set values dependent on link status */ + if (sym) { + if (matching_lt->last_status != IIB_LT_STATUS_SYM) { + /* Set corresponding neighbor tuple to symmetric (Section 13.1 of RFC 6130) */ + if (matching_lt->nb_elt) { + nib_set_nb_entry_sym(matching_lt->nb_elt); + } + } + + matching_lt->sym_time = timex_add(*now, v_time); + matching_lt->last_status = IIB_LT_STATUS_SYM; + } + else if (lost) { + matching_lt->sym_time.microseconds = 0; + matching_lt->sym_time.seconds = 0; + + if (matching_lt->last_status == IIB_LT_STATUS_SYM) { + update_nb_tuple_symmetry(base_entry, matching_lt, now); + } + + if (get_tuple_status(matching_lt, now) == IIB_LT_STATUS_HEARD) { + matching_lt->last_status = IIB_LT_STATUS_HEARD; + matching_lt->exp_time = timex_add(*now, l_hold); + } + else { + matching_lt->last_status = IIB_LT_STATUS_UNKNOWN; + } + } + + /* Set time values */ + matching_lt->heard_time = get_max_timex(timex_add(*now, v_time), matching_lt->sym_time); + + if (matching_lt->pending) { + /* L_status is PENDING */ + matching_lt->exp_time = get_max_timex(matching_lt->exp_time, matching_lt->heard_time); + } + else if (!matching_lt->lost) { + if ((timex_cmp(matching_lt->sym_time, *now) == 1) + || (timex_cmp(matching_lt->heard_time, *now) == 1)) { + /* L_status is HEARD or SYMMETRIC */ + matching_lt->exp_time = get_max_timex(matching_lt->exp_time, + timex_add(matching_lt->heard_time, l_hold)); + } + } + + return matching_lt; +} + +/** + * Update the status of a link tuple and process necessary changes and execute + * necessary changes in the 2-Hop Set and in the Neighbor Information Base + * Implements logic of Section 13 of RFC 6130 + */ +static void wr_update_ls_status(iib_base_entry_t *base_entry, + iib_link_set_entry_t *ls_elt, timex_t *now) +{ + if (timex_cmp(ls_elt->exp_time, *now) != 1) { + /* Entry expired and has to be removed */ + if (ls_elt->last_status == IIB_LT_STATUS_SYM) { + update_nb_tuple_symmetry(base_entry, ls_elt, now); + } + + rem_not_heard_nb_tuple(ls_elt, now); + rem_link_set_entry(base_entry, ls_elt); + } + else if ((ls_elt->last_status == IIB_LT_STATUS_SYM) + && (timex_cmp(ls_elt->sym_time, *now) != 1)) { + /* Status changed from SYMMETRIC to HEARD */ + update_nb_tuple_symmetry(base_entry, ls_elt, now); + ls_elt->last_status = IIB_LT_STATUS_HEARD; + + if (timex_cmp(ls_elt->heard_time, *now) != 1) { + /* New status is LOST (equals IIB_LT_STATUS_UNKNOWN) */ + rem_not_heard_nb_tuple(ls_elt, now); + ls_elt->nb_elt = NULL; + ls_elt->last_status = IIB_LT_STATUS_UNKNOWN; + } + } + else if ((ls_elt->last_status == IIB_LT_STATUS_HEARD) + && (timex_cmp(ls_elt->heard_time, *now) != 1)) { + /* Status changed from HEARD to LOST (equals IIB_LT_STATUS_UNKNOWN) */ + rem_not_heard_nb_tuple(ls_elt, now); + ls_elt->nb_elt = NULL; + ls_elt->last_status = IIB_LT_STATUS_UNKNOWN; + } +} + +/** + * Add a new Link Tuple with default values to the given Link Set + */ +static iib_link_set_entry_t *add_default_link_set_entry(iib_base_entry_t *base_entry, timex_t *now, + uint64_t val_time) +{ + iib_link_set_entry_t *new_entry; + timex_t v_time = timex_from_uint64(val_time * MS_IN_USEC); + + new_entry = (iib_link_set_entry_t *) malloc(sizeof(iib_link_set_entry_t)); + + if (!new_entry) { + /* Insufficient memory */ + return NULL; + } + + new_entry->address_list_head = NULL; + new_entry->heard_time.microseconds = 0; + new_entry->heard_time.seconds = 0; + new_entry->sym_time.microseconds = 0; + new_entry->sym_time.seconds = 0; + new_entry->pending = NHDP_INITIAL_PENDING; + new_entry->lost = 0; + new_entry->exp_time = timex_add(*now, v_time); + new_entry->last_status = IIB_LT_STATUS_UNKNOWN; + new_entry->nb_elt = NULL; + LL_PREPEND(base_entry->link_set_head, new_entry); + + return new_entry; +} + +/** + * Reset a given Link Tuple for reusage + */ +static void reset_link_set_entry(iib_link_set_entry_t *ls_entry, timex_t *now, uint64_t val_time) +{ + timex_t v_time = timex_from_uint64(val_time * MS_IN_USEC); + + release_link_tuple_addresses(ls_entry); + ls_entry->sym_time.microseconds = 0; + ls_entry->sym_time.seconds = 0; + ls_entry->heard_time.microseconds = 0; + ls_entry->heard_time.seconds = 0; + ls_entry->pending = NHDP_INITIAL_PENDING; + ls_entry->lost = 0; + ls_entry->exp_time = timex_add(*now, v_time); + ls_entry->nb_elt = NULL; + ls_entry->last_status = IIB_LT_STATUS_UNKNOWN; +} + +/** + * Remove a given Link Tuple + */ +static void rem_link_set_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry) +{ + LL_DELETE(base_entry->link_set_head, ls_entry); + release_link_tuple_addresses(ls_entry); + free(ls_entry); +} + +/** + * Free all address entries of a link tuple + */ +static void release_link_tuple_addresses(iib_link_set_entry_t *ls_entry) +{ + nhdp_free_addr_list(ls_entry->address_list_head); + ls_entry->address_list_head = NULL; +} + +/** + * Update the 2-Hop Set during HELLO message processing + */ +static int update_two_hop_set(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry, + nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list, + timex_t *now, uint64_t val_time) +{ + /* Check whether a corresponding link tuple was created */ + if (ls_entry == NULL) { + return -1; + } + + /* If the link to the neighbor is still symmetric */ + if (get_tuple_status(ls_entry, now) == IIB_LT_STATUS_SYM) { + iib_two_hop_set_entry_t *ths_elt, *ths_tmp; + nhdp_addr_entry_t *sym_elt; + + /* Loop through all the two hop tuples of the two hop set */ + LL_FOREACH_SAFE(base_entry->two_hop_set_head, ths_elt, ths_tmp) { + if (timex_cmp(ths_elt->exp_time, *now) != 1) { + /* Entry is expired, remove it */ + rem_two_hop_entry(base_entry, ths_elt); + } + else if (ths_elt->ls_elt == ls_entry) { + if (!rem_non_sym_th_entries(base_entry, ths_elt, th_rem_list)) { + rem_exst_th_entries(base_entry, ths_elt, th_sym_list); + } + } + } + + /* Add a new entry for every signaled symmetric neighbor address */ + LL_FOREACH(th_sym_list, sym_elt) { + if (add_two_hop_entry(base_entry, ls_entry, sym_elt->address, now, val_time)) { + /* No more memory available, return error */ + return -1; + } + } + } + + return 0; +} + +/** + * Remove an existing 2-Hop Tuple if its address was signalled as lost in the received HELLO + */ +static uint8_t rem_non_sym_th_entries(iib_base_entry_t *base_entry, + iib_two_hop_set_entry_t *ts_elt, + nhdp_addr_entry_t *th_rem_list) +{ + nhdp_addr_entry_t *rem_elt; + + /* Remove the given two hop entry if it was signaled as lost */ + LL_FOREACH(th_rem_list, rem_elt) { + if (ts_elt->th_nb_addr == rem_elt->address) { + /* Addresses are equal (same NHDP address db entry) */ + rem_two_hop_entry(base_entry, ts_elt); + return 1; + } + } + return 0; +} + +/** + * Remove an existing 2-Hop Tuple if it is present in the 2-Hop symmetric address + * list of the received HELLO + */ +static uint8_t rem_exst_th_entries(iib_base_entry_t *base_entry, + iib_two_hop_set_entry_t *ts_elt, nhdp_addr_entry_t *th_sym_list) +{ + nhdp_addr_entry_t *sym_elt; + + /* Remove the given two hop entry if it was signaled as symmetric (new one is added) */ + LL_FOREACH(th_sym_list, sym_elt) { + if (ts_elt->th_nb_addr == sym_elt->address) { + /* Addresses are equal (same NHDP address db entry) */ + rem_two_hop_entry(base_entry, ts_elt); + return 1; + } + } + return 0; +} + +/** + * Add a 2-Hop Tuple for a given address + */ +static int add_two_hop_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry, + nhdp_addr_t *th_addr, timex_t *now, uint64_t val_time) +{ + iib_two_hop_set_entry_t *new_entry; + timex_t v_time = timex_from_uint64(val_time * MS_IN_USEC); + + new_entry = (iib_two_hop_set_entry_t *) malloc(sizeof(iib_two_hop_set_entry_t)); + + if (!new_entry) { + /* Insufficient memory */ + return -1; + } + + /* Increment usage counter of address in central NHDP address storage */ + th_addr->usg_count++; + new_entry->th_nb_addr = th_addr; + new_entry->ls_elt = ls_entry; + new_entry->exp_time = timex_add(*now, v_time); + LL_PREPEND(base_entry->two_hop_set_head, new_entry); + + return 0; +} + +/** + * Remove a given 2-Hop Tuple + */ +static void rem_two_hop_entry(iib_base_entry_t *base_entry, iib_two_hop_set_entry_t *th_entry) +{ + LL_DELETE(base_entry->two_hop_set_head, th_entry); + nhdp_decrement_addr_usage(th_entry->th_nb_addr); + free(th_entry); +} + +/** + * Remove all corresponding two hop entries for a given link tuple that lost symmetry status. + * Additionally reset the neighbor tuple's symmmetry flag (for the neighbor tuple this link + * tuple is represented in), if no more corresponding symmetric link tuples are left. + * Implements section 13.2 of RFC 6130 + */ +static void update_nb_tuple_symmetry(iib_base_entry_t *base_entry, + iib_link_set_entry_t *ls_entry, timex_t *now) +{ + iib_two_hop_set_entry_t *th_elt, *th_tmp; + + /* First remove all two hop entries for the corresponding link tuple */ + LL_FOREACH_SAFE(base_entry->two_hop_set_head, th_elt, th_tmp) { + if (th_elt->ls_elt == ls_entry) { + rem_two_hop_entry(base_entry, th_elt); + } + } + + /* Afterwards check the neighbor tuple containing the link tuple's addresses */ + if ((ls_entry->nb_elt != NULL) && (ls_entry->nb_elt->symmetric == 1)) { + iib_base_entry_t *base_tmp; + LL_FOREACH(iib_base_entry_head, base_tmp) { + iib_link_set_entry_t *ls_tmp; + LL_FOREACH(base_tmp->link_set_head, ls_tmp) { + if ((ls_entry->nb_elt == ls_tmp->nb_elt) && (ls_entry != ls_tmp)) { + if (timex_cmp(ls_tmp->sym_time, *now) == 1) { + return; + } + } + } + } + + /* No remaining symmetric link tuple for the neighbor tuple */ + nib_reset_nb_entry_sym(ls_entry->nb_elt, now); + } +} + +/** + * Remove a neighbor tuple if no more corresponding heard link tuples are left + * Implements section 13.3 of RFC 6130 + */ +static void rem_not_heard_nb_tuple(iib_link_set_entry_t *ls_entry, timex_t *now) +{ + /* Check whether the corresponding neighbor tuple still exists */ + if (ls_entry->nb_elt) { + iib_base_entry_t *base_tmp; + LL_FOREACH(iib_base_entry_head, base_tmp) { + iib_link_set_entry_t *ls_tmp; + LL_FOREACH(base_tmp->link_set_head, ls_tmp) { + if ((ls_entry->nb_elt == ls_tmp->nb_elt) && (ls_entry != ls_tmp)) { + if (timex_cmp(ls_tmp->heard_time, *now) == 1) { + return; + } + + ls_tmp->nb_elt = NULL; + } + } + } + + /* No remaining heard link tuple for the neighbor tuple */ + nib_rem_nb_entry(ls_entry->nb_elt); + } +} + +/** + * Get the L_STATUS value of a given link tuple + */ +static iib_link_tuple_status_t get_tuple_status(iib_link_set_entry_t *ls_entry, timex_t *now) +{ + if (ls_entry->pending) { + return IIB_LT_STATUS_PENDING; + } + else if (ls_entry->lost) { + return IIB_LT_STATUS_LOST; + } + else if (timex_cmp(ls_entry->sym_time, *now) == 1) { + return IIB_LT_STATUS_SYM; + } + else if (timex_cmp(ls_entry->heard_time, *now) == 1) { + return IIB_LT_STATUS_HEARD; + } + + return IIB_LT_STATUS_UNKNOWN; +} + +/** + * Get the later one of two timex representation + */ +static inline timex_t get_max_timex(timex_t time_one, timex_t time_two) +{ + if (timex_cmp(time_one, time_two) != -1) { + return time_one; + } + + return time_two; +} diff --git a/sys/net/routing/nhdp/iib_table.h b/sys/net/routing/nhdp/iib_table.h new file mode 100644 index 0000000000..77e63ff8d7 --- /dev/null +++ b/sys/net/routing/nhdp/iib_table.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Interface Information Base interface for NHDP + * + * @author Fabian Nack + */ + +#ifndef IIB_TABLE_H_ +#define IIB_TABLE_H_ + +#include "timex.h" +#include "kernel_types.h" + +#include "nib_table.h" +#include "nhdp_address.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Possible L_STATUS values of a link tuple + */ +typedef enum iib_link_tuple_status_t { + IIB_LT_STATUS_PENDING, + IIB_LT_STATUS_LOST, + IIB_LT_STATUS_HEARD, + IIB_LT_STATUS_SYM, + IIB_LT_STATUS_UNKNOWN +} iib_link_tuple_status_t; + +/** + * @brief Link Set entry (link tuple) + */ +typedef struct iib_link_set_entry_t { + nhdp_addr_entry_t *address_list_head; /**< Pointer to head of this tuple's addresses */ + timex_t heard_time; /**< Time at which entry leaves heard status */ + timex_t sym_time; /**< Time at which entry leaves symmetry status */ + uint8_t pending; /**< Flag whether link is pending */ + uint8_t lost; /**< Flag whether link is lost */ + timex_t exp_time; /**< Time at which entry expires */ + nib_entry_t *nb_elt; /**< Pointer to corresponding nb tuple */ + enum iib_link_tuple_status_t last_status; /**< Last processed status of link tuple */ + struct iib_link_set_entry_t *next; /**< Pointer to next list entry */ +} iib_link_set_entry_t; + +/** + * @brief 2-Hop Set entry (2-Hop tuple) + */ +typedef struct iib_two_hop_set_entry_t { + struct iib_link_set_entry_t *ls_elt; /**< Pointer to corresponding link tuple */ + nhdp_addr_t *th_nb_addr; /**< Address of symmetric 2-hop neighbor */ + timex_t exp_time; /**< Time at which entry expires */ + struct iib_two_hop_set_entry_t *next; /**< Pointer to next list entry */ +} iib_two_hop_set_entry_t; + +/** + * @brief Link set for a registered interface + */ +typedef struct iib_base_entry_t { + kernel_pid_t if_pid; /**< PID of the interface */ + struct iib_link_set_entry_t *link_set_head; /**< Pointer to this if's link tuples */ + struct iib_two_hop_set_entry_t *two_hop_set_head; /**< Pointer to this if's 2-hop tuples */ + struct iib_base_entry_t *next; /**< Pointer to next list entry */ +} iib_base_entry_t; + +/** + * @brief Register a new interface in the IIB + * + * This function creates a new empty Link Set and a new empty 2-Hop Set for the + * given interface. + * + * @param[in] pid PID of the interface + * + * @return 0 on success + * @return -1 on error + */ +int iib_register_if(kernel_pid_t pid); + +/** + * @brief Process a received HELLO message in the IIB + * + * @note + * Must not be called from outside the NHDP reader's message processing. + * + * @param[in] if_pid PID of the interface the message was received on + * @param[in] nb_elt Pointer to the Neighbor Tuple for the message originator + * @param[in] send_list Pointer to the Sending Address List from the received HELLO + * @param[in] th_sym_list Pointer to the Addr List of the originator's symmetric 2-Hop neighbors + * @param[in] th_rem_list Pointer to the Addr List of the originator's lost 2-Hop neighbors + * @param[in] rem_list Pointer to the Removed Address List + * @param[in] validity_time Validity time in milliseconds for the originator's information + * @param[in] is_sym_nb Flag whether the link to the originator is symmetric + * @param[in] is_lost Flag whether the originator marked this link as lost + * + * @return 0 on success + */ +int iib_process_hello(kernel_pid_t if_pid, nib_entry_t *nb_elt, nhdp_addr_entry_t *send_list, + nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list, + nhdp_addr_entry_t *rem_list, uint64_t validity_time, uint8_t is_sym_nb, + uint8_t is_lost); + +/** + * @brief Add addresses to the currently constructed HELLO message + * + * @note + * Must not be called from outside the NHDP writer's message creation process. + * + * @param[in] if_pid PID of the interface the message is constructed for + * @param[in] wr The NHDP writer used for message construction + */ +void iib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr); + +/** + * @brief Update L_STATUS of all existing Link Tuples + * + * @note + * If a status change appears the steps described in section 13 of RFC 6130 are executed. + * + * @param[in] now Pointer to current time timex representation + */ +void iib_update_lt_status(timex_t *now); + +/** + * @brief Exchange the corresponding Neighbor Tuple of existing Link Tuples + * + * This function exchanges the corresponding Neighbor Tuple of every Link Tuple that + * was assigned to old_entry. Primarily used on Neighbor Tuple deletion of old_entry. + * + * @param[in] old_entry Pointer to the old corresponding Neighbor Tuple + * @param[in] new_entry Pointer to the new corresponding Neighbor Tuple + */ +void iib_propagate_nb_entry_change(nib_entry_t *old_entry, nib_entry_t *new_entry); + +#ifdef __cplusplus +} +#endif + +#endif /* IIB_TABLE_H_ */ +/** @} */ diff --git a/sys/net/routing/nhdp/lib_table.c b/sys/net/routing/nhdp/lib_table.c new file mode 100644 index 0000000000..c35e1c684c --- /dev/null +++ b/sys/net/routing/nhdp/lib_table.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Local Information Base implementation for NHDP + * + * @author Fabian Nack + * + * @} + */ + +#include "mutex.h" +#include "utlist.h" +#include "kernel_types.h" + +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_writer.h" + +#include "lib_table.h" +#include "nhdp_address.h" +#include "nhdp_writer.h" + +/* Internal variables */ +static mutex_t mtx_lib_access = MUTEX_INIT; +static lib_entry_t *lib_entry_head = NULL; + +/* Internal function prototypes */ +static int create_if_entry(kernel_pid_t if_pid, nhdp_addr_t *addr); +static int add_address_to_if(lib_entry_t *if_entry, nhdp_addr_t *addr); + + +/*---------------------------------------------------------------------------* + * Local Information Base API * + *---------------------------------------------------------------------------*/ + +int lib_add_if_addr(kernel_pid_t if_pid, nhdp_addr_t *addr) +{ + lib_entry_t *lib_elt; + nhdp_addr_entry_t *addr_elt; + int result = -1; + + mutex_lock(&mtx_lib_access); + + /* Check whether the given interface is already registered */ + LL_FOREACH(lib_entry_head, lib_elt) { + if (lib_elt->if_pid == if_pid) { + LL_FOREACH(lib_entry_head->if_addr_list_head, addr_elt) { + if (addr_elt->address == addr) { + /* Address already known for the interface */ + result = 0; + break; + } + } + + if (result) { + /* Existing interface entry, but new address */ + result = add_address_to_if(lib_elt, addr); + break; + } + } + } + + if (result) { + /* New interface, create a lib entry */ + result = create_if_entry(if_pid, addr); + } + + mutex_unlock(&mtx_lib_access); + + return result; +} + +void lib_rem_if(kernel_pid_t if_pid) +{ + lib_entry_t *lib_elt, *lib_tmp; + + mutex_lock(&mtx_lib_access); + + LL_FOREACH_SAFE(lib_entry_head, lib_elt, lib_tmp) { + if (lib_elt->if_pid == if_pid) { + nhdp_free_addr_list(lib_elt->if_addr_list_head); + LL_DELETE(lib_entry_head, lib_elt); + free(lib_elt); + break; + } + } + + mutex_unlock(&mtx_lib_access); +} + +void lib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr) +{ + lib_entry_t *lib_elt; + nhdp_addr_entry_t *add_tmp; + + mutex_lock(&mtx_lib_access); + + /* First fill the list for LOCAL_IF = THIS_IF */ + LL_FOREACH(lib_entry_head, lib_elt) { + if (lib_elt->if_pid == if_pid) { + LL_FOREACH(lib_elt->if_addr_list_head, add_tmp) { + nhdp_writer_add_addr(wr, add_tmp->address, + RFC5444_ADDRTLV_LOCAL_IF, RFC5444_LOCALIF_THIS_IF); + add_tmp->address->in_tmp_table = NHDP_ADDR_TMP_ANY; + } + break; + } + } + + /* Second fill the list for LOCAL_IF = OTHER_IF */ + LL_FOREACH(lib_entry_head, lib_elt) { + if (lib_elt->if_pid != if_pid) { + LL_FOREACH(lib_elt->if_addr_list_head, add_tmp) { + /* Check if this address is not already included in a list */ + if (!NHDP_ADDR_TMP_IN_ANY(add_tmp->address)) { + /* Address can be added */ + nhdp_writer_add_addr(wr, add_tmp->address, + RFC5444_ADDRTLV_LOCAL_IF, RFC5444_LOCALIF_OTHER_IF); + add_tmp->address->in_tmp_table = NHDP_ADDR_TMP_ANY; + } + } + } + } + + mutex_unlock(&mtx_lib_access); +} + +uint8_t lib_is_reg_addr(kernel_pid_t if_pid, nhdp_addr_t *addr) +{ + lib_entry_t *lib_elt; + nhdp_addr_entry_t *addr_elt; + + LL_FOREACH(lib_entry_head, lib_elt) { + LL_FOREACH(lib_elt->if_addr_list_head, addr_elt) { + if (addr_elt->address == addr) { + if (lib_elt->if_pid == if_pid) { + /* Given address is assigned to the given IF */ + return 1; + } + + /* Given address is assigned to any other IF */ + return 2; + } + } + } + return 0; +} + + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +/** + * Create an entry for a newly registered interface + */ +static int create_if_entry(kernel_pid_t if_pid, nhdp_addr_t *addr) +{ + lib_entry_t *new_entry; + + new_entry = (lib_entry_t *) malloc(sizeof(lib_entry_t)); + + if (!new_entry) { + /* Insufficient memory */ + return -1; + } + + new_entry->if_addr_list_head = NULL; + new_entry->if_pid = if_pid; + + if (add_address_to_if(new_entry, addr)) { + /* Insufficient memory */ + free(new_entry); + return -1; + } + + LL_PREPEND(lib_entry_head, new_entry); + + return 0; +} + +/** + * Add another address to an interface entry + */ +static int add_address_to_if(lib_entry_t *if_entry, nhdp_addr_t *addr) +{ + nhdp_addr_entry_t *new_entry = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t)); + + if (!new_entry) { + /* Insufficient memory */ + return -1; + } + + /* Increment usage counter of address in central NHDP address storage */ + addr->usg_count++; + new_entry->address = addr; + LL_PREPEND(if_entry->if_addr_list_head, new_entry); + + return 0; +} diff --git a/sys/net/routing/nhdp/lib_table.h b/sys/net/routing/nhdp/lib_table.h new file mode 100644 index 0000000000..2425e75aa9 --- /dev/null +++ b/sys/net/routing/nhdp/lib_table.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Local Information Base interface for NHDP + * + * @author Fabian Nack + */ + +#ifndef LIB_TABLE_H_ +#define LIB_TABLE_H_ + +#include "kernel_types.h" + +#include "rfc5444/rfc5444_writer.h" + +#include "nhdp_address.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Local Interface Set entry (local interface tuple) + */ +typedef struct lib_entry_t { + kernel_pid_t if_pid; /**< PID of the interface's handling thread */ + nhdp_addr_entry_t *if_addr_list_head; /**< Pointer to head of this interface's addr list */ + struct lib_entry_t *next; /**< Pointer to next list entry */ +} lib_entry_t; + +/** + * @brief Add an interface to the Local Information Base + * + * This function can also be used to add an additional address to an existing LIB tuple. + * + * @param[in] if_pid PID of the interface + * @param[in] addr The (additional) NHDP address to register for the interface + * + * @return 0 on success + * @return -1 on error + */ +int lib_add_if_addr(kernel_pid_t if_pid, nhdp_addr_t *addr); + +/** + * @brief Remove a given interface's Local Information Base entry + * + * @param[in] if_pid PID of the interface that should be removed + */ +void lib_rem_if(kernel_pid_t if_pid); + +/** + * @brief Add addresses to the currently constructed HELLO message + * + * @note + * Must not be called from outside the NHDP writer's message creation process. + * + * @param[in] if_pid PID of the interface the message is constructed for + * @param[in] wr The NHDP writer used for message construction + */ +void lib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr); + +/** + * @brief Check whether a given NHDP address is used as a local address + * + * @param[in] if_pid PID of the interface to check for + * @param[in] addr Pointer to the NHDP address that has to be checked + * + * @return 1 if the given address is assigned to the given interface + * @return 2 if the given address is assigned to any other local interface + * @return 0 otherwise + */ +uint8_t lib_is_reg_addr(kernel_pid_t if_pid, nhdp_addr_t *addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LIB_TABLE_H_ */ +/** @} */ diff --git a/sys/net/routing/nhdp/nhdp.c b/sys/net/routing/nhdp/nhdp.c new file mode 100644 index 0000000000..e582dc340a --- /dev/null +++ b/sys/net/routing/nhdp/nhdp.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Implementation of NHDP's core functionality + * + * @author Fabian Nack + * + * @} + */ + +#include "msg.h" +#include "netapi.h" +#include "thread.h" +#include "utlist.h" +#include "mutex.h" + +#include "rfc5444/rfc5444_writer.h" + +#include "lib_table.h" +#include "iib_table.h" +#include "nib_table.h" +#include "nhdp.h" +#include "nhdp_address.h" +#include "nhdp_writer.h" +#include "nhdp_reader.h" + +char nhdp_stack[NHDP_STACK_SIZE]; +char nhdp_rcv_stack[NHDP_STACK_SIZE]; + +/* Internal variables */ +static kernel_pid_t nhdp_pid = KERNEL_PID_UNDEF; +static kernel_pid_t nhdp_rcv_pid = KERNEL_PID_UNDEF; +static kernel_pid_t helper_pid = KERNEL_PID_UNDEF; +static nhdp_if_entry_t *nhdp_if_entry_head = NULL; +static mutex_t send_rcv_mutex = MUTEX_INIT; +static sockaddr6_t sa_bcast; +static int sock_rcv; + +/* Internal function prototypes */ +static void *_nhdp_runner(void *arg __attribute__((unused))); +static void *_nhdp_receiver(void *arg __attribute__((unused))); +static void write_packet(struct rfc5444_writer *wr __attribute__((unused)), + struct rfc5444_writer_target *iface __attribute__((unused)), + void *buffer, size_t length); + +/*---------------------------------------------------------------------------* + * NHDP Core API * + *---------------------------------------------------------------------------*/ + +void nhdp_init(void) +{ + if (nhdp_pid != KERNEL_PID_UNDEF) { + /* do not initialize twice */ + return; + } + + /* Initialize reader and writer */ + nhdp_writer_init(); + nhdp_reader_init(); +} + +kernel_pid_t nhdp_start(void) +{ + if (nhdp_pid == KERNEL_PID_UNDEF) { + /* Init destination address for NHDP's packets */ + sa_bcast.sin6_family = AF_INET6; + sa_bcast.sin6_port = HTONS(MANET_PORT); + ipv6_addr_set_all_nodes_addr(&sa_bcast.sin6_addr); + + /* Configure sending/receiving UDP socket */ + sock_rcv = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + /* Start the NHDP thread */ + nhdp_pid = thread_create(nhdp_stack, sizeof(nhdp_stack), PRIORITY_MAIN - 1, + CREATE_STACKTEST, _nhdp_runner, NULL, "NHDP"); + } + + return nhdp_pid; +} + +int nhdp_register_if_default(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, + uint8_t addr_type, uint16_t max_pl_size) +{ + return nhdp_register_if(if_pid, addr, addr_size, addr_type, max_pl_size, + NHDP_DEFAULT_HELLO_INT_MS, NHDP_DEFAULT_HOLD_TIME_MS); +} + +int nhdp_register_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type, + uint16_t max_pl_size, uint16_t hello_int_ms, uint16_t val_time_ms) +{ + nhdp_if_entry_t *if_entry; + nhdp_addr_t *nhdp_addr; + msg_t signal_msg; + + if (nhdp_rcv_pid != KERNEL_PID_UNDEF) { + return -2; + } + + if_entry = (nhdp_if_entry_t *) malloc(sizeof(nhdp_if_entry_t)); + + if (!if_entry) { + /* Insufficient memory */ + return -1; + } + + /* Create an interface writer targer for the nhdp_writer */ + if_entry->wr_target = (struct rfc5444_writer_target *) + calloc(1, sizeof(struct rfc5444_writer_target)); + + if (!if_entry->wr_target) { + /* Insufficient memory */ + free(if_entry); + return -1; + } + + uint16_t payload_size = max_pl_size > NHDP_MAX_RFC5444_PACKET_SZ + ? NHDP_MAX_RFC5444_PACKET_SZ : max_pl_size; + if_entry->wr_target->packet_buffer = (uint8_t *) calloc(payload_size, sizeof(uint8_t)); + + if (!if_entry->wr_target->packet_buffer) { + /* Insufficient memory */ + free(if_entry->wr_target); + free(if_entry); + return -1; + } + + if_entry->wr_target->packet_size = payload_size; + if_entry->wr_target->sendPacket = write_packet; + + /* Get NHDP address entry for the given address */ + nhdp_addr = nhdp_addr_db_get_address(addr, addr_size, addr_type); + + if (!nhdp_addr) { + /* Insufficient memory */ + free(if_entry->wr_target->packet_buffer); + free(if_entry->wr_target); + free(if_entry); + return -1; + } + + /* Set Interface's PID */ + if_entry->if_pid = if_pid; + /* Set HELLO_INTERVAL and H_HOLD_TIME (validity time) */ + if_entry->hello_interval.seconds = 0; + if_entry->hello_interval.microseconds = MS_IN_USEC * hello_int_ms; + if_entry->validity_time.seconds = 0; + if_entry->validity_time.microseconds = MS_IN_USEC * val_time_ms; + timex_normalize(&if_entry->hello_interval); + timex_normalize(&if_entry->validity_time); + + /* Add the interface to the LIB */ + if (lib_add_if_addr(if_entry->if_pid, nhdp_addr) != 0) { + free(if_entry->wr_target->packet_buffer); + free(if_entry->wr_target); + free(if_entry); + nhdp_decrement_addr_usage(nhdp_addr); + return -1; + } + + /* Create new IIB for the interface */ + if (iib_register_if(if_pid) != 0) { + /* TODO: Cleanup lib entry */ + free(if_entry->wr_target->packet_buffer); + free(if_entry->wr_target); + free(if_entry); + nhdp_decrement_addr_usage(nhdp_addr); + return -1; + } + + /* Everything went well */ + nhdp_decrement_addr_usage(nhdp_addr); + nhdp_writer_register_if(if_entry->wr_target); + LL_PREPEND(nhdp_if_entry_head, if_entry); + helper_pid = if_pid; + + /* Start the receiving thread */ + nhdp_rcv_pid = thread_create(nhdp_rcv_stack, sizeof(nhdp_rcv_stack), PRIORITY_MAIN - 1, + CREATE_STACKTEST, _nhdp_receiver, NULL, "nhdp_rcv_thread"); + + /* Start sending periodic HELLO */ + signal_msg.type = MSG_TIMER; + signal_msg.content.ptr = (char *) if_entry; + /* TODO: msg_send or msg_try_send? */ + msg_try_send(&signal_msg, nhdp_pid); + + return 0; +} + +int nhdp_register_non_manet_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, + uint8_t addr_type) +{ + return nhdp_add_address(if_pid, addr, addr_size, addr_type); +} + +int nhdp_add_address(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type) +{ + int result; + + /* Get NHDP address entry for the given address */ + nhdp_addr_t *nhdp_addr = nhdp_addr_db_get_address(addr, addr_size, addr_type); + + if (!nhdp_addr) { + /* Insufficient memory */ + return -1; + } + + result = lib_add_if_addr(if_pid, nhdp_addr); + nhdp_decrement_addr_usage(nhdp_addr); + + return result; +} + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +/** + * Function executed by NHDP thread receiving messages in an endless loop + */ +static void *_nhdp_runner(void *arg) +{ + nhdp_if_entry_t *if_entry; + msg_t msg_rcvd, msg_queue[NHDP_MSG_QUEUE_SIZE]; + + (void)arg; + msg_init_queue(msg_queue, NHDP_MSG_QUEUE_SIZE); + + while (1) { + msg_receive(&msg_rcvd); + + switch (msg_rcvd.type) { + case MSG_TIMER: + mutex_lock(&send_rcv_mutex); + if_entry = (nhdp_if_entry_t *) msg_rcvd.content.ptr; + + nhdp_writer_send_hello(if_entry); + + /* TODO: Add jitter */ + + /* Schedule next sending */ + vtimer_set_msg(&if_entry->if_timer, if_entry->hello_interval, + thread_getpid(), MSG_TIMER, (void *) if_entry); + mutex_unlock(&send_rcv_mutex); + break; + + default: + break; + } + } + + return 0; +} + +/** + * Receive HELLOs over the configured socket and handle them + */ +static void *_nhdp_receiver(void *arg __attribute__((unused))) +{ + uint32_t fromlen; + char nhdp_rcv_buf[NHDP_MAX_RFC5444_PACKET_SZ]; + msg_t msg_q[NHDP_MSG_QUEUE_SIZE]; + + msg_init_queue(msg_q, NHDP_MSG_QUEUE_SIZE); + + /* Configure socket address for the manet port 269 */ + sockaddr6_t sa_rcv = {.sin6_family = AF_INET6, + .sin6_port = HTONS(MANET_PORT) + }; + + /* Bind UDP socket to socket address */ + if (socket_base_bind(sock_rcv, &sa_rcv, sizeof(sa_rcv)) == -1) { + /* Failed binding the socket */ + socket_base_close(sock_rcv); + return 0; + } + + while (1) { + int32_t rcv_size = socket_base_recvfrom(sock_rcv, (void *)nhdp_rcv_buf, + NHDP_MAX_RFC5444_PACKET_SZ, 0, &sa_rcv, &fromlen); + + if (rcv_size > 0) { + /* Packet received, let the reader handle it */ + mutex_lock(&send_rcv_mutex); + nhdp_reader_handle_packet(helper_pid, (void *)nhdp_rcv_buf, rcv_size); + mutex_unlock(&send_rcv_mutex); + } + } + + socket_base_close(sock_rcv); + return 0; +} + +/** + * Send packet for the registered interface + * Called by oonf_api to send packet over the configured socket + */ +static void write_packet(struct rfc5444_writer *wr __attribute__((unused)), + struct rfc5444_writer_target *iface __attribute__((unused)), + void *buffer, size_t length) +{ + socket_base_sendto(sock_rcv, buffer, length, 0, &sa_bcast, sizeof(sa_bcast)); +} diff --git a/sys/net/routing/nhdp/nhdp.h b/sys/net/routing/nhdp/nhdp.h new file mode 100644 index 0000000000..2465205319 --- /dev/null +++ b/sys/net/routing/nhdp/nhdp.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup nhdp NHDP + * @ingroup net + * @brief The MANET Neighborhood Discovery Protocol (RFC 6130) + * @{ + * + * @file + * @brief Interface for core functionality of NHDP + * + * @author Fabian Nack + */ + +#ifndef NHDP_H_ +#define NHDP_H_ + +#include "timex.h" +#include "kernel_types.h" +#include "socket_base/socket.h" + +#include "rfc5444/rfc5444_writer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Definition for RIOTs debugging option */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @name NHDP protocol macros + * + * @{ + */ +#ifndef MANET_PORT +/** @brief Well-known MANET port from RFC 5498 */ +#define MANET_PORT (269) +#endif + +/** @brief Stack size for NHDP thread */ +#if ENABLE_DEBUG +#define NHDP_STACK_SIZE (KERNEL_CONF_STACKSIZE_DEFAULT + KERNEL_CONF_STACKSIZE_PRINTF) +#else +#define NHDP_STACK_SIZE (KERNEL_CONF_STACKSIZE_DEFAULT) +#endif + +/** @brief Buffer size in bytes for NHDP writer's msg buffer */ +#define NHDP_WR_MSG_BUF_SIZE (256) +/** @brief Buffer size in bytes for NHDP writer's tlv buffer */ +#define NHDP_WR_TLV_BUF_SIZE (512) + +/* TODO: Determine a good value */ +/** @brief Queue size for msg queue of NHDP */ +#define NHDP_MSG_QUEUE_SIZE (16) + +/** @brief Maximum size of handled RFC5444 packets */ +#define NHDP_MAX_RFC5444_PACKET_SZ (128) + +/** @brief Default hello interval in milliseconds */ +#define NHDP_DEFAULT_HELLO_INT_MS (2000) +/** @brief Default hold time in milliseconds */ +#define NHDP_DEFAULT_HOLD_TIME_MS (3 * NHDP_DEFAULT_HELLO_INT_MS) + +/** + * @brief Initial pending flag value for new link tuples + * + * Do not change (link quality currently not considered) + */ +#define NHDP_INITIAL_PENDING (0) + +/** @brief Maximum jitter for nhdp messages in milliseconds */ +#define NHDP_HP_MAXJITTER_MS (200) + +#define NHDP_L_HOLD_TIME_MS (NHDP_DEFAULT_HOLD_TIME_MS) +#define NHDP_N_HOLD_TIME_MS (NHDP_DEFAULT_HOLD_TIME_MS) +#define NHDP_I_HOLD_TIME_MS (NHDP_DEFAULT_HOLD_TIME_MS) +/** @} */ + +/** + * @brief MANET interface representation + */ +typedef struct nhdp_if_entry_t { + kernel_pid_t if_pid; /**< PID of the interface's handling thread */ + vtimer_t if_timer; /**< Vtimer used for the periodic signaling */ + timex_t hello_interval; /**< Interval time for periodic HELLOs */ + timex_t validity_time; /**< Validity time for propagated information */ + struct rfc5444_writer_target *wr_target; /**< Interface specific writer target */ + struct nhdp_if_entry_t *next; /**< Pointer to next list entry */ +} nhdp_if_entry_t; + +/** + * @brief Additional address types for link layer operation + */ +enum nhdp_address_type_t { + AF_CC110X = AF_MAX + 3, +}; + +/** + * @brief Initialize NHDP for operation + * + * Sets up NHDP's reader and writer. Call first before starting NHDP's thread + * and registering interfaces. + */ +void nhdp_init(void); + +/** + * @brief Start NHDP's operational thread + * + * @return PID of NHDP's operational thread + * @return KERNEL_PID_UNDEF on error + */ +kernel_pid_t nhdp_start(void); + +/** + * @brief Register an interface for NHDP operation with default values + * + * Registers the interface completely for NHDP operation. Registration includes a new + * Local Information Base entry, a new Interface Information Base and starting + * the periodical HELLO messaging. + * + * @note + * Default values are a hello interval of 2 seconds and a validity time + * of 6 seconds for propagated information. + * + * @param[in] if_pid PID of the interface + * @param[in] addr A local address of this interface represented in bytes + * @param[in] addr_size Length in bytes of the local address + * @param[in] addr_type AF type of the local address + * @param[in] max_pl_size Maximum payload size for packets send over this interface + * + * @return 0 on success + * @return -1 on error + * @return -2 on maximum number of interfaces registered + */ +int nhdp_register_if_default(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, + uint8_t addr_type, uint16_t max_pl_size); + +/** + * @brief Register an interface for NHDP operation + * + * Registers the interface completely for NHDP operation. Registration includes a new + * Local Information Base entry, a new Interface Information Base and starting + * the periodical HELLO messaging. + * + * @param[in] if_pid PID of the interface + * @param[in] addr A local address of this interface represented in bytes + * @param[in] addr_size Length of the local address (number of bytes) + * @param[in] addr_type AF type of the given address + * @param[in] max_pl_size Maximum payload size for packets send over this interface + * @param[in] hello_int_ms Hello interval in ms for periodic message generation + * @param[in] val_time_ms Validity time in ms for propagated information + * + * @return 0 on success + * @return -1 on error + * @return -2 on maximum number of interfaces registered + */ +int nhdp_register_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type, + uint16_t max_pl_size, uint16_t hello_int_ms, uint16_t val_time_ms); + +/** + * @brief Register a non MANET interface in NHDP + * + * The registered interface does not take part in NHDP operation. Its addresses + * will be represented only in other interfaces' messages. No periodical messages + * are created for this interface and no message processing is done. + * + * @param[in] if_pid PID of the interface + * @param[in] addr A local address of this interface represented in bytes + * @param[in] addr_size Length of the local address (number of bytes) + * @param[in] addr_type AF type of the given address + * + * @return 0 on success + * @return -1 on error + */ +int nhdp_register_non_manet_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, + uint8_t addr_type); + +/** + * @brief Register an additional address for an interface in NHDP + * + * The address is added to the Local Information Base entry of this interface and + * will afterwards be propagated as a local address in newly created HELLO messages. + * + * @param[in] if_pid PID of the interface + * @param[in] addr Additional local address of this interface represented in bytes + * @param[in] addr_size Length of the local address (number of bytes) + * @param[in] addr_type AF type of the given address + * + * @return 0 on success + * @return -1 on error + */ +int nhdp_add_address(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type); + +#ifdef __cplusplus +} +#endif + +#endif /* NHDP_H_ */ +/** @} */ diff --git a/sys/net/routing/nhdp/nhdp_address.c b/sys/net/routing/nhdp/nhdp_address.c new file mode 100644 index 0000000000..50ab72a697 --- /dev/null +++ b/sys/net/routing/nhdp/nhdp_address.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Centralized address storage implementation for NHDP + * + * @author Fabian Nack + * + * @} + */ + +#include "mutex.h" +#include "utlist.h" + +#include "nhdp.h" +#include "nhdp_address.h" + +/* Internal variables */ +static mutex_t mtx_addr_access = MUTEX_INIT; +static nhdp_addr_t *nhdp_addr_db_head = NULL; + + +/*---------------------------------------------------------------------------* + * Centralized Address Storage API * + *---------------------------------------------------------------------------*/ + +nhdp_addr_t *nhdp_addr_db_get_address(uint8_t *addr, size_t addr_size, uint8_t addr_type) +{ + nhdp_addr_t *addr_elt; + + mutex_lock(&mtx_addr_access); + + LL_FOREACH(nhdp_addr_db_head, addr_elt) { + if ((addr_elt->addr_size == addr_size) && (addr_elt->addr_type == addr_type)) { + if (memcmp(addr_elt->addr, addr, addr_size) == 0) { + /* Found a matching entry */ + break; + } + } + } + + if (!addr_elt) { + /* No matching entry, create a new one */ + addr_elt = (nhdp_addr_t *) malloc(sizeof(nhdp_addr_t)); + + if (!addr_elt) { + /* Insufficient memory */ + return NULL; + } + + /* Allocate space for the address */ + addr_elt->addr = (uint8_t *) malloc(addr_size * sizeof(uint8_t)); + + if (!addr_elt->addr) { + /* Insufficient memory */ + free(addr_elt); + return NULL; + } + + memcpy(addr_elt->addr, addr, addr_size); + addr_elt->addr_size = addr_size; + addr_elt->addr_type = addr_type; + addr_elt->usg_count = 0; + addr_elt->in_tmp_table = NHDP_ADDR_TMP_NONE; + LL_PREPEND(nhdp_addr_db_head, addr_elt); + } + + addr_elt->usg_count++; + + mutex_unlock(&mtx_addr_access); + + return addr_elt; +} + +void nhdp_decrement_addr_usage(nhdp_addr_t *addr) +{ + mutex_lock(&mtx_addr_access); + + /* Decrement usage count and delete address if no longer used */ + if (addr) { + addr->usg_count--; + + if (addr->usg_count <= 0) { + /* Free address space if address is no longer used */ + LL_DELETE(nhdp_addr_db_head, addr); + free(addr->addr); + free(addr); + } + } + + mutex_unlock(&mtx_addr_access); +} + +void nhdp_free_addr_list(nhdp_addr_entry_t *list_head) +{ + nhdp_addr_entry_t *list_elt, *list_tmp; + + LL_FOREACH_SAFE(list_head, list_elt, list_tmp) { + nhdp_free_addr_entry(list_elt); + } +} + +void nhdp_free_addr_entry(nhdp_addr_entry_t *addr_entry) +{ + nhdp_decrement_addr_usage(addr_entry->address); + free(addr_entry); +} + +nhdp_addr_entry_t *nhdp_generate_new_addr_list(nhdp_addr_entry_t *orig_list) +{ + nhdp_addr_entry_t *new_list_head, *addr_elt; + + new_list_head = NULL; + LL_FOREACH(orig_list, addr_elt) { + nhdp_addr_entry_t *new_entry = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t)); + + if (!new_entry) { + /* Insufficient memory, free all previously allocated memory */ + nhdp_free_addr_list(new_list_head); + return NULL; + } + + new_entry->address = addr_elt->address; + /* Increment usage counter of address in central NHDP address storage */ + addr_elt->address->usg_count++; + LL_PREPEND(new_list_head, new_entry); + } + + return new_list_head; +} + +void nhdp_reset_addresses_tmp_usg(void) +{ + nhdp_addr_t *addr_elt; + + LL_FOREACH(nhdp_addr_db_head, addr_elt) { + addr_elt->in_tmp_table = NHDP_ADDR_TMP_NONE; + } +} diff --git a/sys/net/routing/nhdp/nhdp_address.h b/sys/net/routing/nhdp/nhdp_address.h new file mode 100644 index 0000000000..2d9b6195df --- /dev/null +++ b/sys/net/routing/nhdp/nhdp_address.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Centralized address storage interface for NHDP + * + * @author Fabian Nack + */ + +#ifndef NHDP_ADDRESS_H_ +#define NHDP_ADDRESS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief NHDP address representation + */ +typedef struct nhdp_addr_t { + uint8_t *addr; /**< Pointer to the address data */ + size_t addr_size; /**< Size in bytes of the address */ + uint8_t addr_type; /**< AF type for the address */ + uint8_t usg_count; /**< Usage count in information bases */ + uint8_t in_tmp_table; /**< Signals usage in a writers temp table */ + struct nhdp_addr_t *next; /**< Pointer to next address (used in central storage) */ +} nhdp_addr_t; + +/** + * @brief Container for NHDP address storage in a list + */ +typedef struct nhdp_addr_entry_t { + struct nhdp_addr_t *address; /**< Pointer to NHDP address storage entry */ + struct nhdp_addr_entry_t *next; /**< Pointer to the next address list element */ +} nhdp_addr_entry_t; + +/** + * @name NHDP address temp usage helper macros + * + * @{ + */ +#define NHDP_ADDR_TMP_NONE (0x00) +#define NHDP_ADDR_TMP_ANY (0x01) +#define NHDP_ADDR_TMP_SYM (0x03) + +#define NHDP_ADDR_TMP_IN_SYM(addr) ((addr->in_tmp_table & 0x02) >> 1) +#define NHDP_ADDR_TMP_IN_ANY(addr) ((addr->in_tmp_table & 0x01)) +/** @} */ + +/** + * @brief Get or create a NHDP address for the given address + * + * @param[in] addr Pointer to the given address + * @param[in] addr_size Length in bytes of the given address + * @param[in] addr_type AF type of the given address + * + * @return Pointer to the NHDP address representation of the given address + * @return NULL on error + */ +nhdp_addr_t *nhdp_addr_db_get_address(uint8_t *addr, size_t addr_size, uint8_t addr_type); + +/** + * @brief Decrement the usage counter of a given NHDP address + * + * The NHDP address is deleted if the usage counter reaches zero. + * + * @param[in] addr Pointer to the NHDP address + */ +void nhdp_decrement_addr_usage(nhdp_addr_t *addr); + +/** + * @brief Free the given address list + * + * This function frees every address list entry of the given address list. + * + * @param[in] list_head Pointer to the head of the address list to free + */ +void nhdp_free_addr_list(nhdp_addr_entry_t *list_head); + +/** + * @brief Free the given address list entry + * + * Additionally to freeing the address entry, this function takes care that + * the usage counter of the list entry's address is decremented. + * + * @param[in] addr_entry Pointer to the address list entry to free + */ +void nhdp_free_addr_entry(nhdp_addr_entry_t *addr_entry); + +/** + * @brief Construct an address list containing the addresses of the given list + * + * @param[in] orig_list Pointer to the head of the address list to 'clone' + * + * @return Pointer to the head of the newly created address list + * @return NULL on error + */ +nhdp_addr_entry_t *nhdp_generate_new_addr_list(nhdp_addr_entry_t *orig_list); + +/** + * @brief Reset in_tmp_table flag of all NHDP addresses + * + * @note + * Must not be called from outside the NHDP writer's message creation process. + */ +void nhdp_reset_addresses_tmp_usg(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NHDP_ADDRESS_H_ */ +/** @} */ diff --git a/sys/net/routing/nhdp/nhdp_reader.c b/sys/net/routing/nhdp/nhdp_reader.c new file mode 100644 index 0000000000..d5d152cd7a --- /dev/null +++ b/sys/net/routing/nhdp/nhdp_reader.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Reader implementation for message processing in NHDP + * + * @author Fabian Nack + * + * @} + */ + +#include "mutex.h" +#include "utlist.h" + +#include "rfc5444/rfc5444.h" +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_reader.h" + +#include "lib_table.h" +#include "nib_table.h" +#include "iib_table.h" +#include "nhdp.h" +#include "nhdp_address.h" +#include "nhdp_reader.h" + +/* Internal variables */ +struct rfc5444_reader reader; +static mutex_t mtx_packet_handler = MUTEX_INIT; + +static nhdp_addr_entry_t *send_addr_list_head; +static nhdp_addr_entry_t *nb_addr_list_head; +static nhdp_addr_entry_t *th_sym_addr_list_head; +static nhdp_addr_entry_t *th_rem_addr_list_head; +static nhdp_addr_entry_t *rem_addr_list_head; + +static kernel_pid_t if_pid; +static uint64_t val_time; +static uint64_t int_time; +static uint8_t sym = 0; +static uint8_t lost = 0; + +/* Internal function prototypes */ +static enum rfc5444_result _nhdp_blocktlv_msg_cb(struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _nhdp_blocktlv_address_cb(struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _nhdp_msg_end_cb(struct rfc5444_reader_tlvblock_context *cont, + bool dropped); +static enum rfc5444_result check_msg_validity(struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result check_addr_validity(nhdp_addr_t *addr); +static nhdp_addr_t *get_nhdp_db_addr(uint8_t *addr, uint8_t prefix); +static void process_temp_tables(void); +static void cleanup_temp_addr_lists(void); + +/* Array containing the processable message TLVs for HELLO messages */ +static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_msg_tlvs[] = { + [RFC5444_MSGTLV_INTERVAL_TIME] = { .type = RFC5444_MSGTLV_INTERVAL_TIME, .mandatory = false }, + [RFC5444_MSGTLV_VALIDITY_TIME] = { .type = RFC5444_MSGTLV_VALIDITY_TIME, .mandatory = true }, +}; + +/* Array containing the processable address TLVs for HELLO message address blocks */ +static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_addr_tlvs[] = { + [RFC5444_ADDRTLV_LOCAL_IF] = { .type = RFC5444_ADDRTLV_LOCAL_IF }, + [RFC5444_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS }, + [RFC5444_ADDRTLV_OTHER_NEIGHB] = { .type = RFC5444_ADDRTLV_OTHER_NEIGHB }, +}; + +/* oonf_api message consumer used for HELLO message consumption */ +static struct rfc5444_reader_tlvblock_consumer _nhdp_msg_consumer = { + .msg_id = RFC5444_MSGTYPE_HELLO, + .block_callback = _nhdp_blocktlv_msg_cb, + .end_callback = _nhdp_msg_end_cb, +}; + +/* oonf_api message consumer user for HELLO message address block consumption */ +static struct rfc5444_reader_tlvblock_consumer _nhdp_address_consumer = { + .msg_id = RFC5444_MSGTYPE_HELLO, + .addrblock_consumer = true, + .block_callback = _nhdp_blocktlv_address_cb, +}; + + +/*---------------------------------------------------------------------------* + * NHDP Reader API * + *---------------------------------------------------------------------------*/ + +void nhdp_reader_init(void) +{ + /* Reset locally created address lists */ + send_addr_list_head = NULL; + nb_addr_list_head = NULL; + th_sym_addr_list_head = NULL; + th_rem_addr_list_head = NULL; + rem_addr_list_head = NULL; + + /* Initialize reader */ + rfc5444_reader_init(&reader); + + /* Register HELLO message consumer */ + rfc5444_reader_add_message_consumer(&reader, &_nhdp_msg_consumer, + _nhdp_msg_tlvs, ARRAYSIZE(_nhdp_msg_tlvs)); + rfc5444_reader_add_message_consumer(&reader, &_nhdp_address_consumer, + _nhdp_addr_tlvs, ARRAYSIZE(_nhdp_addr_tlvs)); +} + +int nhdp_reader_handle_packet(kernel_pid_t rcvg_if_pid, void *buffer, size_t length) +{ + int result; + + mutex_lock(&mtx_packet_handler); + + /* Store PID of interface this packet was received on */ + if_pid = rcvg_if_pid; + /* Parse packet with reader */ + result = rfc5444_reader_handle_packet(&reader, buffer, length); + + mutex_unlock(&mtx_packet_handler); + + return result; +} + +void nhdp_reader_cleanup(void) +{ + cleanup_temp_addr_lists(); + rfc5444_reader_cleanup(&reader); +} + + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +/** + * Handle one address and its corresponding TLVs + * Called by oonf_api for every included address to allow parsing + */ +static enum rfc5444_result +_nhdp_blocktlv_address_cb(struct rfc5444_reader_tlvblock_context *cont) +{ + uint8_t tmp_result; + /* Create address list entry to add it later to one of the temp address lists */ + nhdp_addr_entry_t *current_addr = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t)); + + if (!current_addr) { + /* Insufficient memory */ + return RFC5444_DROP_MESSAGE; + } + + /* Get NHDP address for the current netaddr */ + current_addr->address = get_nhdp_db_addr(&cont->addr._addr[0], cont->addr._prefix_len); + + if (!current_addr->address) { + /* Insufficient memory */ + free(current_addr); + return RFC5444_DROP_MESSAGE; + } + + /* Check validity of address tlvs */ + if (check_addr_validity(current_addr->address) != RFC5444_OKAY) { + nhdp_free_addr_entry(current_addr); + return RFC5444_DROP_MESSAGE; + } + + /* Handle address and add it to proper temporary list */ + if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv) { + switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv->single_value) { + case RFC5444_LOCALIF_THIS_IF: + LL_PREPEND(send_addr_list_head, current_addr); + /* Local IF marked addresses have to be added to two temp lists */ + nhdp_addr_entry_t *sec_container = + (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t)); + + if (!sec_container) { + return RFC5444_DROP_MESSAGE; + } + + /* Increment usage counter of address in central NHDP address storage */ + current_addr->address->usg_count++; + sec_container->address = current_addr->address; + LL_PREPEND(nb_addr_list_head, sec_container); + break; + + case RFC5444_LOCALIF_OTHER_IF: + LL_PREPEND(nb_addr_list_head, current_addr); + break; + + default: + /* Wrong value, drop message */ + nhdp_free_addr_entry(current_addr); + return RFC5444_DROP_MESSAGE; + } + } + else if ((tmp_result = lib_is_reg_addr(if_pid, current_addr->address))) { + /* The address is one of our local addresses (do not add it for processing) */ + if ((!sym) && (tmp_result == 1) && _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv) { + /* If address is a local address of the receiving interface, check */ + /* whether we can derive a status for this link (symmetry or lost) */ + switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->single_value) { + case RFC5444_LINKSTATUS_SYMMETRIC: + /* Fall - through */ + + case RFC5444_LINKSTATUS_HEARD: + sym = 1; + break; + + case RFC5444_LINKSTATUS_LOST: + lost = 1; + break; + + default: + /* Wrong value, drop message */ + nhdp_free_addr_entry(current_addr); + return RFC5444_DROP_MESSAGE; + } + } + + /* Address is one of our own addresses, ignore it */ + nhdp_free_addr_entry(current_addr); + } + else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv) { + switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->single_value) { + case RFC5444_LINKSTATUS_SYMMETRIC: + LL_PREPEND(th_sym_addr_list_head, current_addr); + break; + + case RFC5444_LINKSTATUS_HEARD: + /* Fall-through */ + + case RFC5444_LINKSTATUS_LOST: + if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv + && *_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->single_value + == RFC5444_OTHERNEIGHB_SYMMETRIC) { + /* Symmetric has higher priority */ + LL_PREPEND(th_sym_addr_list_head, current_addr); + } + else { + LL_PREPEND(th_rem_addr_list_head, current_addr); + } + + break; + + default: + /* Wrong value, drop message */ + nhdp_free_addr_entry(current_addr); + return RFC5444_DROP_MESSAGE; + } + } + else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv) { + switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->single_value) { + case RFC5444_OTHERNEIGHB_SYMMETRIC: + LL_PREPEND(th_sym_addr_list_head, current_addr); + break; + + case RFC5444_OTHERNEIGHB_LOST: + LL_PREPEND(th_rem_addr_list_head, current_addr); + break; + + default: + /* Wrong value, drop message */ + nhdp_free_addr_entry(current_addr); + return RFC5444_DROP_MESSAGE; + } + } + else { + /* Addresses without expected TLV are ignored */ + nhdp_free_addr_entry(current_addr); + return RFC5444_DROP_ADDRESS; + } + + return RFC5444_OKAY; +} + +/** + * Handle message TLVs of received HELLO + * Called by oonf_api to allow message TLV parsing + */ +static enum rfc5444_result +_nhdp_blocktlv_msg_cb(struct rfc5444_reader_tlvblock_context *cont) +{ + /* Check whether specified message TLVs are correctly included */ + if (check_msg_validity(cont) != RFC5444_OKAY) { + return RFC5444_DROP_MESSAGE; + } + + /* Validity time must be included as message tlv */ + val_time = rfc5444_timetlv_decode( + *_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv->single_value); + + /* Interval time is not mandatory as message tlv */ + if (_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv) { + int_time = rfc5444_timetlv_decode( + *_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv->single_value); + } + + return RFC5444_OKAY; +} + +/** + * Process received addresses and clean up temporary stuff + * Called by oonf_api after message was parsed + */ +static enum rfc5444_result +_nhdp_msg_end_cb(struct rfc5444_reader_tlvblock_context *cont __attribute__((unused)), + bool dropped) +{ + if (!dropped) { + /* Only process the received addresses if message was valid */ + process_temp_tables(); + } + + /* Clean all temporary stuff */ + val_time = 0ULL; + int_time = 0ULL; + sym = 0; + lost = 0; + cleanup_temp_addr_lists(); + + if (dropped) { + return RFC5444_DROP_MESSAGE; + } + + return RFC5444_OKAY; +} + +/** + * Check validity of HELLO message header and message TLVs + */ +static enum rfc5444_result check_msg_validity(struct rfc5444_reader_tlvblock_context *cont) +{ + if (cont->has_hoplimit && cont->hoplimit != 1) { + /* Hop Limit other than 1 */ + return RFC5444_DROP_MESSAGE; + } + + if (cont->has_hopcount && cont->hopcount != 0) { + /* Hop Count other than zero */ + return RFC5444_DROP_MESSAGE; + } + + if (!(_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv)) { + /* No validity time tlv */ + return RFC5444_DROP_MESSAGE; + } + else if (_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv->next_entry) { + /* Multiple validity time tlvs */ + return RFC5444_DROP_MESSAGE; + } + + if (_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv + && _nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv->next_entry) { + /* Multiple interval time tlvs */ + return RFC5444_DROP_MESSAGE; + } + + return RFC5444_OKAY; +} + +/** + * Check validity of address block TLVs + */ +static enum rfc5444_result check_addr_validity(nhdp_addr_t *addr) +{ + if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv) { + if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv + || _nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv) { + /* Conflicting tlv types for the address */ + return RFC5444_DROP_MESSAGE; + } + else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv->next_entry) { + /* Multiple tlvs of the same type are not allowed */ + return RFC5444_DROP_MESSAGE; + } + else if (lib_is_reg_addr(if_pid, addr)) { + /* Address of one of neighbor's IFs equals one of ours */ + return RFC5444_DROP_MESSAGE; + } + } + else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv + && _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->next_entry) { + /* Multiple tlvs of the same type are not allowed */ + return RFC5444_DROP_MESSAGE; + } + else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv + && _nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->next_entry) { + /* Multiple tlvs of the same type are not allowed */ + return RFC5444_DROP_MESSAGE; + } + + return RFC5444_OKAY; +} + +/** + * Get a new or existing NHDP address entry from the centralized address storage + * for the given address data + */ +static nhdp_addr_t *get_nhdp_db_addr(uint8_t *addr, uint8_t prefix) +{ + switch (prefix) { + case 8: + return nhdp_addr_db_get_address(addr, 1, AF_CC110X); + + case 32: + return nhdp_addr_db_get_address(addr, 4, AF_INET); + + default: + if (prefix < 32) { + return nhdp_addr_db_get_address(addr, 4, AF_INET); + } + else { + return nhdp_addr_db_get_address(addr, 16, AF_INET6); + } + } +} + +/** + * Process address lists from the HELLO msg in the information bases + */ +static void process_temp_tables(void) +{ + nib_entry_t *nib_elt; + timex_t now; + + vtimer_now(&now); + iib_update_lt_status(&now); + + nib_elt = nib_process_hello(nb_addr_list_head, &rem_addr_list_head); + + if (nib_elt) { + iib_process_hello(if_pid, nib_elt, send_addr_list_head, th_sym_addr_list_head, + th_rem_addr_list_head, rem_addr_list_head, val_time, sym, lost); + } +} + +/** + * Free all allocated space for the temporary address lists + */ +static void cleanup_temp_addr_lists(void) +{ + nhdp_free_addr_list(send_addr_list_head); + nhdp_free_addr_list(nb_addr_list_head); + nhdp_free_addr_list(th_sym_addr_list_head); + nhdp_free_addr_list(th_rem_addr_list_head); + nhdp_free_addr_list(rem_addr_list_head); + send_addr_list_head = NULL; + nb_addr_list_head = NULL; + th_sym_addr_list_head = NULL; + th_rem_addr_list_head = NULL; + rem_addr_list_head = NULL; +} diff --git a/sys/net/routing/nhdp/nhdp_reader.h b/sys/net/routing/nhdp/nhdp_reader.h new file mode 100644 index 0000000000..15f4af37c2 --- /dev/null +++ b/sys/net/routing/nhdp/nhdp_reader.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Reader interface for message processing in NHDP + * + * @author Fabian Nack + */ + +#ifndef NHDP_READER_H_ +#define NHDP_READER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set up the NHDP reader + * + * Initializes the reader and registers HELLO messages as a message + * type consumed by this reader. + */ +void nhdp_reader_init(void); + +/** + * @brief Clean up the NHDP reader + */ +void nhdp_reader_cleanup(void); + +/** + * @brief Handle a packet received from a registered interface + * + * @note + * HELLO message processing is triggered from this function. + * + * @param[in] rcvg_if_pid PID of the interface the packet was received on + * @param[in] buffer Pointer to start of packet data + * @param[in] length Length in bytes of the packet data + * + * @return rfc5444_result of the packet handling process + */ +int nhdp_reader_handle_packet(kernel_pid_t rcvg_if_pid, void *buffer, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif /* NHDP_READER_H_ */ +/** @} */ diff --git a/sys/net/routing/nhdp/nhdp_writer.c b/sys/net/routing/nhdp/nhdp_writer.c new file mode 100644 index 0000000000..f4c12a42b3 --- /dev/null +++ b/sys/net/routing/nhdp/nhdp_writer.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Writer implementation for message generation in NHDP + * + * @author Fabian Nack + * + * @} + */ + +#include + +#include "timex.h" +#include "mutex.h" + +#include "rfc5444/rfc5444.h" +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_writer.h" + +#include "nhdp.h" +#include "nhdp_address.h" +#include "nhdp_writer.h" +#include "lib_table.h" +#include "nib_table.h" +#include "iib_table.h" + +/* Internal variables */ +static mutex_t mtx_packet_write = MUTEX_INIT; +static struct rfc5444_writer nhdp_writer; +static nhdp_if_entry_t *nhdp_wr_curr_if_entry; +static uint8_t msg_buffer[NHDP_WR_MSG_BUF_SIZE]; +static uint8_t msg_addrtlvs[NHDP_WR_TLV_BUF_SIZE]; + +/* Internal function prototypes */ +static void _nhdp_add_hello_msg_header_cb(struct rfc5444_writer *wr, + struct rfc5444_writer_message *msg); +static void _nhdp_add_message_tlvs_cb(struct rfc5444_writer *wr); +static void _nhdp_add_addresses_cb(struct rfc5444_writer *wr); +static void netaddr_from_nhdp_address(struct netaddr *target, nhdp_addr_t *n_addr); + +/* Array containing the known Address TLVs */ +static struct rfc5444_writer_tlvtype _nhdp_addrtlvs[] = { + [RFC5444_ADDRTLV_LOCAL_IF] = { .type = RFC5444_ADDRTLV_LOCAL_IF }, + [RFC5444_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS }, + [RFC5444_ADDRTLV_OTHER_NEIGHB] = { .type = RFC5444_ADDRTLV_OTHER_NEIGHB }, +}; + +/* Writer content provider for HELLO messages */ +static struct rfc5444_writer_content_provider _nhdp_message_content_provider = { + .msg_type = RFC5444_MSGTYPE_HELLO, + .addMessageTLVs = _nhdp_add_message_tlvs_cb, + .addAddresses = _nhdp_add_addresses_cb, +}; + + +/*---------------------------------------------------------------------------* + * NHDP Writer API * + *---------------------------------------------------------------------------*/ + +void nhdp_writer_init(void) +{ + struct rfc5444_writer_message *_hello_msg; + + mutex_lock(&mtx_packet_write); + + /* Reset current interface */ + nhdp_wr_curr_if_entry = NULL; + + /* Configure NHDP writer */ + nhdp_writer.msg_buffer = msg_buffer; + nhdp_writer.msg_size = sizeof(msg_buffer); + nhdp_writer.addrtlv_buffer = msg_addrtlvs; + nhdp_writer.addrtlv_size = sizeof(msg_addrtlvs); + + /* Initialize writer */ + rfc5444_writer_init(&nhdp_writer); + + /* Register HELLO msg with 16 byte addresses and content provider */ + rfc5444_writer_register_msgcontentprovider(&nhdp_writer, + &_nhdp_message_content_provider, _nhdp_addrtlvs, ARRAYSIZE(_nhdp_addrtlvs)); + _hello_msg = rfc5444_writer_register_message(&nhdp_writer, RFC5444_MSGTYPE_HELLO, false, 16); + _hello_msg->addMessageHeader = _nhdp_add_hello_msg_header_cb; + + mutex_unlock(&mtx_packet_write); +} + +void nhdp_writer_cleanup(void) +{ + mutex_lock(&mtx_packet_write); + + nhdp_wr_curr_if_entry = NULL; + rfc5444_writer_cleanup(&nhdp_writer); + + mutex_unlock(&mtx_packet_write); +} + +void nhdp_writer_register_if(struct rfc5444_writer_target *new_if) +{ + mutex_lock(&mtx_packet_write); + + /* Register target interface in writer */ + rfc5444_writer_register_target(&nhdp_writer, new_if); + + mutex_unlock(&mtx_packet_write); +} + +void nhdp_writer_send_hello(nhdp_if_entry_t *if_entry) +{ + mutex_lock(&mtx_packet_write); + + /* Register interface as current sending interface */ + nhdp_wr_curr_if_entry = if_entry; + + /* Create HELLO message and send it using the given interface */ + rfc5444_writer_create_message(&nhdp_writer, RFC5444_MSGTYPE_HELLO, + rfc5444_writer_singletarget_selector, if_entry->wr_target); + rfc5444_writer_flush(&nhdp_writer, if_entry->wr_target, false); + + mutex_unlock(&mtx_packet_write); +} + +void nhdp_writer_add_addr(struct rfc5444_writer *wr, nhdp_addr_t *addr, + enum rfc5444_addrtlv_iana type, uint8_t value) +{ + struct rfc5444_writer_address *wr_addr; + struct netaddr n_addr; + + netaddr_from_nhdp_address(&n_addr, addr); + + switch (type) { + case RFC5444_ADDRTLV_LOCAL_IF: + /* Address is mandatory for every sub-msg (if message is splitted) */ + wr_addr = rfc5444_writer_add_address(wr, _nhdp_message_content_provider.creator, + &n_addr, true); + break; + + case RFC5444_ADDRTLV_LINK_STATUS: + /* Fall through */ + + case RFC5444_ADDRTLV_OTHER_NEIGHB: + /* Address only has to be included in one sub-msg (if message is splitted) */ + wr_addr = rfc5444_writer_add_address(wr, _nhdp_message_content_provider.creator, + &n_addr, false); + break; + + default: + /* Unknown type, extend switch if other types are allowed */ + return; + } + + rfc5444_writer_add_addrtlv(wr, wr_addr, &_nhdp_addrtlvs[type], + &value, sizeof(uint8_t), false); +} + + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +/** + * Set the header for the currently constructed HELLO message + * Called by oonf_api during message creation + */ +static void +_nhdp_add_hello_msg_header_cb(struct rfc5444_writer *wr, struct rfc5444_writer_message *msg) +{ + /* No originator, no hopcount, no hoplimit, no sequence number */ + rfc5444_writer_set_msg_header(wr, msg, false, false, false, false); +} + +/** + * Add validity time and interval time message TLVs to current message + * Called by oonf_api during message creation + */ +static void _nhdp_add_message_tlvs_cb(struct rfc5444_writer *wr) +{ + uint8_t validity_time, interval_time; + /* Convert validity time and interval time to milliseconds */ + uint64_t val_tmp = (uint64_t) nhdp_wr_curr_if_entry->validity_time.seconds * SEC_IN_MS + + (nhdp_wr_curr_if_entry->validity_time.microseconds / 1000ULL); + uint64_t int_tmp = (uint64_t) nhdp_wr_curr_if_entry->hello_interval.seconds * SEC_IN_MS + + (nhdp_wr_curr_if_entry->hello_interval.microseconds / 1000ULL); + + /* Add validity time (mandatory) and interval time to msg */ + validity_time = rfc5444_timetlv_encode(val_tmp); + interval_time = rfc5444_timetlv_encode(int_tmp); + rfc5444_writer_add_messagetlv(wr, RFC5444_MSGTLV_VALIDITY_TIME, 0, &validity_time, + sizeof(validity_time)); + rfc5444_writer_add_messagetlv(wr, RFC5444_MSGTLV_INTERVAL_TIME, 0, &interval_time, + sizeof(interval_time)); +} + +/** + * Add addresses and corresponding TLVs to current message + * Called by oonf_api during message creation + */ +static void _nhdp_add_addresses_cb(struct rfc5444_writer *wr) +{ + lib_fill_wr_addresses(nhdp_wr_curr_if_entry->if_pid, wr); + iib_fill_wr_addresses(nhdp_wr_curr_if_entry->if_pid, wr); + nib_fill_wr_addresses(wr); + nhdp_reset_addresses_tmp_usg(); +} + +/** + * Construct a netaddr from a given NHDP address + */ +static void netaddr_from_nhdp_address(struct netaddr *target, nhdp_addr_t *n_addr) +{ + memset(target->_addr, 0, NETADDR_MAX_LENGTH); + memcpy(target->_addr, n_addr->addr, n_addr->addr_size); + + switch (n_addr->addr_type) { + case AF_CC110X: + target->_prefix_len = 8u; + target->_type = AF_CC110X; + break; + + case AF_INET: + target->_prefix_len = 32u; + target->_type = AF_INET; + break; + + case AF_INET6: + /* Fall-through */ + + default: + target->_prefix_len = 128u; + target->_type = AF_INET6; + break; + } +} diff --git a/sys/net/routing/nhdp/nhdp_writer.h b/sys/net/routing/nhdp/nhdp_writer.h new file mode 100644 index 0000000000..2998e0f82c --- /dev/null +++ b/sys/net/routing/nhdp/nhdp_writer.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Writer interface for message generation in NHDP + * + * @author Fabian Nack + */ + +#ifndef NHDP_WRITER_H_ +#define NHDP_WRITER_H_ + +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_writer.h" + +#include "nhdp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set up the NHDP writer + * + * Sets the writer's buffers, initializes the writer and registers HELLO messages + * as new message type. + */ +void nhdp_writer_init(void); + +/** + * @brief Clean up the NHDP writer + */ +void nhdp_writer_cleanup(void); + +/** + * @brief Register a new target interface in the writer + * + * @param[in] new_if Pointer to the writer target of the NHDP interface entry + */ +void nhdp_writer_register_if(struct rfc5444_writer_target *new_if); + +/** + * @brief Construct and send a HELLO message using the given interface + * + * @param[in] if_entry Pointer to NHDP interface entry the message must be created for + */ +void nhdp_writer_send_hello(nhdp_if_entry_t *if_entry); + +/** + * @brief Add a NHDP address to the currently constructed message + * + * @note + * Must not be called from outside the NHDP writer's message creation process. + * + * @param[in] wr Pointer to the current NHDP writer used for message creation + * @param[in] addr Pointer to a NHDP address to add to the HELLO message + * @param[in] type TLV type for the address + * @param[in] value TLV value for the address + */ +void nhdp_writer_add_addr(struct rfc5444_writer *wr, nhdp_addr_t *addr, + enum rfc5444_addrtlv_iana type, uint8_t value); + +#ifdef __cplusplus +} +#endif + +#endif /* NHDP_WRITER_H_ */ +/** @} */ diff --git a/sys/net/routing/nhdp/nib_table.c b/sys/net/routing/nhdp/nib_table.c new file mode 100644 index 0000000000..d72a16016e --- /dev/null +++ b/sys/net/routing/nhdp/nib_table.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Neighbor Information Base implementation for NHDP + * + * @author Fabian Nack + * + * @} + */ + +#include "timex.h" +#include "mutex.h" +#include "vtimer.h" +#include "utlist.h" + +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_writer.h" + +#include "nib_table.h" +#include "iib_table.h" +#include "nhdp_address.h" +#include "nhdp_writer.h" + +/* Internal variables */ +static mutex_t mtx_nib_access = MUTEX_INIT; +static nib_entry_t *nib_entry_head = NULL; +static nib_lost_address_entry_t *nib_lost_address_entry_head = NULL; + +/* Internal function prototypes */ +static nib_entry_t *add_nib_entry(nhdp_addr_entry_t *nb_list); +static void rem_nib_entry(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list, + nhdp_addr_entry_t **out_list, timex_t *now); +static void clear_nb_addresses(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list, + nhdp_addr_entry_t **out_list, timex_t *now); +static int add_lost_neighbor_address(nhdp_addr_t *lost_addr, timex_t *now); +static void rem_ln_entry(nib_lost_address_entry_t *ln_entry); + + +/*---------------------------------------------------------------------------* + * Neighbor Information Base API * + *---------------------------------------------------------------------------*/ + +nib_entry_t *nib_process_hello(nhdp_addr_entry_t *nb_list, nhdp_addr_entry_t **out_list) +{ + nib_entry_t *nb_match = NULL; + timex_t now; + uint8_t matches = 0; + + mutex_lock(&mtx_nib_access); + + if (nb_list) { + nib_entry_t *nib_elt, *nib_tmp; + + vtimer_now(&now); + + LL_FOREACH_SAFE(nib_entry_head, nib_elt, nib_tmp) { + nhdp_addr_entry_t *list_elt; + LL_FOREACH(nib_elt->address_list_head, list_elt) { + nhdp_addr_entry_t *list_elt2; + LL_FOREACH(nb_list, list_elt2) { + if (list_elt->address == list_elt2->address) { + /* Addresses are equal (same NHDP address db entry) */ + matches++; + + if (matches > 1) { + /* Multiple matching nb tuples, delete the previous one */ + iib_propagate_nb_entry_change(nb_match, nib_elt); + rem_nib_entry(nb_match, nb_list, out_list, &now); + } + + nb_match = nib_elt; + break; + } + } + + if (nb_match == nib_elt) { + /* This nb tuple is already detected as matching */ + break; + } + } + } + + /* Add or update nb tuple */ + if (matches > 0) { + /* We found matching nb tuples, reuse the last one */ + clear_nb_addresses(nb_match, nb_list, out_list, &now); + + if (matches > 1) { + nb_match->symmetric = 0; + } + + nb_match->address_list_head = nhdp_generate_new_addr_list(nb_list); + + if (!nb_match->address_list_head) { + /* Insufficient memory */ + LL_DELETE(nib_entry_head, nb_match); + free(nb_match); + nb_match = NULL; + } + } + else { + nb_match = add_nib_entry(nb_list); + } + } + + mutex_unlock(&mtx_nib_access); + return nb_match; +} + +void nib_fill_wr_addresses(struct rfc5444_writer *wr) +{ + nib_entry_t *nib_elt; + nhdp_addr_entry_t *addr_elt; + nib_lost_address_entry_t *lost_elt, *lost_tmp; + timex_t now; + + mutex_lock(&mtx_nib_access); + + vtimer_now(&now); + + /* Add addresses of symmetric neighbors to HELLO msg */ + LL_FOREACH(nib_entry_head, nib_elt) { + if (nib_elt->symmetric) { + LL_FOREACH(nib_elt->address_list_head, addr_elt) { + /* Check whether address is not already included with link status symmetric */ + if (!NHDP_ADDR_TMP_IN_SYM(addr_elt->address)) { + nhdp_writer_add_addr(wr, addr_elt->address, RFC5444_ADDRTLV_OTHER_NEIGHB, + RFC5444_OTHERNEIGHB_SYMMETRIC); + addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_SYM; + } + } + } + } + + /* Add lost addresses of neighbors to HELLO msg */ + LL_FOREACH_SAFE(nib_lost_address_entry_head, lost_elt, lost_tmp) { + if (timex_cmp(lost_elt->expiration_time, now) != 1) { + /* Entry expired, remove it */ + rem_ln_entry(lost_elt); + } + else { + /* Check if address is not already present in one of the temporary lists */ + if (!NHDP_ADDR_TMP_IN_ANY(lost_elt->address)) { + /* Address is not present in one of the lists, add it */ + nhdp_writer_add_addr(wr, lost_elt->address, RFC5444_ADDRTLV_OTHER_NEIGHB, + RFC5444_OTHERNEIGHB_LOST); + } + } + } + + mutex_unlock(&mtx_nib_access); +} + +void nib_rem_nb_entry(nib_entry_t *nib_entry) +{ + nhdp_free_addr_list(nib_entry->address_list_head); + LL_DELETE(nib_entry_head, nib_entry); + free(nib_entry); +} + +void nib_set_nb_entry_sym(nib_entry_t *nib_entry) +{ + nib_lost_address_entry_t *ln_elt, *ln_tmp; + nhdp_addr_entry_t *nb_elt; + + nib_entry->symmetric = 1; + LL_FOREACH(nib_entry->address_list_head, nb_elt) { + LL_FOREACH_SAFE(nib_lost_address_entry_head, ln_elt, ln_tmp) { + /* Remove all Lost Neighbor Tuples matching an address of the newly sym nb */ + if (ln_elt->address == nb_elt->address) { + rem_ln_entry(ln_elt); + break; + } + } + } +} + +void nib_reset_nb_entry_sym(nib_entry_t *nib_entry, timex_t *now) +{ + nhdp_addr_entry_t *nb_elt; + + nib_entry->symmetric = 0; + LL_FOREACH(nib_entry->address_list_head, nb_elt) { + /* Add a Lost Neighbor Tuple for each address of the neighbor */ + if (add_lost_neighbor_address(nb_elt->address, now) == -1) { + /* Insufficient memory */ + return; + } + } +} + + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +/** + * Add a Neighbor Tuple with the given address list + */ +static nib_entry_t *add_nib_entry(nhdp_addr_entry_t *nb_list) +{ + nib_entry_t *new_elem; + + new_elem = malloc(sizeof(nib_entry_t)); + + if (!new_elem) { + /* Insufficient memory */ + return NULL; + } + + /* Copy address list to new neighbor tuple */ + new_elem->address_list_head = nhdp_generate_new_addr_list(nb_list); + + if (!new_elem->address_list_head) { + /* Insufficient memory */ + free(new_elem); + return NULL; + } + + new_elem->symmetric = 0; + LL_PREPEND(nib_entry_head, new_elem); + + return new_elem; +} + +/** + * Remove a given Neighbor Tuple + */ +static void rem_nib_entry(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list, + nhdp_addr_entry_t **out_list, timex_t *now) +{ + clear_nb_addresses(nib_entry, nb_list, out_list, now); + LL_DELETE(nib_entry_head, nib_entry); + free(nib_entry); +} + +/** + * Clear address list of a Neighbor Tuple and add Lost Neighbor Tuple for addresses + * no longer used by this neighbor + */ +static void clear_nb_addresses(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list, + nhdp_addr_entry_t **out_list, timex_t *now) +{ + nhdp_addr_entry_t *elt, *nib_elt, *nib_tmp; + uint8_t found; + + LL_FOREACH_SAFE(nib_entry->address_list_head, nib_elt, nib_tmp) { + found = 0; + LL_FOREACH(nb_list, elt) { + /* Check whether address is still present in the new neighbor address list */ + if (nib_elt->address == elt->address) { + /* Simply free the address entry */ + nhdp_free_addr_entry(nib_elt); + found = 1; + break; + } + } + + if (!found) { + /* Address is not in the newly received address list of the neighbor */ + /* Add it to the Removed Address List (out_list) */ + LL_PREPEND(*out_list, nib_elt); + + if (nib_entry->symmetric) { + /* Additionally create a Lost Neighbor Tuple for symmetric neighbors */ + add_lost_neighbor_address(nib_elt->address, now); + } + } + } + nib_entry->address_list_head = NULL; +} + +/** + * Add or update a Lost Neighbor Tuple + */ +static int add_lost_neighbor_address(nhdp_addr_t *lost_addr, timex_t *now) +{ + nib_lost_address_entry_t *elt; + timex_t n_hold = timex_from_uint64(((uint64_t)NHDP_N_HOLD_TIME_MS) * MS_IN_USEC); + + LL_FOREACH(nib_lost_address_entry_head, elt) { + if (elt->address == lost_addr) { + /* Existing entry for this address, no need to add a new one */ + if (timex_cmp(elt->expiration_time, *now) == -1) { + /* Entry expired, so just update expiration time */ + elt->expiration_time = timex_add(*now, n_hold); + } + + return 0; + } + } + + /* No existing entry, create a new one */ + elt = malloc(sizeof(nib_lost_address_entry_t)); + + if (!elt) { + /* Insufficient memory */ + return -1; + } + + /* Increment usage counter of address in central NHDP address storage */ + lost_addr->usg_count++; + elt->address = lost_addr; + elt->expiration_time = timex_add(*now, n_hold); + LL_PREPEND(nib_lost_address_entry_head, elt); + + return 0; +} + +/** + * Remove a given Lost Neighbor Tuple + */ +static void rem_ln_entry(nib_lost_address_entry_t *ln_entry) +{ + nhdp_decrement_addr_usage(ln_entry->address); + LL_DELETE(nib_lost_address_entry_head, ln_entry); + free(ln_entry); +} diff --git a/sys/net/routing/nhdp/nib_table.h b/sys/net/routing/nhdp/nib_table.h new file mode 100644 index 0000000000..9971b729d1 --- /dev/null +++ b/sys/net/routing/nhdp/nib_table.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup nhdp + * @{ + * + * @file + * @brief Neighbor Information Base interface for NHDP + * + * @author Fabian Nack + */ + +#ifndef NIB_TABLE_H_ +#define NIB_TABLE_H_ + +#include "timex.h" + +#include "rfc5444/rfc5444_writer.h" + +#include "nhdp_address.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Neighbor Set entry (neighbor tuple) + */ +typedef struct nib_entry_t { + nhdp_addr_entry_t *address_list_head; /**< Pointer to this tuple's addresses*/ + uint8_t symmetric; /**< Flag whether sym link to this nb exists */ + struct nib_entry_t *next; /**< Pointer to next list entry */ +} nib_entry_t; + +/** + * @brief Lost Neighbor Set entry (lost neighbor tuple, lnt) + */ +typedef struct nib_lost_address_entry_t { + nhdp_addr_t *address; /**< Pointer to addr represented by this lnt */ + timex_t expiration_time; /**< Time at which entry expires */ + struct nib_lost_address_entry_t *next; /**< Pointer to next list entry */ +} nib_lost_address_entry_t; + +/** + * @brief Process a received HELLO message in the NIB + * + * @note + * Must not be called from outside the NHDP reader's message processing. + * + * @param[in] nb_list Pointer to the Neighbor Address List from the received HELLO + * @param[out] out_list Pointer to the created Removed Address List + * + * @return Pointer to the new Neighbor Tuple + * @return NULL on error + */ +nib_entry_t *nib_process_hello(nhdp_addr_entry_t *nb_list, nhdp_addr_entry_t **out_list); + +/** + * @brief Add addresses to the currently constructed HELLO message + * + * @note + * Must not be called from outside the NHDP writer's message creation process. + * + * @param[in] wr The NHDP writer used for message construction + */ +void nib_fill_wr_addresses(struct rfc5444_writer *wr); + +/** + * @brief Remove a Neighbor Tuple + * + * @param[in] nib_entry Pointer to the Neighbor Tuple + */ +void nib_rem_nb_entry(nib_entry_t *nib_entry); + +/** + * @brief Set a Neighbor Tuple's symmetry flag + * + * Removes all Lost Neighbor Tuples representing addresses included in the + * Neighbor Tuple's address list. + * + * @param[in] nib_entry Pointer to the Neighbor Tuple + */ +void nib_set_nb_entry_sym(nib_entry_t *nib_entry); + +/** + * @brief Reset a Neighbor Tuple's symmetry flag + * + * Adds a Lost Neighbor Tuple for every address in the Neighbor Tuple's + * address list. + * + * @param[in] nib_entry Pointer to the Neighbor Tuple + * @param[in] now Pointer to current time timex representation + */ +void nib_reset_nb_entry_sym(nib_entry_t *nib_entry, timex_t *now); + +#ifdef __cplusplus +} +#endif + +#endif /* NIB_TABLE_H_ */ +/** @} */