diff --git a/Makefile.dep b/Makefile.dep
index e859a6e38..03b1dfa7d 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -64,6 +64,14 @@ ifneq (,$(filter gnrc_sixlowpan_router_default,$(USEMODULE)))
USEMODULE += gnrc_sixlowpan_iphc
endif
+ifneq (,$(filter gnrc_sixlowpan_border_router_default,$(USEMODULE)))
+ USEMODULE += gnrc_ipv6_router_default
+ USEMODULE += gnrc_sixlowpan_nd_border_router
+ USEMODULE += gnrc_sixlowpan_router
+ USEMODULE += gnrc_sixlowpan_frag
+ USEMODULE += gnrc_sixlowpan_iphc
+endif
+
ifneq (,$(filter gnrc_sixlowpan_router,$(USEMODULE)))
USEMODULE += gnrc_sixlowpan
endif
@@ -89,6 +97,10 @@ ifneq (,$(filter gnrc_sixlowpan_ctx,$(USEMODULE)))
USEMODULE += vtimer
endif
+ifneq (,$(filter gnrc_sixlowpan_nd_border_router,$(USEMODULE)))
+ USEMODULE += gnrc_sixlowpan_nd_router
+endif
+
ifneq (,$(filter gnrc_sixlowpan_nd_router,$(USEMODULE)))
USEMODULE += gnrc_sixlowpan_nd
endif
diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules
index a9c4dea1d..1e123a297 100644
--- a/Makefile.pseudomodules
+++ b/Makefile.pseudomodules
@@ -3,6 +3,8 @@ PSEUDOMODULES += gnrc_ipv6_default
PSEUDOMODULES += gnrc_ipv6_router
PSEUDOMODULES += gnrc_ipv6_router_default
PSEUDOMODULES += gnrc_sixlowpan_default
+PSEUDOMODULES += gnrc_sixlowpan_border_router_default
+PSEUDOMODULES += gnrc_sixlowpan_nd_border_router
PSEUDOMODULES += gnrc_sixlowpan_router
PSEUDOMODULES += gnrc_sixlowpan_router_default
PSEUDOMODULES += gnrc_pktbuf
diff --git a/doc/doxygen/riot.doxyfile b/doc/doxygen/riot.doxyfile
index 7260c3d2e..432da9515 100644
--- a/doc/doxygen/riot.doxyfile
+++ b/doc/doxygen/riot.doxyfile
@@ -2005,6 +2005,7 @@ PREDEFINED = DOXYGEN \
MODULE_GNRC_NDP_ROUTER \
MODULE_GNRC_SIXLOWPAN \
MODULE_GNRC_SIXLOWPAN_ND_ROUTER \
+ MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER \
MODULE_GNRC_UDP
diff --git a/sys/include/net/gnrc/sixlowpan/nd.h b/sys/include/net/gnrc/sixlowpan/nd.h
index 919008a13..0b3b3aefa 100644
--- a/sys/include/net/gnrc/sixlowpan/nd.h
+++ b/sys/include/net/gnrc/sixlowpan/nd.h
@@ -33,6 +33,7 @@
#include "net/sixlowpan/nd.h"
#include "timex.h"
+#include "net/gnrc/sixlowpan/nd/border_router.h"
#include "net/gnrc/sixlowpan/nd/router.h"
#ifdef __cplusplus
@@ -78,6 +79,16 @@ extern "C" {
#define GNRC_SIXLOWPAN_ND_AR_LTIME (15U)
#endif
+/**
+ * @name Border router constants
+ * @{
+ * @see
+ * RFC 6775, section 9
+ *
+ */
+#define GNRC_SIXLOWPAN_ND_RTR_MIN_CTX_DELAY (300U) /**< minimum delay between context change and
+ * stop of C=0 dissimination in seconds */
+/** @} */
/**
* @name Host constants
* @{
diff --git a/sys/include/net/gnrc/sixlowpan/nd/border_router.h b/sys/include/net/gnrc/sixlowpan/nd/border_router.h
new file mode 100644
index 000000000..9e84d5b6c
--- /dev/null
+++ b/sys/include/net/gnrc/sixlowpan/nd/border_router.h
@@ -0,0 +1,43 @@
+/*
+ * 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 gnrc_sixlowpan_nd_border_router Border router part of 6LoWPAN-ND
+ * @ingroup gnrc_sixlowpan_nd
+ * @brief Border router part of 6LoWPAN-ND
+ * @{
+ *
+ * @file
+ * @brief Border router definitions for 6LoWPAN.
+ *
+ * @author Martine Lenders
+ */
+#ifndef GNRC_SIXLOWPAN_BORDER_ROUTER_H_
+#define GNRC_SIXLOWPAN_BORDER_ROUTER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Default lifetime in minutes for 6LoWPAN border router information.
+ *
+ * @see
+ * RFC 6775, section 4.3
+ *
+ */
+#ifndef GNRC_SIXLOWPAN_ND_BORDER_ROUTER_DEFAULT_LTIME
+#define GNRC_SIXLOWPAN_ND_BORDER_ROUTER_DEFAULT_LTIME (10000U)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GNRC_SIXLOWPAN_BORDER_ROUTER_H_ */
+/** @} */
diff --git a/sys/include/net/gnrc/sixlowpan/nd/router.h b/sys/include/net/gnrc/sixlowpan/nd/router.h
index 9d02c7eca..296e7a270 100644
--- a/sys/include/net/gnrc/sixlowpan/nd/router.h
+++ b/sys/include/net/gnrc/sixlowpan/nd/router.h
@@ -156,6 +156,71 @@ bool gnrc_sixlowpan_nd_router_abr_older(sixlowpan_nd_opt_abr_t *abr_opt);
*/
void gnrc_sixlowpan_nd_router_abr_remove(gnrc_sixlowpan_nd_router_abr_t *abr);
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+/**
+ * @brief Makes this node a new border router.
+ *
+ * @per addr != NULL
+ *
+ * @param[in] addr The local address to use in the ABROs
+ * @param[in] ltime The lifetime to advertise in the ABROs. 0 assumes a default value of
+ * @ref GNRC_SIXLOWPAN_ND_BORDER_ROUTER_DEFAULT_LTIME
+ *
+ * @return The new border router object.
+ * @return NULL, on error.
+ */
+gnrc_sixlowpan_nd_router_abr_t *gnrc_sixlowpan_nd_router_abr_create(ipv6_addr_t *addr,
+ unsigned int ltime);
+
+/**
+ * @brief Adds a prefix for this border router to manage.
+ *
+ * @pre iface != NULL && prefix != NULL
+ *
+ * @param[in] abr The local border router
+ * @param[in] iface The IPv6 interface the prefix was added to.
+ * @param[in] prefix The prefix.
+ *
+ * @return 0, on success
+ * @return -ENOMEM, if no space for the new prefix is available.
+ * @return -ENOENT, if @p abr is not registered.
+ */
+int gnrc_sixlowpan_nd_router_abr_add_prf(gnrc_sixlowpan_nd_router_abr_t* abr,
+ gnrc_ipv6_netif_t *iface, gnrc_ipv6_netif_addr_t *prefix);
+
+/**
+ * @brief Removes a prefix from this border router.
+ *
+ * @param[in] abr The local border router
+ * @param[in] iface The IPv6 interface the prefix was added to.
+ * @param[in] prefix The prefix.
+ */
+void gnrc_sixlowpan_nd_router_abr_rem_prf(gnrc_sixlowpan_nd_router_abr_t *abr,
+ gnrc_ipv6_netif_t *iface, gnrc_ipv6_netif_addr_t *prefix);
+
+/**
+ * @brief Adds a context for this border router to manage.
+ *
+ * @param[in] abr The local border router
+ * @param[in] ctx The context to be add.
+ *
+ * @return 0, on success
+ * @return -EINVAL, if @p ctx is greater than 15.
+ * @return -ENOENT, if @p abr is not registered.
+ */
+int gnrc_sixlowpan_nd_router_abr_add_ctx(gnrc_sixlowpan_nd_router_abr_t *abr, uint8_t cid);
+
+/**
+ * @brief Removes a context from this border router.
+ *
+ * @param[in] abr The local border router
+ * @param[in] ctx The context to be remove.
+ */
+void gnrc_sixlowpan_nd_router_abr_rem_ctx(gnrc_sixlowpan_nd_router_abr_t *abr, uint8_t cid);
+#else
+#define gnrc_sixlowpan_nd_router_abr_create(addr, ltime) (NULL)
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c b/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
index cc8b967f0..30fc643ad 100644
--- a/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
+++ b/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
@@ -111,6 +111,12 @@ static ipv6_addr_t *_add_addr_to_entry(gnrc_ipv6_netif_t *entry, const ipv6_addr
gnrc_ndp_router_retrans_rtr_adv(entry);
mutex_lock(&entry->mutex); /* relock mutex */
}
+#endif
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+ gnrc_sixlowpan_nd_router_abr_t *abr = gnrc_sixlowpan_nd_router_abr_get();
+ if (gnrc_sixlowpan_nd_router_abr_add_prf(abr, entry, tmp_addr) < 0) {
+ DEBUG("ipv6_netif: error adding prefix to 6LoWPAN-ND management\n");
+ }
#endif
}
else {
@@ -303,6 +309,10 @@ static void _remove_addr_from_entry(gnrc_ipv6_netif_t *entry, ipv6_addr_t *addr)
return;
}
#endif
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+ gnrc_sixlowpan_nd_router_abr_t *abr = gnrc_sixlowpan_nd_router_abr_get();
+ gnrc_sixlowpan_nd_router_abr_rem_prf(abr, entry, &entry->addrs[i]);
+#endif
mutex_unlock(&entry->mutex);
return;
@@ -752,6 +762,9 @@ void gnrc_ipv6_netif_init_by_dev(void)
{
kernel_pid_t ifs[GNRC_NETIF_NUMOF];
size_t ifnum = gnrc_netif_get(ifs);
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+ bool abr_init = false;
+#endif
for (size_t i = 0; i < ifnum; i++) {
ipv6_addr_t addr;
@@ -826,6 +839,14 @@ void gnrc_ipv6_netif_init_by_dev(void)
#endif
#ifdef MODULE_GNRC_SIXLOWPAN_ND
if (ipv6_if->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+ /* first interface wins */
+ if (!abr_init) {
+ gnrc_sixlowpan_nd_router_abr_create(&addr, 0);
+ gnrc_ipv6_netif_set_rtr_adv(ipv6_if, true);
+ abr_init = true;
+ }
+#endif
gnrc_sixlowpan_nd_init(ipv6_if);
continue; /* skip gnrc_ndp_host_init() */
}
diff --git a/sys/net/gnrc/network_layer/sixlowpan/ctx/gnrc_sixlowpan_ctx.c b/sys/net/gnrc/network_layer/sixlowpan/ctx/gnrc_sixlowpan_ctx.c
index 5c3ff30ae..c1a186da2 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/ctx/gnrc_sixlowpan_ctx.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/ctx/gnrc_sixlowpan_ctx.c
@@ -127,7 +127,6 @@ gnrc_sixlowpan_ctx_t *gnrc_sixlowpan_ctx_update(uint8_t id, const ipv6_addr_t *p
_ctx_inval_times[id] = ltime + _current_minute();
mutex_unlock(&_ctx_mutex);
-
return &(_ctxs[id]);
}
diff --git a/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c b/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c
index a30c25bf1..0d6963f98 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c
@@ -95,6 +95,17 @@ static void _add_ctx(gnrc_sixlowpan_nd_router_abr_t *abr, sixlowpan_nd_opt_6ctx_
bf_set(abr->ctxs, sixlowpan_nd_opt_6ctx_get_cid(ctx_opt));
}
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+static inline bool _is_me(ipv6_addr_t *addr)
+{
+ ipv6_addr_t *res;
+
+ return (gnrc_ipv6_netif_find_by_addr(&res, addr) != KERNEL_PID_UNDEF);
+}
+#else
+#define _is_me(ignore) (false)
+#endif
+
gnrc_sixlowpan_nd_router_abr_t *gnrc_sixlowpan_nd_router_abr_get(void)
{
if (ipv6_addr_is_unspecified(&_abrs[0].addr)) {
@@ -113,6 +124,10 @@ bool gnrc_sixlowpan_nd_router_abr_older(sixlowpan_nd_opt_abr_t *abr_opt)
return true;
}
+ if (_is_me(&abr_opt->braddr)) {
+ return false;
+ }
+
abr = _get_abr(&abr_opt->braddr);
if (abr == NULL) {
@@ -155,9 +170,11 @@ void gnrc_sixlowpan_nd_opt_abr_handle(kernel_pid_t iface, ndp_rtr_adv_t *rtr_adv
gnrc_sixlowpan_nd_router_abr_t *abr;
timex_t t = { 0, 0 };
+ if (_is_me(&abr_opt->braddr)) {
+ return;
+ }
/* validity and version was checked in previously called
* gnrc_sixlowpan_nd_router_abr_older() */
-
abr = _get_abr(&abr_opt->braddr);
if (abr == NULL) {
@@ -167,7 +184,7 @@ void gnrc_sixlowpan_nd_opt_abr_handle(kernel_pid_t iface, ndp_rtr_adv_t *rtr_adv
abr->ltime = byteorder_ntohs(abr_opt->ltime);
if (abr->ltime == 0) {
- gnrc_sixlowpan_nd_router_abr_remove(abr);
+ abr->ltime = GNRC_SIXLOWPAN_ND_BORDER_ROUTER_DEFAULT_LTIME;
return;
}
@@ -242,4 +259,99 @@ gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_abr_build(uint32_t version, uint16_t ltime
return pkt;
}
+#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
+gnrc_sixlowpan_nd_router_abr_t *gnrc_sixlowpan_nd_router_abr_create(ipv6_addr_t *addr,
+ unsigned int ltime)
+{
+ assert(addr != NULL);
+ gnrc_sixlowpan_nd_router_abr_t *abr = _get_abr(addr);
+ if (abr == NULL) {
+ return NULL;
+ }
+ /* TODO: store and get this somewhere stable */
+ abr->version = 0;
+ abr->ltime = (uint16_t)ltime;
+ abr->addr.u64[0] = addr->u64[0];
+ abr->addr.u64[1] = addr->u64[1];
+ memset(abr->ctxs, 0, sizeof(abr->ctxs));
+ abr->prfs = NULL;
+ return abr;
+}
+
+int gnrc_sixlowpan_nd_router_abr_add_prf(gnrc_sixlowpan_nd_router_abr_t* abr,
+ gnrc_ipv6_netif_t *iface, gnrc_ipv6_netif_addr_t *prefix)
+{
+ assert((iface != NULL) && (prefix != NULL));
+ gnrc_sixlowpan_nd_router_prf_t *prf_ent;
+ if ((abr < _abrs) || (abr > (_abrs + GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF))) {
+ return -ENOENT;
+ }
+ prf_ent = _get_free_prefix(&prefix->addr, prefix->prefix_len);
+ if (prf_ent == NULL) {
+ return -ENOMEM;
+ }
+ prf_ent->iface = iface;
+ prf_ent->prefix = prefix;
+ LL_PREPEND(abr->prfs, prf_ent);
+ abr->version++; /* TODO: store somewhere stable */
+ return 0;
+}
+
+
+void gnrc_sixlowpan_nd_router_abr_rem_prf(gnrc_sixlowpan_nd_router_abr_t *abr,
+ gnrc_ipv6_netif_t *iface, gnrc_ipv6_netif_addr_t *prefix)
+{
+ assert((iface != NULL) && (prefix != NULL));
+ gnrc_sixlowpan_nd_router_prf_t *prf_ent = abr->prfs, *prev = NULL;
+ if ((abr < _abrs) || (abr > (_abrs + GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF))) {
+ return;
+ }
+ while (prf_ent) {
+ if (prf_ent->prefix == prefix) {
+ if (prev == NULL) {
+ abr->prfs = prf_ent->next;
+ }
+ else {
+ prev->next = prf_ent->next;
+ }
+ prf_ent->next = NULL;
+ prf_ent->iface = NULL;
+ prf_ent->prefix = NULL;
+ abr->version++; /* TODO: store somewhere stable */
+ break;
+ }
+ prev = prf_ent;
+ prf_ent = prf_ent->next;
+ }
+}
+
+int gnrc_sixlowpan_nd_router_abr_add_ctx(gnrc_sixlowpan_nd_router_abr_t *abr, uint8_t cid)
+{
+ if ((abr < _abrs) || (abr > (_abrs + GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF))) {
+ return -ENOENT;
+ }
+ if (cid >= GNRC_SIXLOWPAN_CTX_SIZE) {
+ return -EINVAL;
+ }
+ if (bf_isset(abr->ctxs, cid)) {
+ return -EADDRINUSE;
+ }
+ bf_set(abr->ctxs, cid);
+ abr->version++; /* TODO: store somewhere stable */
+ return 0;
+}
+
+void gnrc_sixlowpan_nd_router_abr_rem_ctx(gnrc_sixlowpan_nd_router_abr_t *abr, uint8_t cid)
+{
+ if ((abr < _abrs) || (abr > (_abrs + GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF))) {
+ return;
+ }
+ if (cid >= GNRC_SIXLOWPAN_CTX_SIZE) {
+ return;
+ }
+ bf_unset(abr->ctxs, cid);
+ abr->version++; /* TODO: store somewhere stable */
+ return;
+}
+#endif
/** @} */