diff --git a/Makefile.dep b/Makefile.dep index 5e40df93ee..2c14046231 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -48,6 +48,11 @@ ifneq (,$(filter sixlowpan,$(USEMODULE))) USEMODULE += vtimer endif +ifneq (,$(filter ng_sixlowpan_ctx,$(USEMODULE))) + USEMODULE += ng_ipv6_addr + USEMODULE += vtimer +endif + ifneq (,$(filter ng_ipv6_hdr,$(USEMODULE))) USEMODULE += ng_inet_csum USEMODULE += ng_pktbuf diff --git a/sys/Makefile b/sys/Makefile index b734317e10..dd4b6ba0dc 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -92,6 +92,9 @@ endif ifneq (,$(filter ng_pktbuf,$(USEMODULE))) DIRS += net/crosslayer/ng_pktbuf endif +ifneq (,$(filter ng_sixlowpan_ctx,$(USEMODULE))) + DIRS += net/network_layer/ng_sixlowpan/ctx +endif ifneq (,$(filter netapi,$(USEMODULE))) DIRS += net/crosslayer/netapi endif diff --git a/sys/include/net/ng_sixlowpan/ctx.h b/sys/include/net/ng_sixlowpan/ctx.h new file mode 100644 index 0000000000..275408e08b --- /dev/null +++ b/sys/include/net/ng_sixlowpan/ctx.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 net_ng_sixlowpan_ctx Contexts for 6LoWPAN address compression + * @ingroup net_ng_sixlowpan + * @brief Context buffer for stateful 6LoWPAN address compression + * @see + * RFC 6282, section 3.1.2 + * + * @see + * RFC 6775, section 4.2 + * + * @{ + * + * @file + * @brief Context buffer definitions + * + * @author Martine Lenders + */ +#ifndef NG_SIXLOWPAN_CTX_H_ +#define NG_SIXLOWPAN_CTX_H_ + +#include + +#include "net/ng_ipv6/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NG_SIXLOWPAN_CTX_SIZE (16) /**< maximum number of entries in + * context buffer */ + +/** + * @brief Entry in the 6LoWPAN context buffer. + */ +typedef struct { + ng_ipv6_addr_t prefix; /**< The prefix associated to this context. */ + uint8_t prefix_len; /**< Length of ng_sixlowpan_ctx_t::prefix in bit. */ + /** + * @brief 4-bit Context ID. + * + * @note This needs to be here to easily translate prefixes to + * ID. + */ + uint8_t id; + /** + * @brief Lifetime in minutes this context is valid. + * + * @see + * 6LoWPAN Context Option + * + */ + uint16_t ltime; +} ng_sixlowpan_ctx_t; + +/** + * @brief Gets a context matching the given IPv6 address best with its prefix. + * + * @param[in] addr An IPv6 address. + * + * @return The context associated with the best prefix for @p addr. + * @return NULL if there is no such context. + */ +ng_sixlowpan_ctx_t *ng_sixlowpan_ctx_lookup_addr(const ng_ipv6_addr_t *addr); + +/** + * @brief Gets context by ID. + * + * @param[in] id A context ID. + * + * @return The context associated with @p id. + * @return NULL if there is no such context. + */ +ng_sixlowpan_ctx_t *ng_sixlowpan_ctx_lookup_id(uint8_t id); + +/** + * @brief Updates (or adds if currently not registered) a context + * + * @param[in] id The ID for the context. + * Must be < @ref NG_SIXLOWPAN_CTX_SIZE. + * @param[in] prefix The prefix for the context. + * @param[in] prefix_len Length of @p prefix in bits. Must be > 0, when + * @p ltime > 0. + * @param[in] ltime New lifetime of the context. The context will + * be removed if 0. + * + * @return The new context on success. + * @return NULL, on error or on removal. + */ +ng_sixlowpan_ctx_t *ng_sixlowpan_ctx_update(uint8_t id, const ng_ipv6_addr_t *prefix, + uint8_t prefix_len, uint16_t ltime); + + +#ifdef TEST_SUITES +/** + * @brief Resets the whole context buffer. + */ +void ng_sixlowpan_ctx_reset(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NG_SIXLOWPAN_CTX_H_ */ +/** @} */ diff --git a/sys/net/network_layer/ng_sixlowpan/ctx/Makefile b/sys/net/network_layer/ng_sixlowpan/ctx/Makefile new file mode 100644 index 0000000000..afa4a1ee6d --- /dev/null +++ b/sys/net/network_layer/ng_sixlowpan/ctx/Makefile @@ -0,0 +1,3 @@ +MODULE = ng_sixlowpan_ctx + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ng_sixlowpan/ctx/ng_sixlowpan_ctx.c b/sys/net/network_layer/ng_sixlowpan/ctx/ng_sixlowpan_ctx.c new file mode 100644 index 0000000000..7d4752c41a --- /dev/null +++ b/sys/net/network_layer/ng_sixlowpan/ctx/ng_sixlowpan_ctx.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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. + */ + +/** + * @{ + * + * @file + */ + +#include +#include + +#include "mutex.h" +#include "net/ng_sixlowpan/ctx.h" +#include "vtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static ng_sixlowpan_ctx_t _ctxs[NG_SIXLOWPAN_CTX_SIZE]; +static uint32_t _ctx_inval_times[NG_SIXLOWPAN_CTX_SIZE]; +static mutex_t _ctx_mutex = MUTEX_INIT; + +static uint32_t _current_minute(void); +static void _update_lifetime(unsigned int id); + +#if ENABLE_DEBUG +static char ipv6str[NG_IPV6_ADDR_MAX_STR_LEN]; +#endif + +static inline bool _still_valid(unsigned int id) +{ + _update_lifetime(id); + return (_ctxs[id].ltime > 0); +} + +ng_sixlowpan_ctx_t *ng_sixlowpan_ctx_lookup_addr(const ng_ipv6_addr_t *addr) +{ + uint8_t best = 0; + ng_sixlowpan_ctx_t *res = NULL; + + mutex_lock(&_ctx_mutex); + + for (unsigned int id = 0; id < NG_SIXLOWPAN_CTX_SIZE; id++) { + if (_still_valid(id)) { + uint8_t match = ng_ipv6_addr_match_prefix(&_ctxs[id].prefix, addr); + + if ((_ctxs[id].prefix_len <= match) && (match > best)) { + best = match; + res = &(_ctxs[id]); + } + } + } + + mutex_unlock(&_ctx_mutex); + +#if ENABLE_DEBUG + if (res != NULL) { + DEBUG("6lo ctx: found context (%u, %s/%" PRIu8 ") ", res->id, + ng_ipv6_addr_to_str(ipv6str, &res->prefix, sizeof(ipv6str)), + res->prefix_len); + DEBUG("for address %s\n", ng_ipv6_addr_to_str(ipv6str, addr, sizeof(ipv6str))); + } +#endif + + return res; +} + +ng_sixlowpan_ctx_t *ng_sixlowpan_ctx_lookup_id(uint8_t id) +{ + if (id >= NG_SIXLOWPAN_CTX_SIZE) { + return NULL; + } + + mutex_lock(&_ctx_mutex); + + if (_still_valid((unsigned int)id)) { + DEBUG("6lo ctx: found context (%u, %s/%" PRIu8 ")\n", id, + ng_ipv6_addr_to_str(ipv6str, &_ctxs[id].prefix, sizeof(ipv6str)), + _ctxs[id].prefix_len); + mutex_unlock(&_ctx_mutex); + return &(_ctxs[id]); + } + + mutex_unlock(&_ctx_mutex); + + return NULL; +} + +ng_sixlowpan_ctx_t *ng_sixlowpan_ctx_update(uint8_t id, const ng_ipv6_addr_t *prefix, + uint8_t prefix_len, uint16_t ltime) +{ + if ((id >= NG_SIXLOWPAN_CTX_SIZE)) { + return NULL; + } + + mutex_lock(&_ctx_mutex); + + _ctxs[id].ltime = ltime; + + if (ltime == 0) { + mutex_unlock(&_ctx_mutex); + DEBUG("6lo ctx: remove context (%u, %s/%" PRIu8 ")\n", id, + ng_ipv6_addr_to_str(ipv6str, &_ctxs[id].prefix, sizeof(ipv6str)), + _ctxs[id].prefix_len); + return NULL; + } + + /* test prefix_len now so that invalidation is possible regardless of the + * value. */ + if (prefix_len == 0) { + mutex_unlock(&_ctx_mutex); + _ctxs[id].ltime = 0; + return NULL; + } + + if (prefix_len > NG_IPV6_ADDR_BIT_LEN) { + _ctxs[id].prefix_len = NG_IPV6_ADDR_BIT_LEN; + } + else { + _ctxs[id].prefix_len = prefix_len; + } + + _ctxs[id].id = id; + + if (!ng_ipv6_addr_equal(&(_ctxs[id].prefix), prefix)) { + ng_ipv6_addr_set_unspecified(&(_ctxs[id].prefix)); + ng_ipv6_addr_init_prefix(&(_ctxs[id].prefix), prefix, + _ctxs[id].prefix_len); + } + DEBUG("6lo ctx: update context (%u, %s/%" PRIu8 "), lifetime: %" PRIu16 " min\n", + id, ng_ipv6_addr_to_str(ipv6str, &_ctxs[id].prefix, sizeof(ipv6str)), + _ctxs[id].prefix_len, _ctxs[id].ltime); + _ctx_inval_times[id] = ltime + _current_minute(); + + mutex_unlock(&_ctx_mutex); + + return &(_ctxs[id]); +} + +static uint32_t _current_minute(void) +{ + timex_t now; + vtimer_now(&now); + return now.seconds / 60; +} + +static void _update_lifetime(unsigned int id) +{ + uint32_t now; + + if (_ctxs[id].ltime == 0) { + return; + } + + now = _current_minute(); + + if (now >= _ctx_inval_times[id]) { + DEBUG("6lo ctx: context %u was invalidated\n", id); + _ctxs[id].ltime = 0; + } + else { + _ctxs[id].ltime = (uint16_t)(_ctx_inval_times[id] - now); + } +} + +#ifdef TEST_SUITES +#include + +void ng_sixlowpan_ctx_reset(void) +{ + memset(_ctxs, 0, sizeof(_ctxs)); +} +#endif + +/** @} */ diff --git a/tests/unittests/tests-sixlowpan_ctx/Makefile b/tests/unittests/tests-sixlowpan_ctx/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/tests/unittests/tests-sixlowpan_ctx/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-sixlowpan_ctx/Makefile.include b/tests/unittests/tests-sixlowpan_ctx/Makefile.include new file mode 100644 index 0000000000..4aff25f608 --- /dev/null +++ b/tests/unittests/tests-sixlowpan_ctx/Makefile.include @@ -0,0 +1 @@ +USEMODULE += ng_sixlowpan_ctx diff --git a/tests/unittests/tests-sixlowpan_ctx/tests-sixlowpan_ctx.c b/tests/unittests/tests-sixlowpan_ctx/tests-sixlowpan_ctx.c new file mode 100644 index 0000000000..3f8a384761 --- /dev/null +++ b/tests/unittests/tests-sixlowpan_ctx/tests-sixlowpan_ctx.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 Martine Lenders + * + * 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. + */ + +/** + * @{ + * + * @file + */ +#include +#include + +#include "embUnit.h" + +#include "net/ng_ipv6/addr.h" +#include "net/ng_sixlowpan/ctx.h" + +#include "unittests-constants.h" +#include "tests-sixlowpan_ctx.h" + +#define DEFAULT_TEST_ID (5) +#define DEFAULT_TEST_PREFIX { { \ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, \ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f \ + } \ + } +#define DEFAULT_TEST_PREFIX_LEN (63) + +#define OTHER_TEST_ID (12) +#define OTHER_TEST_PREFIX { { \ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, \ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f \ + } \ + } +#define WRONG_TEST_PREFIX { { \ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x05, \ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f \ + } \ + } + +static void tear_down(void) +{ + ng_sixlowpan_ctx_reset(); +} + +static void test_sixlowpan_ctx_update__success(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + + TEST_ASSERT_NOT_NULL(ng_sixlowpan_ctx_update(DEFAULT_TEST_ID, &addr, + DEFAULT_TEST_PREFIX_LEN, + TEST_UINT16)); + TEST_ASSERT_NOT_NULL(ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID)); +} + +static void test_sixlowpan_ctx_update__wrong_id1(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + /* NG_SIXLOWPAN_CTX_SIZE out of bound so neither context update nor lookup + * should not be possible */ + TEST_ASSERT_NULL(ng_sixlowpan_ctx_update(NG_SIXLOWPAN_CTX_SIZE, &addr, + DEFAULT_TEST_PREFIX_LEN, + TEST_UINT16)); + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_id(NG_SIXLOWPAN_CTX_SIZE)); + + TEST_ASSERT_NOT_NULL(ng_sixlowpan_ctx_update(DEFAULT_TEST_ID, &addr, + DEFAULT_TEST_PREFIX_LEN, + TEST_UINT16)); + TEST_ASSERT_NOT_NULL(ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID)); +} + +static void test_sixlowpan_ctx_update__wrong_id2(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + /* UINT8_MAX out of bound so neither context update nor lookup should not + * be possible */ + TEST_ASSERT_NULL(ng_sixlowpan_ctx_update(UINT8_MAX, &addr, + DEFAULT_TEST_PREFIX_LEN, + TEST_UINT16)); + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_id(UINT8_MAX)); + + TEST_ASSERT_NOT_NULL(ng_sixlowpan_ctx_update(DEFAULT_TEST_ID, &addr, + DEFAULT_TEST_PREFIX_LEN, + TEST_UINT16)); + TEST_ASSERT_NOT_NULL(ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID)); +} + +static void test_sixlowpan_ctx_update__wrong_prefix_len(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + + TEST_ASSERT_NULL(ng_sixlowpan_ctx_update(DEFAULT_TEST_ID, &addr, + 0, TEST_UINT16)); + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID)); +} + +static void test_sixlowpan_ctx_update__ltime0(void) +{ + test_sixlowpan_ctx_update__success(); /* add context to DEFAULT_TEST_ID */ + TEST_ASSERT_NULL(ng_sixlowpan_ctx_update(DEFAULT_TEST_ID, NULL, 0, 0)); + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID)); +} + +static void test_sixlowpan_ctx_lookup_addr__empty(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_addr(&addr)); +} + +static void test_sixlowpan_ctx_lookup_addr__same_addr(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + ng_sixlowpan_ctx_t *ctx; + + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + TEST_ASSERT_NOT_NULL((ctx = ng_sixlowpan_ctx_lookup_addr(&addr))); + TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_ID, ctx->id); + TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_PREFIX_LEN, ctx->prefix_len); + TEST_ASSERT(TEST_UINT16 >= ctx->ltime); + TEST_ASSERT(DEFAULT_TEST_PREFIX_LEN >= ng_ipv6_addr_match_prefix(&addr, &ctx->prefix)); +} + +static void test_sixlowpan_ctx_lookup_addr__other_addr_same_prefix(void) +{ + ng_ipv6_addr_t addr1 = DEFAULT_TEST_PREFIX; + ng_ipv6_addr_t addr2 = OTHER_TEST_PREFIX; + ng_sixlowpan_ctx_t *ctx; + + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + TEST_ASSERT_NOT_NULL((ctx = ng_sixlowpan_ctx_lookup_addr(&addr2))); + TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_ID, ctx->id); + TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_PREFIX_LEN, ctx->prefix_len); + TEST_ASSERT(TEST_UINT16 >= ctx->ltime); + TEST_ASSERT(DEFAULT_TEST_PREFIX_LEN >= ng_ipv6_addr_match_prefix(&addr1, &ctx->prefix)); +} + +static void test_sixlowpan_ctx_lookup_addr__other_addr_other_prefix(void) +{ + ng_ipv6_addr_t addr = WRONG_TEST_PREFIX; + + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_addr(&addr)); +} + +static void test_sixlowpan_ctx_lookup_id__empty(void) +{ + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID)); +} + +static void test_sixlowpan_ctx_lookup_id__wrong_id(void) +{ + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + TEST_ASSERT_NULL(ng_sixlowpan_ctx_lookup_id(OTHER_TEST_ID)); +} + +static void test_sixlowpan_ctx_lookup_id__success(void) +{ + ng_ipv6_addr_t addr = DEFAULT_TEST_PREFIX; + ng_sixlowpan_ctx_t *ctx; + + /* add context DEFAULT_TEST_PREFIX to DEFAULT_TEST_ID */ + test_sixlowpan_ctx_update__success(); + TEST_ASSERT_NOT_NULL((ctx = ng_sixlowpan_ctx_lookup_id(DEFAULT_TEST_ID))); + TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_ID, ctx->id); + TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_PREFIX_LEN, ctx->prefix_len); + TEST_ASSERT(TEST_UINT16 >= ctx->ltime); + TEST_ASSERT(DEFAULT_TEST_PREFIX_LEN >= ng_ipv6_addr_match_prefix(&addr, &ctx->prefix)); +} + +Test *tests_sixlowpan_ctx_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_sixlowpan_ctx_update__wrong_id1), + new_TestFixture(test_sixlowpan_ctx_update__wrong_id2), + new_TestFixture(test_sixlowpan_ctx_update__wrong_prefix_len), + new_TestFixture(test_sixlowpan_ctx_update__success), + new_TestFixture(test_sixlowpan_ctx_update__ltime0), + new_TestFixture(test_sixlowpan_ctx_lookup_addr__empty), + new_TestFixture(test_sixlowpan_ctx_lookup_addr__same_addr), + new_TestFixture(test_sixlowpan_ctx_lookup_addr__other_addr_same_prefix), + new_TestFixture(test_sixlowpan_ctx_lookup_addr__other_addr_other_prefix), + new_TestFixture(test_sixlowpan_ctx_lookup_id__empty), + new_TestFixture(test_sixlowpan_ctx_lookup_id__wrong_id), + new_TestFixture(test_sixlowpan_ctx_lookup_id__success), + }; + + EMB_UNIT_TESTCALLER(sixlowpan_ctx_tests, NULL, tear_down, fixtures); + + return (Test *)&sixlowpan_ctx_tests; +} + +void tests_sixlowpan_ctx(void) +{ + TESTS_RUN(tests_sixlowpan_ctx_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-sixlowpan_ctx/tests-sixlowpan_ctx.h b/tests/unittests/tests-sixlowpan_ctx/tests-sixlowpan_ctx.h new file mode 100644 index 0000000000..ad96e58e49 --- /dev/null +++ b/tests/unittests/tests-sixlowpan_ctx/tests-sixlowpan_ctx.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file tests-sixlowpan_ctx.h + * @brief Unittests for the ``sixlowpan_ctx`` module + * + * @author Martine Lenders + */ +#ifndef TESTS_SIXLOWPAN_CTX_H_ +#define TESTS_SIXLOWPAN_CTX_H_ + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_sixlowpan_ctx(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_SIXLOWPAN_CTX_H_ */ +/** @} */