From 7f6966aaccd8993d9712c451ae01a1c748e0b16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cenk=20G=C3=BCndo=C4=9Fan?= Date: Fri, 22 May 2015 21:47:37 +0200 Subject: [PATCH] ng_rpl: RPL for the gnrc network stack --- Makefile.dep | 10 + sys/Makefile | 3 + sys/include/net/ng_ipv6/netif.h | 4 +- sys/include/net/ng_rpl.h | 399 ++++++++++- sys/include/net/ng_rpl/dodag.h | 216 ++++++ sys/include/net/ng_rpl/of_manager.h | 46 ++ sys/include/net/ng_rpl/structs.h | 234 +++++++ sys/net/network_layer/ng_icmpv6/ng_icmpv6.c | 7 - sys/net/routing/ng_rpl/Makefile | 1 + sys/net/routing/ng_rpl/ng_rpl.c | 313 +++++++++ .../routing/ng_rpl/ng_rpl_control_messages.c | 619 ++++++++++++++++++ sys/net/routing/ng_rpl/ng_rpl_dodag.c | 398 +++++++++++ sys/net/routing/ng_rpl/of0.c | 94 +++ sys/net/routing/ng_rpl/of0.h | 43 ++ sys/net/routing/ng_rpl/rpl_of_manager.c | 54 ++ 15 files changed, 2427 insertions(+), 14 deletions(-) create mode 100644 sys/include/net/ng_rpl/dodag.h create mode 100644 sys/include/net/ng_rpl/of_manager.h create mode 100644 sys/include/net/ng_rpl/structs.h create mode 100644 sys/net/routing/ng_rpl/Makefile create mode 100644 sys/net/routing/ng_rpl/ng_rpl.c create mode 100644 sys/net/routing/ng_rpl/ng_rpl_control_messages.c create mode 100644 sys/net/routing/ng_rpl/ng_rpl_dodag.c create mode 100644 sys/net/routing/ng_rpl/of0.c create mode 100644 sys/net/routing/ng_rpl/of0.h create mode 100644 sys/net/routing/ng_rpl/rpl_of_manager.c diff --git a/Makefile.dep b/Makefile.dep index 1775fd02467a..85c785d83a7c 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -258,3 +258,13 @@ endif ifneq (,$(filter hih6130,$(USEMODULE))) USEMODULE += vtimer endif + +ifneq (,$(filter ng_rpl,$(USEMODULE))) + USEMODULE += timex + USEMODULE += vtimer + USEMODULE += ng_ipv6_router_default + USEMODULE += trickle + USEMODULE += net_help + USEMODULE += universal_address + USEMODULE += fib +endif diff --git a/sys/Makefile b/sys/Makefile index 6343af63fe93..94bce4564ddd 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -88,6 +88,9 @@ endif ifneq (,$(filter ng_zep,$(USEMODULE))) DIRS += net/application_layer/ng_zep endif +ifneq (,$(filter ng_rpl,$(USEMODULE))) + DIRS += net/routing/ng_rpl +endif ifneq (,$(filter ng_rpl_srh,$(USEMODULE))) DIRS += net/routing/ng_rpl/srh endif diff --git a/sys/include/net/ng_ipv6/netif.h b/sys/include/net/ng_ipv6/netif.h index dcb2b8851d73..a217739b038b 100644 --- a/sys/include/net/ng_ipv6/netif.h +++ b/sys/include/net/ng_ipv6/netif.h @@ -42,9 +42,9 @@ extern "C" { */ #ifndef NG_IPV6_NETIF_ADDR_NUMOF #ifdef MODULE_NG_IPV6_ROUTER -#define NG_IPV6_NETIF_ADDR_NUMOF (7) /* router needs all-routers multicast address */ +#define NG_IPV6_NETIF_ADDR_NUMOF (8) /* router needs all-routers multicast address */ #else -#define NG_IPV6_NETIF_ADDR_NUMOF (6) +#define NG_IPV6_NETIF_ADDR_NUMOF (7) #endif #endif diff --git a/sys/include/net/ng_rpl.h b/sys/include/net/ng_rpl.h index fc738e282314..67717a1ab6c9 100644 --- a/sys/include/net/ng_rpl.h +++ b/sys/include/net/ng_rpl.h @@ -1,5 +1,7 @@ /* + * Copyright (C) 2013 - 2014 INRIA. * Copyright (C) 2015 Martine Lenders + * Copyright (C) 2015 Cenk Gündoğan * * 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 @@ -7,27 +9,414 @@ */ /** - * @defgroup net_ng_rpl New RPL + * @defgroup net_ng_rpl RPL * @ingroup net * @{ * * @file - * @brief TODO + * @brief RPL header. Declaration of global variables and functions needed for + * core functionality of RPL. * - * @author Martine Lenders + * Header which includes all core RPL-functions. + * + * @author Eric Engel + * @author Fabian Brandt + * @author Martine Lenders + * @author Cenk Gündoğan */ + #ifndef NG_RPL_H_ #define NG_RPL_H_ - +#include +#include +#include "net/ng_ipv6.h" +#include "net/ng_icmpv6.h" +#include "net/ng_nettype.h" +#include "net/ng_rpl/structs.h" +#include "net/ng_rpl/dodag.h" +#include "net/ng_rpl/of_manager.h" +#include "inet_ntop.h" +#include "net/fib.h" +#include "vtimer.h" +#include "trickle.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Default stack size to use for the IPv6 thread + */ +#ifndef NG_RPL_STACK_SIZE +#define NG_RPL_STACK_SIZE (THREAD_STACKSIZE_DEFAULT) +#endif + +/** + * @brief Default priority for the RPL thread + */ +#ifndef NG_RPL_PRIO +#define NG_RPL_PRIO (THREAD_PRIORITY_MAIN - 4) +#endif + +/** + * @brief Default message queue size to use for the RPL thread. + */ +#ifndef NG_RPL_MSG_QUEUE_SIZE +#define NG_RPL_MSG_QUEUE_SIZE (8U) +#endif + +/** + * @brief Static initializer for the all-RPL-nodes multicast IPv6 + * address (ff02::1a) + * + * @see + * RFC 6550, section 6 + * + */ +#define NG_IPV6_ADDR_ALL_RPL_NODES {{ 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1a }} + +/** + * @brief Message type for lifetime updates + */ +#define NG_RPL_MSG_TYPE_LIFETIME_UPDATE (0x0900) + +/** + * @brief Message type for trickle intervals + */ +#define NG_RPL_MSG_TYPE_TRICKLE_INTERVAL (0x0901) + +/** + * @brief Message type for trickle callbacks + */ +#define NG_RPL_MSG_TYPE_TRICKLE_CALLBACK (0x0902) + +/** + * @brief Message type for handling DAO sending + */ +#define NG_RPL_MSG_TYPE_DAO_HANDLE (0x0903) + +/** + * @brief Message type for handling DODAG cleanup + */ +#define NG_RPL_MSG_TYPE_CLEANUP_HANDLE (0x0904) + +/** + * @brief Infinite rank + * @see + * RFC 6550, section 17 + * + */ +#define NG_RPL_INFINITE_RANK (0xFFFF) + +/** + * @brief Default minimum hop rank increase + * @see + * RFC 6550, section 17 + * + */ +#define NG_RPL_DEFAULT_MIN_HOP_RANK_INCREASE (256) + +/** + * @brief Maximum rank increase + */ +#define NG_RPL_DEFAULT_MAX_RANK_INCREASE (0) + +/** + * @brief Number of implemented Objective Functions + */ +#define NG_RPL_IMPLEMENTED_OFS_NUMOF (1) + +/** + * @brief Default Objective Code Point (OF0) + */ +#define NG_RPL_DEFAULT_OCP (0) + +/** + * @name RPL Mode of Operations + * @{ + */ +#define NG_RPL_MOP_NO_DOWNWARD_ROUTES (0x00) +#define NG_RPL_MOP_NON_STORING_MODE (0x01) +#define NG_RPL_MOP_STORING_MODE_NO_MC (0x02) +#define NG_RPL_MOP_STORING_MODE_MC (0x03) +/** default MOP set on compile time */ +#ifndef NG_RPL_DEFAULT_MOP +# define NG_RPL_DEFAULT_MOP NG_RPL_MOP_STORING_MODE_NO_MC +#endif +/** @} */ + +/** + * @name Sequence counter handling + * @see + * Sequence Counter Operation + * + * @{ + */ +#define NG_RPL_COUNTER_MAX (255) +#define NG_RPL_COUNTER_LOWER_REGION (127) +#define NG_RPL_COUNTER_SEQ_WINDOW (16) +#define NG_RPL_COUNTER_INIT (NG_RPL_COUNTER_MAX - NG_RPL_COUNTER_SEQ_WINDOW + 1) + +static inline uint8_t NG_RPL_COUNTER_INCREMENT(uint8_t counter) +{ + return ((counter > NG_RPL_COUNTER_LOWER_REGION) ? + ((counter == NG_RPL_COUNTER_MAX) ? counter = 0 : ++counter) : + ((counter == NG_RPL_COUNTER_LOWER_REGION) ? counter = 0 : ++counter)); +} + +static inline bool NG_RPL_COUNTER_IS_INIT(uint8_t counter) +{ + return (counter > NG_RPL_COUNTER_LOWER_REGION); +} + +static inline bool NG_RPL_COUNTER_GREATER_THAN_LOCAL(uint8_t A, uint8_t B) +{ + return (((A < B) && (NG_RPL_COUNTER_LOWER_REGION + 1 - B + A < NG_RPL_COUNTER_SEQ_WINDOW)) + || ((A > B) && (A - B < NG_RPL_COUNTER_SEQ_WINDOW))); +} + +static inline bool NG_RPL_COUNTER_GREATER_THAN(uint8_t A, uint8_t B) +{ + return ((A > NG_RPL_COUNTER_LOWER_REGION) ? ((B > NG_RPL_COUNTER_LOWER_REGION) ? + NG_RPL_COUNTER_GREATER_THAN_LOCAL(A, B) : 0) : + ((B > NG_RPL_COUNTER_LOWER_REGION) ? 1 : NG_RPL_COUNTER_GREATER_THAN_LOCAL(A, B))); +} +/** @} */ + +/** + * @name Trickle parameters + * @see + * Trickle Parameters + * + * @{ + */ +#define NG_RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS (20) +#define NG_RPL_DEFAULT_DIO_INTERVAL_MIN (3) +#define NG_RPL_DEFAULT_DIO_REDUNDANCY_CONSTANT (10) +/** @} */ + +/** + * @name Default parent and route entry lifetime + * default lifetime will be multiplied by the lifetime unit to obtain the resulting lifetime + * @{ + */ +#define NG_RPL_DEFAULT_LIFETIME (60) +#define NG_RPL_LIFETIME_UNIT (2) +/** @} */ + +/** + * @brief Interval of the void _update_lifetime() function + */ +#define NG_RPL_LIFETIME_STEP (2) + +/** + * @brief A DODAG can be grounded or floating + * @see + * Grounded and Floating DODAGs + * + */ +#define NG_RPL_GROUNDED (1) + +/** + * @name Parameters used for DAO handling + * @{ + */ +#define NG_RPL_DAO_SEND_RETRIES (4) +#define NG_RPL_DEFAULT_WAIT_FOR_DAO_ACK (3) +#define NG_RPL_REGULAR_DAO_INTERVAL (60) +#define NG_RPL_DEFAULT_DAO_DELAY (5) +/** @} */ + +/** + * @brief Cleanup timeout in seconds + */ +#define NG_RPL_CLEANUP_TIME (30) + +/** + * @name Node Status + * @{ + */ +#define NG_RPL_NORMAL_NODE (0) +#define NG_RPL_ROOT_NODE (1) +#define NG_RPL_LEAF_NODE (2) +/** @} */ + + +/** + * @name RPL Control Message Options + * @see + * RPL Control Message Options + * + * @{ + */ +#define NG_RPL_OPT_PAD1 (0) +#define NG_RPL_OPT_PADN (1) +#define NG_RPL_OPT_DAG_METRIC_CONTAINER (2) +#define NG_RPL_OPT_ROUTE_INFO (3) +#define NG_RPL_OPT_DODAG_CONF (4) +#define NG_RPL_OPT_TARGET (5) +#define NG_RPL_OPT_TRANSIT (6) +#define NG_RPL_OPT_SOLICITED_INFO (7) +#define NG_RPL_OPT_PREFIX_INFO (8) +#define NG_RPL_OPT_TARGET_DESC (9) +/** @} */ + +/** + * @brief Rank of the root node + */ +#define NG_RPL_ROOT_RANK (256) + +/** + * @brief DIS ICMPv6 code + * @see + * RFC 6550, section 6 + * + */ +#define NG_RPL_ICMPV6_CODE_DIS (0x00) + +/** + * @brief DIO ICMPv6 code + * @see + * RFC 6550, section 6 + * + */ +#define NG_RPL_ICMPV6_CODE_DIO (0x01) + +/** + * @brief DAO ICMPv6 code + * @see + * RFC 6550, section 6 + * + */ +#define NG_RPL_ICMPV6_CODE_DAO (0x02) + +/** + * @brief DAO ACK ICMPv6 code + * @see + * RFC 6550, section 6 + * + */ +#define NG_RPL_ICMPV6_CODE_DAO_ACK (0x03) + +/** + * @brief Update interval of the lifetime update function + */ +#define NG_RPL_LIFETIME_UPDATE_STEP (2) + +/** + * @brief PID of the RPL thread. + */ +extern kernel_pid_t ng_rpl_pid; + +/** + * @brief Initialization of the RPL thread. + * + * @param[in] if_pid PID of the interface + * + * @return The PID of the RPL thread, on success. + * @return a negative errno on error. + */ +kernel_pid_t ng_rpl_init(kernel_pid_t if_pid); + +/** + * @brief Initialization of a RPL DODAG as root node. Creates a new instance if necessary. + * + * @param[in] instance_id Id of the instance + * @param[in] dodag_id Id of the DODAG + * + * @return Pointer to the new DODAG, on success. + * @return NULL, otherwise. + */ +ng_rpl_dodag_t *ng_rpl_root_init(uint8_t instance_id, ipv6_addr_t *dodag_id); + +/** + * @brief Send a DIO of the @p dodag to the @p destination. + * + * @param[in] dodag Pointer to the DODAG. + * @param[in] destination IPv6 addres of the destination. + */ +void ng_rpl_send_DIO(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination); + +/** + * @brief Send a DIS of the @p dodag to the @p destination. + * + * @param[in] dodag Pointer to the DODAG, optional. + * @param[in] destination IPv6 addres of the destination. + */ +void ng_rpl_send_DIS(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination); + +/** + * @brief Send a DAO of the @p dodag to the @p destination. + * + * @param[in] dodag Pointer to the DODAG. + * @param[in] destination IPv6 addres of the destination. + * @param[in] lifetime Lifetime of the route to announce. + */ +void ng_rpl_send_DAO(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination, uint8_t lifetime); + +/** + * @brief Send a DAO-ACK of the @p dodag to the @p destination. + * + * @param[in] dodag Pointer to the DODAG, optional. + * @param[in] destination IPv6 addres of the destination. + * @param[in] seq Sequence number to be acknowledged. + */ +void ng_rpl_send_DAO_ACK(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination, uint8_t seq); + +/** + * @brief Parse a DIS. + * + * @param[in] dis Pointer to the DIS message. + * @param[in] src Pointer to the source address of the IPv6 packet. + * @param[in] dst Pointer to the destination address of the IPv6 packet. + * @param[in] len Length of the IPv6 packet. + */ +void ng_rpl_recv_DIS(ng_rpl_dis_t *dis, ipv6_addr_t *src, ipv6_addr_t *dst, uint16_t len); + +/** + * @brief Parse a DIO. + * + * @param[in] dio Pointer to the DIO message. + * @param[in] src Pointer to the source address of the IPv6 packet. + * @param[in] len Length of the IPv6 packet. + */ +void ng_rpl_recv_DIO(ng_rpl_dio_t *dio, ipv6_addr_t *src, uint16_t len); + +/** + * @brief Parse a DAO. + * + * @param[in] dao Pointer to the DAO message. + * @param[in] src Pointer to the source address of the IPv6 packet. + * @param[in] len Length of the IPv6 packet. + */ +void ng_rpl_recv_DAO(ng_rpl_dao_t *dao, ipv6_addr_t *src, uint16_t len); + +/** + * @brief Parse a DAO-ACK. + * + * @param[in] dao_ack Pointer to the DAO-ACK message. + */ +void ng_rpl_recv_DAO_ACK(ng_rpl_dao_ack_t *dao_ack); + +/** + * @brief Delay the DAO sending interval + * + * @param[in] dodag The DODAG of the DAO + */ +void ng_rpl_delay_dao(ng_rpl_dodag_t *dodag); + +/** + * @brief Long delay the DAO sending interval + * + * @param[in] dodag The DODAG of the DAO + */ +void ng_rpl_long_delay_dao(ng_rpl_dodag_t *dodag); #ifdef __cplusplus } #endif #endif /* NG_RPL_H_ */ -/** @} */ +/** + * @} + */ diff --git a/sys/include/net/ng_rpl/dodag.h b/sys/include/net/ng_rpl/dodag.h new file mode 100644 index 000000000000..43eeb67d8a54 --- /dev/null +++ b/sys/include/net/ng_rpl/dodag.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2013 INRIA. + * Copyright (C) 2015 Cenk Gündoğan + * + * 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 net_ng_rpl + * @{ + * + * @file + * @brief DODAG-related functions for RPL + * + * Header file, which defines all public known DODAG-related functions for RPL. + * + * @author Eric Engel + * @author Cenk Gündoğan + */ + +#ifndef NG_RPL_DODAG_H_ +#define NG_RPL_DODAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/ng_ipv6.h" +#include "trickle.h" +#include "net/ng_rpl.h" +#include "net/ng_rpl/structs.h" + +/** + * @brief Number of RPL instances + */ +#ifndef NG_RPL_INSTANCES_NUMOF +#define NG_RPL_INSTANCES_NUMOF (2) +#endif + +/** + * @brief Number of RPL dodags + */ +#ifndef NG_RPL_DODAGS_NUMOF +#define NG_RPL_DODAGS_NUMOF (4) +#endif + +/** + * @brief Number of RPL parents + */ +#ifndef NG_RPL_PARENTS_NUMOF +#define NG_RPL_PARENTS_NUMOF (6) +#endif + +/** + * @brief RPL instance table + */ +extern ng_rpl_instance_t ng_rpl_instances[NG_RPL_INSTANCES_NUMOF]; + +/** + * @brief RPL DODAG table + */ +extern ng_rpl_dodag_t ng_rpl_dodags[NG_RPL_DODAGS_NUMOF]; + +/** + * @brief RPL parent table + */ +extern ng_rpl_parent_t ng_rpl_parents[NG_RPL_PARENTS_NUMOF]; + +/** + * @brief Add a new RPL instance with the id @p instance_id. + * + * @param[in] instance_id The instance id of the new RPL instance. + * @param[out] inst Pointer to an existing or new instance. Otherwise NULL. + * + * @return true, if instance could be created. + * @return false, if instance could not be created or exists already. + */ +bool ng_rpl_instance_add(uint8_t instance_id, ng_rpl_instance_t **inst); + +/** + * @brief Remove a RPL instance with the id @p instance_id. + * + * @param[in] instance_id The instance id of the RPL instance to remove. + * + * @return true, on success. + * @return false, otherwise. + */ +bool ng_rpl_instance_remove_by_id(uint8_t instance_id); + +/** + * @brief Remove a RPL instance with the pointer @p inst. + * + * @param[in] inst Pointer to the the RPL instance to remove. + * + * @return true, on success. + * @return false, otherwise. + */ +bool ng_rpl_instance_remove(ng_rpl_instance_t *inst); + +/** + * @brief Get the RPL instance with the id @p instance_id. + * + * @param[in] instance_id The instance id of the RPL instance to get. + * + * @return Pointer to the RPL instance, on success. + * @return NULL, otherwise. + */ +ng_rpl_instance_t *ng_rpl_instance_get(uint8_t instance_id); + +/** + * @brief Add a new RPL DODAG with the id @p dodag_id to the instance @p instance. + * + * @param[in] instance Pointer to the instance to add the DODAG to + * @param[in] dodag_id The DODAG-ID of the new DODAG + * @param[out] dodag Pointer to an existing or new DODAG. Otherwise NULL. + * + * @return true, if DODAG could be created. + * @return false, if DODAG could not be created or exists already. + */ +bool ng_rpl_dodag_add(ng_rpl_instance_t *instance, ipv6_addr_t *dodag_id, ng_rpl_dodag_t **dodag); + +/** + * @brief Remove the RPL DODAG @p dodag. + * + * @param[in] dodag Pointer to the dodag. + * + * @return true, on success. + * @return false, otherwise. + */ +bool ng_rpl_dodag_remove(ng_rpl_dodag_t *dodag); + +/** + * @brief Remove all parents from the @p dodag. + * + * @param[in] dodag Pointer to the dodag. + */ +void ng_rpl_dodag_remove_all_parents(ng_rpl_dodag_t *dodag); + +/** + * @brief Get the RPL DODAG with the id @p dodag_id to the instance @p instance. + * + * @param[in] instance Pointer to the instance of the RPL DODAG + * @param[in] dodag_id The DODAG-ID of the RPL DODAG + * + * @return Pointer to the existing RPL DODAG, on success. + * @return NULL, otherwise. + */ +ng_rpl_dodag_t *ng_rpl_dodag_get(ng_rpl_instance_t *instance, ipv6_addr_t *dodag_id); + +/** + * @brief Add a new parent with the IPv6 address @p addr to the @p dodag. + * + * @param[in] dodag Pointer to the DODAG + * @param[in] addr IPV6 address of the parent + * @param[out] parent Pointer to an existing or new parent. Otherwise NULL. + * + * @return true. if parent could be created. + * @return false, if parent could not be created or exists already. + */ +bool ng_rpl_parent_add_by_addr(ng_rpl_dodag_t *dodag, ipv6_addr_t *addr, ng_rpl_parent_t **parent); + +/** + * @brief Remove the @p parent from its DODAG. + * + * @param[in] parent Pointer to the parent. + * + * @return true, on success. + * @return false, otherwise. + */ +bool ng_rpl_parent_remove(ng_rpl_parent_t *parent); + +/** + * @brief Get a parent with the IPv6 addr @p addr of the DODAG @p dodag. + * + * @param[in] dodag Pointer to the DODAG + * @param[in] addr IPv6 address of the parent + * + * @return Pointer to the existing parent, on success. + * @return NULL, otherwise. + */ +ng_rpl_parent_t *ng_rpl_parent_get(ng_rpl_dodag_t *dodag, ipv6_addr_t *addr); + +/** + * @brief Update a @p parent of the @p dodag. + * + * @param[in] dodag Pointer to the DODAG + * @param[in] parent Pointer to the parent + */ +void ng_rpl_parent_update(ng_rpl_dodag_t *dodag, ng_rpl_parent_t *parent); + +/** + * @brief Find the parent with the lowest rank and update the DODAG's preferred parent + * + * @param[in] dodag Pointer to the DODAG + * + * @return Pointer to the preferred parent, on success. + * @return NULL, otherwise. + */ +ng_rpl_parent_t *ng_rpl_find_preferred_parent(ng_rpl_dodag_t *dodag); + +/** + * @brief Start a local repair. + * + * @param[in] dodag Pointer to the DODAG + */ +void ng_rpl_local_repair(ng_rpl_dodag_t *dodag); +#ifdef __cplusplus +} +#endif + +#endif /* NG_RPL_DODAG_H_ */ +/** + * @} + */ diff --git a/sys/include/net/ng_rpl/of_manager.h b/sys/include/net/ng_rpl/of_manager.h new file mode 100644 index 000000000000..9a24db01cb43 --- /dev/null +++ b/sys/include/net/ng_rpl/of_manager.h @@ -0,0 +1,46 @@ +/* + * 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 net_ng_rpl + * @{ + * + * @file + * @brief RPL Objective functions manager header + * + * @author Fabian Brandt + */ + +#ifndef RPL_OFM_H +#define RPL_OFM_H + +#include "structs.h" +#include "net/ng_ipv6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialization of Manager and of-functions. +*/ +void ng_rpl_of_manager_init(void); + +/** + * @brief Returns objective function with a given cope point + * @param[in] ocp Objective code point of objective function + * @return Pointer of corresponding objective function implementation +*/ +ng_rpl_of_t *ng_rpl_get_of_for_ocp(uint16_t ocp); + +#ifdef __cplusplus +} +#endif + +#endif /* RPL_OFM_H */ +/** @} */ diff --git a/sys/include/net/ng_rpl/structs.h b/sys/include/net/ng_rpl/structs.h new file mode 100644 index 000000000000..4e601c335990 --- /dev/null +++ b/sys/include/net/ng_rpl/structs.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2013 INRIA. + * Copyright (C) 2015 Cenk Gündoğan + * + * 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 net_ng_rpl + * @{ + * + * @file + * @brief RPL data structs + * + * Header file, which defines all structs used by RPL. + * + * @author Eric Engel + * @author Cenk Gündoğan + */ + +#ifndef NG_RPL_STRUCTS_H_ +#define NG_RPL_STRUCTS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/ng_ipv6.h" +#include "trickle.h" + +/** + * @brief RPL-Option Generic Format + * @see + * RPL Control Message Option Generic Format + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< Option Type */ + uint8_t length; /**< Option Length, does not include the first two byte */ +} ng_rpl_opt_t; + +/** + * @brief DIO Base Object + * @see + * Format of the DIO Base Object + * + */ +typedef struct __attribute__((packed)) { + uint8_t instance_id; /**< id of the instance */ + uint8_t version_number; /**< version number of the DODAG */ + network_uint16_t rank; /**< rank of the parent emitting the DIO */ + uint8_t g_mop_prf; /**< grounded, MOP, preferred flags */ + uint8_t dtsn; /**< Destination Advertisement Trigger Sequence Number */ + uint8_t flags; /**< unused */ + uint8_t reserved; /**< reserved */ + ipv6_addr_t dodag_id; /**< id of the dodag */ +} ng_rpl_dio_t; + +/** + * @brief DODAG Configuration Option + * @see + * DODAG Configuration + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< Option Type: 0x04 */ + uint8_t length; /**< length of option, not including first two bytes */ + uint8_t flags_a_pcs; /**< flags */ + uint8_t dio_int_doubl; /**< trickle Imax parameter */ + uint8_t dio_int_min; /**< trickle Imin parameter */ + uint8_t dio_redun; /**< trickle k parameter */ + network_uint16_t max_rank_inc; /**< allowable increase in rank */ + network_uint16_t min_hop_rank_inc; /**< DAGRank(rank) = floor(rank/MinHopRankIncrease) */ + network_uint16_t ocp; /**< Objective Code Point */ + uint8_t reserved; /**< reserved */ + uint8_t default_lifetime; /**< lifetime of RPL routes (lifetime * lifetime_unit) */ + network_uint16_t lifetime_unit; /**< unit in seconds */ +} ng_rpl_opt_dodag_conf_t; + +/** + * @brief DODAG Information Solicitation + * @see + * DODAG Information Solicitation + * + */ +typedef struct __attribute__((packed)) { + uint8_t flags; /**< unused */ + uint8_t reserved; /**< reserved */ +} ng_rpl_dis_t; + +/** + * @brief Destination Advertisement Object + * @see + * Destination Advertisement Object + * + */ +typedef struct __attribute__((packed)) { + uint8_t instance_id; /**< id of the instance */ + uint8_t k_d_flags; /**< K and D flags */ + uint8_t reserved; /**< reserved */ + uint8_t dao_sequence; /**< sequence of the DAO, needs to be used for DAO-ACK */ + ipv6_addr_t dodag_id; /**< id of the DODAG */ +} ng_rpl_dao_t; + +/** + * @brief Destination Advertisement Object Acknowledgement + * @see + * Destination Advertisement Object Acknowledgement + * + */ +typedef struct __attribute__((packed)) { + uint8_t instance_id; /**< id of the instance */ + uint8_t d_reserved; /**< if set, indicates that the DODAG id should be included */ + uint8_t dao_sequence; /**< sequence must be equal to the sequence from the DAO object */ + uint8_t status; /**< indicates completion */ + ipv6_addr_t dodag_id; /**< id of the DODAG */ +} ng_rpl_dao_ack_t; + +/** + * @brief Target Option + * @see + * RPL Target + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< option type */ + uint8_t length; /**< option length without the first two bytes */ + uint8_t flags; /**< unused */ + uint8_t prefix_length; /**< number of valid leading bits in the IPv6 prefix */ + ipv6_addr_t target; /**< IPv6 prefix, address or multicast group */ +} ng_rpl_opt_target_t; + +/** + * @brief Transit Option + * @see + * Transit Information + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< option type */ + uint8_t length; /**< option length without the first two bytes */ + uint8_t e_flags; /**< external flag indicates external routes */ + uint8_t path_control; /**< limits the number of DAO parents */ + uint8_t path_sequence; /**< increased value for route updates */ + uint8_t path_lifetime; /**< lifetime of routes */ +} ng_rpl_opt_transit_t; + +typedef struct ng_rpl_dodag ng_rpl_dodag_t; +typedef struct ng_rpl_parent ng_rpl_parent_t; + +/** + * @brief Parent representation + */ +struct ng_rpl_parent { + ng_rpl_parent_t *next; /**< pointer to the next parent */ + uint8_t state; /**< 0 for unsued, 1 for used */ + ipv6_addr_t addr; /**< link-local IPv6 address of this parent */ + uint16_t rank; /**< rank of the parent */ + uint8_t dtsn; /**< last seen dtsn of this parent */ + ng_rpl_dodag_t *dodag; /**< DODAG the parent belongs to */ + timex_t lifetime; /**< lifetime of this parent */ + double link_metric; /**< metric of the link */ + uint8_t link_metric_type; /**< type of the metric */ +}; + +/** + * @brief Objective function representation + */ +typedef struct { + uint16_t ocp; /**< objective code point */ + uint16_t (*calc_rank)(ng_rpl_parent_t *parent, uint16_t base_rank); /**< calculate the rank */ + ng_rpl_parent_t *(*which_parent)(ng_rpl_parent_t *, ng_rpl_parent_t *); /**< compare for parents */ + ng_rpl_dodag_t *(*which_dodag)(ng_rpl_dodag_t *, ng_rpl_dodag_t *); /**< compare for dodags */ + void (*reset)(ng_rpl_dodag_t *); /**< resets the OF */ + void (*parent_state_callback)(ng_rpl_parent_t *, int, int); /**< retrieves the state of a parent*/ + void (*init)(void); /**< OF specific init function */ + void (*process_dio)(void); /**< DIO processing callback (acc. to OF0 spec, chpt 5) */ +} ng_rpl_of_t; + + +/** + * @brief Instance representation + */ +typedef struct { + uint8_t id; /**< id of the instance */ + uint8_t state; /**< 0 for unused, 1 for used */ + ng_rpl_dodag_t *dodags; /**< pointer to the DODAG list of this instance */ + uint8_t mop; /**< configured Mode of Operation */ + ng_rpl_of_t *of; /**< configured Objective Function */ + uint16_t min_hop_rank_inc; /**< minimum hop rank increase */ + uint16_t max_rank_inc; /**< max increase in the rank */ +} ng_rpl_instance_t; + +/** + * @brief DODAG representation + */ +struct ng_rpl_dodag { + ng_rpl_instance_t *instance; /**< id of the instance */ + ng_rpl_dodag_t *next; /**< pointer to the next dodag */ + ng_rpl_parent_t *parents; /**< pointer to the parents list of this DODAG */ + ipv6_addr_t dodag_id; /**< id of the DODAG */ + uint8_t state; /**< 0 for unused, 1 for used */ + uint8_t dtsn; /**< DAO Trigger Sequence Number */ + uint8_t prf; /**< preferred flag */ + uint8_t dio_interval_doubl; /**< trickle Imax parameter */ + uint8_t dio_min; /**< trickle Imin parameter */ + uint8_t dio_redun; /**< trickle k parameter */ + uint8_t default_lifetime; /**< lifetime of routes (lifetime * unit) */ + uint16_t lifetime_unit; /**< unit in seconds of the lifetime */ + uint8_t version; /**< version of this DODAG */ + uint8_t grounded; /**< grounded flag */ + uint16_t my_rank; /**< rank/position in the DODAG */ + uint8_t node_status; /**< leaf, normal, or root node */ + uint8_t dao_seq; /**< dao sequence number */ + uint8_t dao_counter; /**< amount of retried DAOs */ + bool dao_ack_received; /**< flag to check for DAO-ACK */ + uint8_t dodag_conf_counter; /**< limitation of the sending of DODAG_CONF options */ + timex_t dao_time; /**< time to schedule the next DAO */ + vtimer_t dao_timer; /**< timer to schedule the next DAO */ + timex_t cleanup_time; /**< time to schedula a DODAG cleanup */ + vtimer_t cleanup_timer; /**< timer to schedula a DODAG cleanup */ + trickle_t trickle; /**< trickle representation */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* NG_RPL_STRUCTS_H_ */ +/** + * @} + */ diff --git a/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c b/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c index 3c811976da7a..bfb372fa5d89 100644 --- a/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c +++ b/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c @@ -114,13 +114,6 @@ void ng_icmpv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt) /* TODO */ break; -#ifdef MODULE_NG_RPL - case NG_ICMPV6_RPL_CTRL: - DEBUG("icmpv6: RPL control message received\n"); - /* TODO */ - break; -#endif - default: DEBUG("icmpv6: unknown type field %" PRIu8 "\n", hdr->type); break; diff --git a/sys/net/routing/ng_rpl/Makefile b/sys/net/routing/ng_rpl/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/net/routing/ng_rpl/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/routing/ng_rpl/ng_rpl.c b/sys/net/routing/ng_rpl/ng_rpl.c new file mode 100644 index 000000000000..2f8e1c798b7b --- /dev/null +++ b/sys/net/routing/ng_rpl/ng_rpl.c @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2015 Cenk Gündoğan + * + * 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 + * + * @author Cenk Gündoğan + */ +#include "net/ng_rpl.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG && defined(MODULE_IPV6_ADDR) +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +static char _stack[NG_RPL_STACK_SIZE]; +kernel_pid_t ng_rpl_pid = KERNEL_PID_UNDEF; +static timex_t _lt_time; +static vtimer_t _lt_timer; +static msg_t _msg_q[NG_RPL_MSG_QUEUE_SIZE]; +static ng_netreg_entry_t _me_reg; + +ng_rpl_instance_t ng_rpl_instances[NG_RPL_INSTANCES_NUMOF]; +ng_rpl_dodag_t ng_rpl_dodags[NG_RPL_DODAGS_NUMOF]; +ng_rpl_parent_t ng_rpl_parents[NG_RPL_PARENTS_NUMOF]; + +static void _update_lifetime(void); +static void _dao_handle_send(ng_rpl_dodag_t *dodag); +static void _receive(ng_pktsnip_t *pkt); +static void *_event_loop(void *args); +static ng_rpl_dodag_t *_root_dodag_init(uint8_t instance_id, ipv6_addr_t *dodag_id, uint8_t mop); + +kernel_pid_t ng_rpl_init(kernel_pid_t if_pid) +{ + /* check if RPL was initialized before */ + if (ng_rpl_pid == KERNEL_PID_UNDEF) { + /* start the event loop */ + ng_rpl_pid = thread_create(_stack, sizeof(_stack), NG_RPL_PRIO, CREATE_STACKTEST, + _event_loop, NULL, "RPL"); + + if (ng_rpl_pid == KERNEL_PID_UNDEF) { + DEBUG("RPL: could not start the event loop\n"); + return KERNEL_PID_UNDEF; + } + + _me_reg.demux_ctx = NG_ICMPV6_RPL_CTRL; + _me_reg.pid = ng_rpl_pid; + /* register interest in all ICMPv6 packets */ + ng_netreg_register(NG_NETTYPE_ICMPV6, &_me_reg); + + ng_rpl_of_manager_init(); + _lt_time = timex_set(NG_RPL_LIFETIME_UPDATE_STEP, 0); + vtimer_set_msg(&_lt_timer, _lt_time, ng_rpl_pid, NG_RPL_MSG_TYPE_LIFETIME_UPDATE, NULL); + } + + /* register all_RPL_nodes multicast address */ + ipv6_addr_t all_RPL_nodes = NG_IPV6_ADDR_ALL_RPL_NODES; + ng_ipv6_netif_add_addr(if_pid, &all_RPL_nodes, IPV6_ADDR_BIT_LEN, 0); + + ng_rpl_send_DIS(NULL, &all_RPL_nodes); + return ng_rpl_pid; +} + +ng_rpl_dodag_t *ng_rpl_root_init(uint8_t instance_id, ipv6_addr_t *dodag_id) +{ + ng_rpl_dodag_t *dodag = _root_dodag_init(instance_id, dodag_id, NG_RPL_DEFAULT_MOP); + + if (!dodag) { + return NULL; + } + + dodag->dtsn = 1; + dodag->prf = 0; + dodag->dio_interval_doubl = NG_RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS; + dodag->dio_min = NG_RPL_DEFAULT_DIO_INTERVAL_MIN; + dodag->dio_redun = NG_RPL_DEFAULT_DIO_REDUNDANCY_CONSTANT; + dodag->default_lifetime = NG_RPL_DEFAULT_LIFETIME; + dodag->lifetime_unit = NG_RPL_LIFETIME_UNIT; + dodag->version = NG_RPL_COUNTER_INIT; + dodag->grounded = NG_RPL_GROUNDED; + dodag->node_status = NG_RPL_ROOT_NODE; + dodag->my_rank = NG_RPL_ROOT_RANK; + + trickle_start(ng_rpl_pid, &dodag->trickle, NG_RPL_MSG_TYPE_TRICKLE_INTERVAL, + NG_RPL_MSG_TYPE_TRICKLE_CALLBACK, (1 << dodag->dio_min), + dodag->dio_interval_doubl, dodag->dio_redun); + + return dodag; +} + +static ng_rpl_dodag_t *_root_dodag_init(uint8_t instance_id, ipv6_addr_t *dodag_id, uint8_t mop) +{ + if (ng_rpl_pid == KERNEL_PID_UNDEF) { + DEBUG("RPL: RPL thread not started\n"); + return NULL; + } + + ipv6_addr_t *configured_addr; + ng_rpl_instance_t *inst = NULL; + ng_rpl_dodag_t *dodag = NULL; + + if (instance_id == 0) { + DEBUG("RPL: instance id (%d) must be a positive number greater than zero\n", instance_id); + return NULL; + } + + if (ng_ipv6_netif_find_by_addr(&configured_addr, dodag_id) == KERNEL_PID_UNDEF) { + DEBUG("RPL: no IPv6 address configured to match the given dodag id: %s\n", + ipv6_addr_to_str(addr_str, dodag_id, sizeof(addr_str))); + return NULL; + } + + if (ng_rpl_instance_add(instance_id, &inst)) { + inst->of = (ng_rpl_of_t *) ng_rpl_get_of_for_ocp(NG_RPL_DEFAULT_OCP); + inst->mop = mop; + inst->min_hop_rank_inc = NG_RPL_DEFAULT_MIN_HOP_RANK_INCREASE; + inst->max_rank_inc = NG_RPL_DEFAULT_MAX_RANK_INCREASE; + } + else if (inst == NULL) { + DEBUG("RPL: could not allocate memory for a new instance with id %d", instance_id); + return NULL; + } + else if (inst->mop != mop) { + DEBUG("RPL: instance (%d) exists with another MOP", instance_id); + return NULL; + } + + if (!ng_rpl_dodag_add(inst, dodag_id, &dodag)) { + DEBUG("RPL: DODAG with id %s exists or no memory left for a new DODAG", + ipv6_addr_to_str(addr_str, dodag_id, sizeof(addr_str))); + return NULL; + } + + return dodag; +} + +static void _receive(ng_pktsnip_t *icmpv6) +{ + ng_pktsnip_t *ipv6 = NULL; + ipv6_hdr_t *ipv6_hdr = NULL; + ng_icmpv6_hdr_t *icmpv6_hdr = NULL; + + LL_SEARCH_SCALAR(icmpv6, ipv6, type, NG_NETTYPE_IPV6); + ipv6_hdr = (ipv6_hdr_t *)ipv6->data; + + icmpv6_hdr = (ng_icmpv6_hdr_t *)icmpv6->data; + switch (icmpv6_hdr->code) { + case NG_RPL_ICMPV6_CODE_DIS: + DEBUG("RPL: DIS received\n"); + ng_rpl_recv_DIS((ng_rpl_dis_t *)(icmpv6_hdr + 1), &ipv6_hdr->src, &ipv6_hdr->dst, + byteorder_ntohs(ipv6_hdr->len)); + break; + case NG_RPL_ICMPV6_CODE_DIO: + DEBUG("RPL: DIO received\n"); + ng_rpl_recv_DIO((ng_rpl_dio_t *)(icmpv6_hdr + 1), &ipv6_hdr->src, + byteorder_ntohs(ipv6_hdr->len)); + break; + case NG_RPL_ICMPV6_CODE_DAO: + DEBUG("RPL: DAO received\n"); + ng_rpl_recv_DAO((ng_rpl_dao_t *)(icmpv6_hdr + 1), &ipv6_hdr->src, + byteorder_ntohs(ipv6_hdr->len)); + break; + case NG_RPL_ICMPV6_CODE_DAO_ACK: + DEBUG("RPL: DAO-ACK received\n"); + ng_rpl_recv_DAO_ACK((ng_rpl_dao_ack_t *)(icmpv6_hdr + 1)); + break; + default: + DEBUG("RPL: Unknown ICMPV6 code received\n"); + break; + } + + ng_pktbuf_release(icmpv6); +} + +static void *_event_loop(void *args) +{ + msg_t msg, reply; + + (void)args; + msg_init_queue(_msg_q, NG_RPL_MSG_QUEUE_SIZE); + + /* preinitialize ACK */ + reply.type = NG_NETAPI_MSG_TYPE_ACK; + + trickle_t *trickle; + ng_rpl_dodag_t *dodag; + /* start event loop */ + while (1) { + DEBUG("RPL: waiting for incoming message.\n"); + msg_receive(&msg); + + switch (msg.type) { + case NG_RPL_MSG_TYPE_LIFETIME_UPDATE: + DEBUG("RPL: NG_RPL_MSG_TYPE_LIFETIME_UPDATE received\n"); + _update_lifetime(); + break; + case NG_RPL_MSG_TYPE_TRICKLE_INTERVAL: + DEBUG("RPL: NG_RPL_MSG_TYPE_TRICKLE_INTERVAL received\n"); + trickle = (trickle_t *) msg.content.ptr; + if (trickle && (trickle->callback.func != NULL)) { + trickle_interval(trickle); + } + break; + case NG_RPL_MSG_TYPE_TRICKLE_CALLBACK: + DEBUG("RPL: NG_RPL_MSG_TYPE_TRICKLE_CALLBACK received\n"); + trickle = (trickle_t *) msg.content.ptr; + if (trickle && (trickle->callback.func != NULL)) { + trickle_callback(trickle); + } + break; + case NG_RPL_MSG_TYPE_DAO_HANDLE: + DEBUG("RPL: NG_RPL_MSG_TYPE_DAO_HANDLE received\n"); + dodag = (ng_rpl_dodag_t *) msg.content.ptr; + if (dodag && (dodag->state != 0)) { + _dao_handle_send(dodag); + } + break; + case NG_RPL_MSG_TYPE_CLEANUP_HANDLE: + DEBUG("RPL: NG_RPL_MSG_TYPE_CLEANUP received\n"); + dodag = (ng_rpl_dodag_t *) msg.content.ptr; + if (dodag && (dodag->state != 0) && (dodag->parents == NULL)) { + /* no parents - delete this DODAG */ + ng_rpl_dodag_remove(dodag); + } + break; + case NG_NETAPI_MSG_TYPE_RCV: + DEBUG("RPL: NG_NETAPI_MSG_TYPE_RCV received\n"); + _receive((ng_pktsnip_t *)msg.content.ptr); + break; + case NG_NETAPI_MSG_TYPE_SND: + case NG_NETAPI_MSG_TYPE_GET: + case NG_NETAPI_MSG_TYPE_SET: + DEBUG("RPL: reply to unsupported recv/get/set\n"); + reply.content.value = -ENOTSUP; + msg_reply(&msg, &reply); + break; + default: + break; + } + } + + return NULL; +} + +void _update_lifetime(void) +{ + timex_t now; + vtimer_now(&now); + ng_rpl_parent_t *parent; + for (uint8_t i = 0; i < NG_RPL_PARENTS_NUMOF; ++i) { + parent = &ng_rpl_parents[i]; + if (parent->state != 0) { + if ((signed)(parent->lifetime.seconds - now.seconds) <= NG_RPL_LIFETIME_UPDATE_STEP) { + ng_rpl_dodag_t *dodag = parent->dodag; + ng_rpl_parent_remove(parent); + ng_rpl_parent_update(dodag, NULL); + continue; + } + else if (((signed)(parent->lifetime.seconds - now.seconds) <= + NG_RPL_LIFETIME_UPDATE_STEP * 2)) { + ng_rpl_send_DIS(parent->dodag, &parent->addr); + } + } + } + vtimer_remove(&_lt_timer); + vtimer_set_msg(&_lt_timer, _lt_time, ng_rpl_pid, NG_RPL_MSG_TYPE_LIFETIME_UPDATE, NULL); +} + +void ng_rpl_delay_dao(ng_rpl_dodag_t *dodag) +{ + dodag->dao_time = timex_set(NG_RPL_DEFAULT_DAO_DELAY, 0); + dodag->dao_counter = 0; + dodag->dao_ack_received = false; + vtimer_remove(&dodag->dao_timer); + vtimer_set_msg(&dodag->dao_timer, dodag->dao_time, ng_rpl_pid, NG_RPL_MSG_TYPE_DAO_HANDLE, dodag); +} + +void ng_rpl_long_delay_dao(ng_rpl_dodag_t *dodag) +{ + dodag->dao_time = timex_set(NG_RPL_REGULAR_DAO_INTERVAL, 0); + dodag->dao_counter = 0; + dodag->dao_ack_received = false; + vtimer_remove(&dodag->dao_timer); + vtimer_set_msg(&dodag->dao_timer, dodag->dao_time, ng_rpl_pid, NG_RPL_MSG_TYPE_DAO_HANDLE, dodag); +} + +void _dao_handle_send(ng_rpl_dodag_t *dodag) +{ + if ((dodag->dao_ack_received == false) && (dodag->dao_counter < NG_RPL_DAO_SEND_RETRIES)) { + dodag->dao_counter++; + ng_rpl_send_DAO(dodag, NULL, dodag->default_lifetime); + dodag->dao_time = timex_set(NG_RPL_DEFAULT_WAIT_FOR_DAO_ACK, 0); + vtimer_remove(&dodag->dao_timer); + vtimer_set_msg(&dodag->dao_timer, dodag->dao_time, + ng_rpl_pid, NG_RPL_MSG_TYPE_DAO_HANDLE, dodag); + } + else if (dodag->dao_ack_received == false) { + ng_rpl_long_delay_dao(dodag); + } +} + +/** + * @} + */ diff --git a/sys/net/routing/ng_rpl/ng_rpl_control_messages.c b/sys/net/routing/ng_rpl/ng_rpl_control_messages.c new file mode 100644 index 000000000000..e98cd6ee5c9b --- /dev/null +++ b/sys/net/routing/ng_rpl/ng_rpl_control_messages.c @@ -0,0 +1,619 @@ +/* + * Copyright (C) 2013 - 2014 INRIA. + * Copyright (C) 2015 Cenk Gündoğan + * + * 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 + * + * @author Cenk Gündoğan + */ + +#include "net/ng_rpl.h" +#include "inet_ntop.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG && defined(MODULE_IPV6_ADDR) +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +#define NG_RPL_GROUNDED_SHIFT (7) +#define NG_RPL_MOP_SHIFT (3) +#define NG_RPL_OPT_DODAG_CONF_LEN (14) +#define NG_RPL_SHIFTED_MOP_MASK (0x7) +#define NG_RPL_PRF_MASK (0x7) + +void _ng_rpl_send(ng_pktsnip_t *pkt, ipv6_addr_t *src, ipv6_addr_t *dst, + ipv6_addr_t *dodag_id) +{ + ng_pktsnip_t *hdr; + ipv6_addr_t all_RPL_nodes = NG_IPV6_ADDR_ALL_RPL_NODES, ll_addr; + kernel_pid_t iface = ng_ipv6_netif_find_by_addr(NULL, &all_RPL_nodes); + if (iface == KERNEL_PID_UNDEF) { + DEBUG("RPL: no suitable interface found for this destination address\n"); + ng_pktbuf_release(pkt); + return; + } + + if (src == NULL) { + ipv6_addr_t *tmp = NULL; + if (dodag_id != NULL) { + tmp = ng_ipv6_netif_match_prefix(iface, dodag_id); + } + else if (dodag_id == NULL) { + tmp = ng_ipv6_netif_find_best_src_addr(iface, &all_RPL_nodes); + } + + if (tmp == NULL) { + DEBUG("RPL: no suitable src address found\n"); + ng_pktbuf_release(pkt); + return; + } + + memcpy(&ll_addr, tmp, sizeof(ll_addr)); + ipv6_addr_set_link_local_prefix(&ll_addr); + src = &ll_addr; + } + + if (dst == NULL) { + dst = &all_RPL_nodes; + } + + hdr = ng_ipv6_hdr_build(pkt, (uint8_t *)src, sizeof(ipv6_addr_t), (uint8_t *)dst, + sizeof(ipv6_addr_t)); + + if (hdr == NULL) { + DEBUG("RPL: Send - no space left in packet buffer\n"); + ng_pktbuf_release(pkt); + return; + } + + if (!ng_netapi_dispatch_send(NG_NETTYPE_IPV6, NG_NETREG_DEMUX_CTX_ALL,hdr)) { + DEBUG("RPL: cannot send packet: no subscribers found.\n"); + ng_pktbuf_release(hdr); + } + +} + +void ng_rpl_send_DIO(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination) +{ + if (dodag == NULL) { + DEBUG("RPL: Error - trying to send DIO without being part of a dodag.\n"); + return; + } + + ng_pktsnip_t *pkt; + ng_icmpv6_hdr_t *icmp; + ng_rpl_dio_t *dio; + uint8_t *pos; + int size = sizeof(ng_icmpv6_hdr_t) + sizeof(ng_rpl_dio_t); + + if ((dodag->dodag_conf_counter % 3) == 0) { + size += sizeof(ng_rpl_opt_dodag_conf_t); + } + + if ((pkt = ng_icmpv6_build(NULL, NG_ICMPV6_RPL_CTRL, NG_RPL_ICMPV6_CODE_DIO, size)) == NULL) { + DEBUG("RPL: Send DIO - no space left in packet buffer\n"); + return; + } + + icmp = (ng_icmpv6_hdr_t *)pkt->data; + dio = (ng_rpl_dio_t *)(icmp + 1); + pos = (uint8_t *) dio; + dio->instance_id = dodag->instance->id; + dio->version_number = dodag->version; + dio->rank = byteorder_htons(dodag->my_rank); + dio->g_mop_prf = (dodag->grounded << NG_RPL_GROUNDED_SHIFT) | + (dodag->instance->mop << NG_RPL_MOP_SHIFT) | dodag->prf; + dio->dtsn = dodag->dtsn; + dio->flags = 0; + dio->reserved = 0; + dio->dodag_id = dodag->dodag_id; + + pos += sizeof(*dio); + + if ((dodag->dodag_conf_counter % 3) == 0) { + ng_rpl_opt_dodag_conf_t *dodag_conf; + dodag_conf = (ng_rpl_opt_dodag_conf_t *) pos; + dodag_conf->type = NG_RPL_OPT_DODAG_CONF; + dodag_conf->length = NG_RPL_OPT_DODAG_CONF_LEN; + dodag_conf->flags_a_pcs = 0; + dodag_conf->dio_int_doubl = dodag->dio_interval_doubl; + dodag_conf->dio_int_min = dodag->dio_min; + dodag_conf->dio_redun = dodag->dio_redun; + dodag_conf->max_rank_inc = byteorder_htons(dodag->instance->max_rank_inc); + dodag_conf->min_hop_rank_inc = byteorder_htons(dodag->instance->min_hop_rank_inc); + dodag_conf->ocp = byteorder_htons(dodag->instance->of->ocp); + dodag_conf->reserved = 0; + dodag_conf->default_lifetime = dodag->default_lifetime; + dodag_conf->lifetime_unit = byteorder_htons(dodag->lifetime_unit); + pos += sizeof(*dodag_conf); + } + + dodag->dodag_conf_counter++; + _ng_rpl_send(pkt, NULL, destination, &dodag->dodag_id); +} + +void ng_rpl_send_DIS(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination) +{ + (void) dodag; + ng_pktsnip_t *pkt; + ng_icmpv6_hdr_t *icmp; + ng_rpl_dis_t *dis; + /* TODO: Currently the DIS is too small so that wireshark complains about an incorrect + * ethernet frame check sequence. In order to prevent this, 4 PAD1 options are added. + * This will be addressed in follow-up PRs */ + int size = sizeof(ng_icmpv6_hdr_t) + sizeof(ng_rpl_dis_t) + 4; + + if ((pkt = ng_icmpv6_build(NULL, NG_ICMPV6_RPL_CTRL, NG_RPL_ICMPV6_CODE_DIS, size)) == NULL) { + DEBUG("RPL: Send DIS - no space left in packet buffer\n"); + return; + } + + icmp = (ng_icmpv6_hdr_t *)pkt->data; + dis = (ng_rpl_dis_t *)(icmp + 1); + dis->flags = 0; + dis->reserved = 0; + /* TODO: see above TODO */ + memset((dis + 1), 0, 4); + + _ng_rpl_send(pkt, NULL, destination, (dodag ? &dodag->dodag_id : NULL)); +} + +void ng_rpl_recv_DIS(ng_rpl_dis_t *dis, ipv6_addr_t *src, ipv6_addr_t *dst, uint16_t len) +{ + /* TODO handle Solicited Information Option */ + (void) dis; + (void) len; + + if (ipv6_addr_is_multicast(dst)) { + for (uint8_t i = 0; i < NG_RPL_DODAGS_NUMOF; ++i) { + if (ng_rpl_dodags[i].state != 0) { + trickle_reset_timer(&ng_rpl_dodags[i].trickle); + } + } + } + else { + for (uint8_t i = 0; i < NG_RPL_DODAGS_NUMOF; ++i) { + if (ng_rpl_dodags[i].state != 0) { + ng_rpl_dodags[i].dodag_conf_counter = 0; + ng_rpl_send_DIO(&ng_rpl_dodags[i], src); + } + } + } +} + +/** @todo allow target prefixes in target options to be of variable length */ +bool _parse_options(int msg_type, ng_rpl_dodag_t *dodag, ng_rpl_opt_t *opt, uint16_t len, + ipv6_addr_t *src) +{ + uint16_t l = 0; + ng_rpl_opt_target_t *first_target = NULL; + while(l < len) { + if ((opt->type != NG_RPL_OPT_PAD1) && (len < opt->length + sizeof(ng_rpl_opt_t) + l)) { + /* return false to delete the dodag, + * because former options may also contain errors */ + return false; + } + + switch(opt->type) { + case (NG_RPL_OPT_PAD1): { + DEBUG("RPL: PAD1 option parsed\n"); + l += 1; + opt = (ng_rpl_opt_t *) (((uint8_t *) opt) + 1); + continue; + } + case (NG_RPL_OPT_PADN): { + DEBUG("RPL: PADN option parsed\n"); + break; + } + case (NG_RPL_OPT_DODAG_CONF): { + if (msg_type != NG_RPL_ICMPV6_CODE_DIO) { + DEBUG("RPL: Ignore DODAG CONF DIO option\n"); + return true; + } + DEBUG("RPL: DODAG CONF DIO option parsed\n"); + ng_rpl_opt_dodag_conf_t *dc = (ng_rpl_opt_dodag_conf_t *) opt; + ng_rpl_of_t *of = ng_rpl_get_of_for_ocp(byteorder_ntohs(dc->ocp)); + if (of != NULL) { + dodag->instance->of = of; + } + else { + DEBUG("RPL: Unsupported OCP 0x%02x\n", byteorder_ntohs(dc->ocp)); + dodag->instance->of = ng_rpl_get_of_for_ocp(NG_RPL_DEFAULT_OCP); + } + dodag->dio_interval_doubl = dc->dio_int_doubl; + dodag->dio_min = dc->dio_int_min; + dodag->dio_redun = dc->dio_redun; + dodag->instance->max_rank_inc = byteorder_ntohs(dc->max_rank_inc); + dodag->instance->min_hop_rank_inc = byteorder_ntohs(dc->min_hop_rank_inc); + dodag->default_lifetime = dc->default_lifetime; + dodag->lifetime_unit = byteorder_ntohs(dc->lifetime_unit); + dodag->trickle.Imin = (1 << dodag->dio_min); + dodag->trickle.Imax = dodag->dio_interval_doubl; + dodag->trickle.k = dodag->dio_redun; + break; + } + case (NG_RPL_OPT_TARGET): { + if (msg_type != NG_RPL_ICMPV6_CODE_DAO) { + DEBUG("RPL: Ignore RPL TARGET DAO option\n"); + return true; + } + DEBUG("RPL: RPL TARGET DAO option parsed\n"); + kernel_pid_t if_id = ng_ipv6_netif_find_by_prefix(NULL, &dodag->dodag_id); + if (if_id == KERNEL_PID_UNDEF) { + DEBUG("RPL: no interface found for the configured DODAG id\n"); + return false; + } + + ng_rpl_opt_target_t *target = (ng_rpl_opt_target_t *) opt; + if (first_target == NULL) { + first_target = target; + } + + fib_add_entry(if_id, target->target.u8, sizeof(ipv6_addr_t), AF_INET6, src->u8, + sizeof(ipv6_addr_t), AF_INET6, + (dodag->default_lifetime * dodag->lifetime_unit) * SEC_IN_MS); + break; + } + case (NG_RPL_OPT_TRANSIT): { + if (msg_type != NG_RPL_ICMPV6_CODE_DAO) { + DEBUG("RPL: Ignore RPL TRANSIT INFO DAO option\n"); + return true; + } + DEBUG("RPL: RPL TRANSIT INFO DAO option parsed\n"); + ng_rpl_opt_transit_t *transit = (ng_rpl_opt_transit_t *) opt; + if (first_target == NULL) { + DEBUG("RPL: Encountered a RPL TRANSIT DAO option without \ +a preceding RPL TARGET DAO option\n"); + break; + } + + do { + fib_update_entry(first_target->target.u8, sizeof(ipv6_addr_t), + src->u8, sizeof(ipv6_addr_t), AF_INET6, + (transit->path_lifetime * dodag->lifetime_unit * SEC_IN_MS)); + first_target = (ng_rpl_opt_target_t *) (((uint8_t *) (first_target)) + + sizeof(ng_rpl_opt_t) + first_target->length); + } + while (first_target->type == NG_RPL_OPT_TARGET); + + first_target = NULL; + break; + } + } + l += opt->length + sizeof(ng_rpl_opt_t); + opt = (ng_rpl_opt_t *) (((uint8_t *) (opt + 1)) + opt->length); + } + return true; +} + +void ng_rpl_recv_DIO(ng_rpl_dio_t *dio, ipv6_addr_t *src, uint16_t len) +{ + ng_rpl_instance_t *inst = NULL; + ng_rpl_dodag_t *dodag = NULL; + + len -= (sizeof(ng_rpl_dio_t) + sizeof(ng_icmpv6_hdr_t)); + + if (ng_rpl_instance_add(dio->instance_id, &inst)) { + inst->mop = (dio->g_mop_prf >> NG_RPL_MOP_SHIFT) & NG_RPL_SHIFTED_MOP_MASK; + inst->of = ng_rpl_get_of_for_ocp(NG_RPL_DEFAULT_OCP); + } + else if (inst == NULL) { + DEBUG("RPL: Could not allocate a new instance.\n"); + return; + } + + if ((byteorder_ntohs(dio->rank) == NG_RPL_INFINITE_RANK) && + (ng_rpl_dodag_get(inst, &dio->dodag_id) == NULL)) { + DEBUG("RPL: ignore INFINITE_RANK DIO when we are not part of this DODAG\n"); + ng_rpl_instance_remove(inst); + return; + } + + if (ng_rpl_dodag_add(inst, &dio->dodag_id, &dodag)) { + DEBUG("RPL: Joined DODAG (%s).\n", + ipv6_addr_to_str(addr_str, &dio->dodag_id, sizeof(addr_str))); + + ng_rpl_parent_t *parent = NULL; + + if (!ng_rpl_parent_add_by_addr(dodag, src, &parent) && (parent == NULL)) { + DEBUG("RPL: Could not allocate new parent.\n"); + ng_rpl_dodag_remove(dodag); + return; + } + + dodag->version = dio->version_number; + + parent->rank = byteorder_ntohs(dio->rank); + + if(!_parse_options(NG_RPL_ICMPV6_CODE_DIO, dodag, (ng_rpl_opt_t *)(dio + 1), len, NULL)) { + ng_rpl_dodag_remove(dodag); + return; + } + + ng_rpl_delay_dao(dodag); + trickle_start(ng_rpl_pid, &dodag->trickle, NG_RPL_MSG_TYPE_TRICKLE_INTERVAL, + NG_RPL_MSG_TYPE_TRICKLE_CALLBACK, (1 << dodag->dio_min), + dodag->dio_interval_doubl, dodag->dio_redun); + + ng_rpl_parent_update(dodag, parent); + return; + } + else if (dodag == NULL) { + DEBUG("RPL: Could not allocate a new DODAG.\n"); + if (inst->dodags == NULL) { + ng_rpl_instance_remove(inst); + } + return; + } + + if (dodag->instance->mop != + ((dio->g_mop_prf >> NG_RPL_MOP_SHIFT) & NG_RPL_SHIFTED_MOP_MASK)) { + DEBUG("RPL: invalid MOP for this instance.\n"); + return; + } + + if (NG_RPL_COUNTER_GREATER_THAN(dio->version_number, dodag->version)) { + if (dodag->node_status == NG_RPL_ROOT_NODE) { + dodag->version = NG_RPL_COUNTER_INCREMENT(dio->version_number); + trickle_reset_timer(&dodag->trickle); + } + else { + dodag->version = dio->version_number; + ng_rpl_local_repair(dodag); + } + } + else if (NG_RPL_COUNTER_GREATER_THAN(dodag->version, dio->version_number)) { + trickle_reset_timer(&dodag->trickle); + return; + } + + if (dodag->node_status == NG_RPL_ROOT_NODE) { + if (byteorder_ntohs(dio->rank) != NG_RPL_INFINITE_RANK) { + trickle_increment_counter(&dodag->trickle); + } + return; + } + + dodag->grounded = dio->g_mop_prf >> NG_RPL_GROUNDED_SHIFT; + dodag->prf = dio->g_mop_prf & NG_RPL_PRF_MASK; + + ng_rpl_parent_t *parent = NULL; + + if (!ng_rpl_parent_add_by_addr(dodag, src, &parent) && (parent == NULL)) { + DEBUG("RPL: Could not allocate new parent.\n"); + if (dodag->parents == NULL) { + ng_rpl_dodag_remove(dodag); + } + return; + } + /* cppcheck-suppress nullPointer */ + else if (parent != NULL) { + trickle_increment_counter(&dodag->trickle); + } + + parent->rank = byteorder_ntohs(dio->rank); + + if(!_parse_options(NG_RPL_ICMPV6_CODE_DIO, dodag, (ng_rpl_opt_t *)(dio + 1), len, NULL)) { + ng_rpl_dodag_remove(dodag); + return; + } + + ng_rpl_parent_update(dodag, parent); + + if (parent->state != 0) { + if (dodag->parents && (parent == dodag->parents) && (parent->dtsn != dio->dtsn)) { + ng_rpl_delay_dao(dodag); + } + parent->dtsn = dio->dtsn; + } +} + +void _dao_fill_target(ng_rpl_opt_target_t *target, ipv6_addr_t *addr) +{ + target->type = NG_RPL_OPT_TARGET; + target->length = sizeof(target->flags) + sizeof(target->prefix_length) + sizeof(target->target); + target->flags = 0; + target->prefix_length = 128; + target->target = *addr; +} + +void ng_rpl_send_DAO(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination, uint8_t lifetime) +{ + size_t dst_size = NG_RPL_PARENTS_NUMOF; + fib_destination_set_entry_t fib_dest_set[NG_RPL_PARENTS_NUMOF]; + + if (dodag == NULL) { + DEBUG("RPL: Error - trying to send DAO without being part of a dodag.\n"); + return; + } + + if (dodag->node_status == NG_RPL_ROOT_NODE) { + return; + } + + if (destination == NULL) { + if (dodag->parents == NULL) { + DEBUG("RPL: dodag has no preferred parent\n"); + return; + } + + destination = &(dodag->parents->addr); + } + + ng_pktsnip_t *pkt; + ng_icmpv6_hdr_t *icmp; + ng_rpl_dao_t *dao; + ng_rpl_opt_target_t *target; + ng_rpl_opt_transit_t *transit; + + /* find my address */ + ipv6_addr_t *me = NULL; + ng_ipv6_netif_find_by_prefix(&me, &dodag->dodag_id); + if (me == NULL) { + DEBUG("RPL: no address configured\n"); + return; + } + + ng_ipv6_netif_addr_t *me_netif = ng_ipv6_netif_addr_get(me); + if (me_netif == NULL) { + DEBUG("RPL: no netif address found for %s\n", ipv6_addr_to_str(addr_str, me, + sizeof(addr_str))); + return; + } + + /* find prefix for my address */ + ipv6_addr_t prefix; + ipv6_addr_init_prefix(&prefix, me, me_netif->prefix_len); + fib_get_destination_set(prefix.u8, sizeof(ipv6_addr_t), fib_dest_set, &dst_size); + + int size = sizeof(ng_icmpv6_hdr_t) + sizeof(ng_rpl_dao_t) + + (sizeof(ng_rpl_opt_target_t) * (dst_size + 1)) + sizeof(ng_rpl_opt_transit_t); + + if ((pkt = ng_icmpv6_build(NULL, NG_ICMPV6_RPL_CTRL, NG_RPL_ICMPV6_CODE_DAO, size)) == NULL) { + DEBUG("RPL: Send DAO - no space left in packet buffer\n"); + return; + } + + icmp = (ng_icmpv6_hdr_t *)pkt->data; + dao = (ng_rpl_dao_t *)(icmp + 1); + + dao->instance_id = dodag->instance->id; + /* set the D flag to indicate that a DODAG id is present */ + /* set the K flag to indicate that a ACKs are required */ + dao->k_d_flags = ((1 << 6) | (1 << 7)); + dao->dao_sequence = dodag->dao_seq; + dao->dodag_id = dodag->dodag_id; + dao->reserved = 0; + + /* add own address */ + target = (ng_rpl_opt_target_t *) (dao + 1); + _dao_fill_target(target, me); + /* add children */ + for (size_t i = 0; i < dst_size; ++i) { + target = (target + 1); + _dao_fill_target(target, ((ipv6_addr_t *) fib_dest_set[i].dest)); + } + + transit = (ng_rpl_opt_transit_t *) (target + 1); + transit->type = NG_RPL_OPT_TRANSIT; + transit->length = sizeof(transit->e_flags) + sizeof(transit->path_control) + + sizeof(transit->path_sequence) + sizeof(transit->path_lifetime); + transit->e_flags = 0; + transit->path_control = 0; + transit->path_sequence = 0; + transit->path_lifetime = lifetime; + + _ng_rpl_send(pkt, NULL, destination, &dodag->dodag_id); + + NG_RPL_COUNTER_INCREMENT(dodag->dao_seq); +} + +void ng_rpl_send_DAO_ACK(ng_rpl_dodag_t *dodag, ipv6_addr_t *destination, uint8_t seq) +{ + if (dodag == NULL) { + DEBUG("RPL: Error - trying to send DAO-ACK without being part of a dodag.\n"); + return; + } + + ng_pktsnip_t *pkt; + ng_icmpv6_hdr_t *icmp; + ng_rpl_dao_ack_t *dao_ack; + int size = sizeof(ng_icmpv6_hdr_t) + sizeof(ng_rpl_dao_ack_t); + + if ((pkt = ng_icmpv6_build(NULL, NG_ICMPV6_RPL_CTRL, NG_RPL_ICMPV6_CODE_DAO_ACK, size)) == NULL) { + DEBUG("RPL: Send DAOACK - no space left in packet buffer\n"); + return; + } + + icmp = (ng_icmpv6_hdr_t *)pkt->data; + dao_ack = (ng_rpl_dao_ack_t *)(icmp + 1); + + dao_ack->instance_id = dodag->instance->id; + /* set the D flag to indicate that a DODAG id is present */ + dao_ack->d_reserved = (1 << 7); + dao_ack->dao_sequence = seq; + dao_ack->status = 0; + dao_ack->dodag_id = dodag->dodag_id; + + _ng_rpl_send(pkt, NULL, destination, &dodag->dodag_id); +} + +void ng_rpl_recv_DAO(ng_rpl_dao_t *dao, ipv6_addr_t *src, uint16_t len) +{ + ng_rpl_instance_t *inst = NULL; + ng_rpl_dodag_t *dodag = NULL; + if ((inst = ng_rpl_instance_get(dao->instance_id)) == NULL) { + DEBUG("RPL: DAO with unknown instance id (%d) received\n", dao->instance_id); + return; + } + + /* check if the D flag is set before accessing the DODAG id */ + if (!(dao->k_d_flags & (1 << 6))) { + DEBUG("RPL: DAO with D flag unset - global instances not supported\n"); + return; + } + + if ((dodag = ng_rpl_dodag_get(inst, &dao->dodag_id)) == NULL) { + DEBUG("RPL: DAO with unknown DODAG id (%s)\n", ipv6_addr_to_str(addr_str, + &dao->dodag_id, sizeof(addr_str))); + return; + } + + len -= (sizeof(ng_rpl_dao_t) + sizeof(ng_icmpv6_hdr_t)); + if(!_parse_options(NG_RPL_ICMPV6_CODE_DAO, dodag, (ng_rpl_opt_t *) (dao + 1), len, src)) { + ng_rpl_dodag_remove(dodag); + return; + } + + /* send a DAO-ACK if K flag is set */ + if (dao->k_d_flags & (1 << 7)) { + ng_rpl_send_DAO_ACK(dodag, src, dao->dao_sequence); + } + + ng_rpl_delay_dao(dodag); +} + +void ng_rpl_recv_DAO_ACK(ng_rpl_dao_ack_t *dao_ack) +{ + ng_rpl_instance_t *inst = NULL; + ng_rpl_dodag_t *dodag = NULL; + if ((inst = ng_rpl_instance_get(dao_ack->instance_id)) == NULL) { + DEBUG("RPL: DAO-ACK with unknown instance id (%d) received\n", dao_ack->instance_id); + return; + } + + /* check if the D flag is set before accessing the DODAG id */ + if (!(dao_ack->d_reserved & (1 << 7))) { + DEBUG("RPL: DAO-ACK with D flag unset - global instances not supported\n"); + return; + } + + if ((dodag = ng_rpl_dodag_get(inst, &dao_ack->dodag_id)) == NULL) { + DEBUG("RPL: DAO-ACK with unknown DODAG id (%s)\n", ipv6_addr_to_str(addr_str, + &dao_ack->dodag_id, sizeof(addr_str))); + return; + } + + if ((dao_ack->status != 0) && (dao_ack->dao_sequence != dodag->dao_seq)) { + DEBUG("RPL: DAO-ACK sequence (%d) does not match expected sequence (%d)\n", + dao_ack->dao_sequence, dodag->dao_seq); + return; + } + + dodag->dao_ack_received = true; + ng_rpl_long_delay_dao(dodag); +} + +/** + * @} + */ diff --git a/sys/net/routing/ng_rpl/ng_rpl_dodag.c b/sys/net/routing/ng_rpl/ng_rpl_dodag.c new file mode 100644 index 000000000000..6da4b1703dfb --- /dev/null +++ b/sys/net/routing/ng_rpl/ng_rpl_dodag.c @@ -0,0 +1,398 @@ +/** + * Copyright (C) 2013, 2014 INRIA. + * Copyright (C) 2015 Cenk Gündoğan + * + * 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 + * @author Eric Engel + * @author Cenk Gündoğan + */ + +#include +#include "net/ng_rpl/dodag.h" +#include "net/ng_rpl/structs.h" +#include "utlist.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG && defined(MODULE_PV6_ADDR) +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +void rpl_trickle_send_dio(void *args) +{ + ng_rpl_dodag_t *dodag = (ng_rpl_dodag_t *) args; + ipv6_addr_t all_RPL_nodes = NG_IPV6_ADDR_ALL_RPL_NODES; + ng_rpl_send_DIO(dodag, &all_RPL_nodes); + DEBUG("trickle callback: Instance (%d) | DODAG: (%s)\n", dodag->instance->id, + ipv6_addr_to_str(addr_str,&dodag->dodag_id, sizeof(addr_str))); +} + +bool ng_rpl_instance_add(uint8_t instance_id, ng_rpl_instance_t **inst) +{ + *inst = NULL; + bool first = true; + for (uint8_t i = 0; i < NG_RPL_INSTANCES_NUMOF; ++i) { + /* save position to the first unused instance */ + if ((ng_rpl_instances[i].state == 0) && first) { + *inst = &ng_rpl_instances[i]; + first = false; + continue; + } + else if ((ng_rpl_instances[i].state != 0) && (ng_rpl_instances[i].id == instance_id)) { + DEBUG("Instance with id %d exists\n", instance_id); + *inst = &ng_rpl_instances[i]; + return false; + } + } + + if (*inst != NULL) { + (*inst)->id = instance_id; + (*inst)->state = 1; + (*inst)->max_rank_inc = NG_RPL_DEFAULT_MAX_RANK_INCREASE; + (*inst)->min_hop_rank_inc = NG_RPL_DEFAULT_MIN_HOP_RANK_INCREASE; + (*inst)->dodags = NULL; + return true; + } + + /* no space available to allocate a new instance */ + DEBUG("Could not allocate a new RPL instance\n"); + *inst = NULL; + return false; +} + +bool ng_rpl_instance_remove_by_id(uint8_t instance_id) +{ + for(uint8_t i = 0; i < NG_RPL_INSTANCES_NUMOF; ++i) { + if (ng_rpl_instances[i].id == instance_id) { + ng_rpl_dodag_t *elt, *tmp; + LL_FOREACH_SAFE(ng_rpl_instances[i].dodags, elt, tmp) { + ng_rpl_dodag_remove(elt); + } + memset(&ng_rpl_instances[i], 0, sizeof(ng_rpl_instance_t)); + return true; + } + } + return false; +} + +bool ng_rpl_instance_remove(ng_rpl_instance_t *inst) +{ + ng_rpl_dodag_t *elt, *tmp; + LL_FOREACH_SAFE(inst->dodags, elt, tmp) { + ng_rpl_dodag_remove(elt); + } + memset(inst, 0, sizeof(ng_rpl_instance_t)); + return true; +} + +ng_rpl_instance_t *ng_rpl_instance_get(uint8_t instance_id) +{ + for (uint8_t i = 0; i < NG_RPL_INSTANCES_NUMOF; ++i) { + if (ng_rpl_instances[i].id == instance_id) { + return &ng_rpl_instances[i]; + } + } + return NULL; +} + +bool ng_rpl_dodag_add(ng_rpl_instance_t *instance, ipv6_addr_t *dodag_id, ng_rpl_dodag_t **dodag) +{ + if ((instance == NULL) || instance->state == 0) { + DEBUG("Instance is NULL or unused\n"); + return false; + } + + *dodag = NULL; + bool first = true; + for (uint8_t i = 0; i < NG_RPL_DODAGS_NUMOF; ++i) { + /* save position to the first unused instance */ + if ((ng_rpl_dodags[i].state == 0) && first) { + *dodag = &ng_rpl_dodags[i]; + first = false; + continue; + } + else if ((ng_rpl_dodags[i].state != 0) && + (ng_rpl_dodags[i].instance->id == instance->id) && + ipv6_addr_equal(&ng_rpl_dodags[i].dodag_id, dodag_id)) { + DEBUG("DODAG with id: %s does exist\n", ipv6_addr_to_str(addr_str, dodag_id, + sizeof(addr_str))); + *dodag = &ng_rpl_dodags[i]; + return false; + } + } + + if (*dodag != NULL) { + (*dodag)->instance = instance; + LL_APPEND(instance->dodags, *dodag); + (*dodag)->state = 1; + (*dodag)->dodag_id = *dodag_id; + (*dodag)->my_rank = NG_RPL_INFINITE_RANK; + (*dodag)->trickle.callback.func = &rpl_trickle_send_dio; + (*dodag)->trickle.callback.args = *dodag; + (*dodag)->dio_interval_doubl = NG_RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS; + (*dodag)->dio_min = NG_RPL_DEFAULT_DIO_INTERVAL_MIN; + (*dodag)->dio_redun = NG_RPL_DEFAULT_DIO_REDUNDANCY_CONSTANT; + (*dodag)->default_lifetime = NG_RPL_DEFAULT_LIFETIME; + (*dodag)->lifetime_unit = NG_RPL_LIFETIME_UNIT; + (*dodag)->node_status = NG_RPL_NORMAL_NODE; + (*dodag)->dao_seq = NG_RPL_COUNTER_INIT; + (*dodag)->dtsn = 0; + (*dodag)->dao_ack_received = false; + (*dodag)->dao_counter = 0; + (*dodag)->parents = NULL; + (*dodag)->cleanup_time = timex_set(NG_RPL_CLEANUP_TIME, 0); + return true; + } + + /* no space available to allocate a new dodag */ + DEBUG("Could not allocate a new RPL DODAG\n"); + *dodag = NULL; + return false; +} + +bool ng_rpl_dodag_remove(ng_rpl_dodag_t *dodag) +{ + ng_rpl_dodag_remove_all_parents(dodag); + ng_rpl_instance_t *inst = dodag->instance; + LL_DELETE(inst->dodags, dodag); + trickle_stop(&dodag->trickle); + vtimer_remove(&dodag->dao_timer); + vtimer_remove(&dodag->cleanup_timer); + memset(dodag, 0, sizeof(ng_rpl_dodag_t)); + if (inst->dodags == NULL) { + ng_rpl_instance_remove(inst); + } + return true; +} + +void ng_rpl_dodag_remove_all_parents(ng_rpl_dodag_t *dodag) +{ + ng_rpl_parent_t *elt, *tmp; + LL_FOREACH_SAFE(dodag->parents, elt, tmp) { + ng_rpl_parent_remove(elt); + } + vtimer_remove(&dodag->cleanup_timer); + vtimer_set_msg(&dodag->cleanup_timer, dodag->cleanup_time, ng_rpl_pid, + NG_RPL_MSG_TYPE_CLEANUP_HANDLE, dodag); +} + +ng_rpl_dodag_t *ng_rpl_dodag_get(ng_rpl_instance_t *instance, ipv6_addr_t *dodag_id) +{ + if ((instance == NULL) || (instance->state == 0)) { + DEBUG("Instance is NULL or unused\n"); + return NULL; + } + + ng_rpl_dodag_t *dodag = NULL; + LL_FOREACH(instance->dodags, dodag) { + if (ipv6_addr_equal(&dodag->dodag_id, dodag_id)) { + return dodag; + } + } + return NULL; +} + +bool ng_rpl_parent_add_by_addr(ng_rpl_dodag_t *dodag, ipv6_addr_t *addr, ng_rpl_parent_t **parent) +{ + if ((dodag == NULL) || (dodag->state == 0)) { + DEBUG("DODAG is NULL or unused\n"); + return false; + } + + *parent = NULL; + bool first = true; + for (uint8_t i = 0; i < NG_RPL_PARENTS_NUMOF; ++i) { + /* save position to the first unused instance */ + if ((ng_rpl_parents[i].state == 0) && first) { + *parent = &ng_rpl_parents[i]; + first = false; + continue; + } + /* return false if parent exists */ + else if ((ng_rpl_parents[i].state != 0) && + (ng_rpl_parents[i].dodag->instance->id == dodag->instance->id) && + ipv6_addr_equal(&ng_rpl_parents[i].dodag->dodag_id, &dodag->dodag_id) && + ipv6_addr_equal(&ng_rpl_parents[i].addr, addr)) { + DEBUG("parent with addr: %s does exist\n", ipv6_addr_to_str(addr_str, addr, + sizeof(addr_str))); + *parent = &ng_rpl_parents[i]; + return false; + } + } + + if (*parent != NULL) { + (*parent)->dodag = dodag; + LL_APPEND(dodag->parents, *parent); + (*parent)->state = 1; + (*parent)->addr = *addr; + if ((*parent) == (*parent)->dodag->parents) { + ipv6_addr_t all_RPL_nodes = NG_IPV6_ADDR_ALL_RPL_NODES; + ipv6_addr_t def = IPV6_ADDR_UNSPECIFIED; + kernel_pid_t if_id = ng_ipv6_netif_find_by_addr(NULL, &all_RPL_nodes); + if (if_id == KERNEL_PID_UNDEF) { + DEBUG("RPL: no interface found for the parent addres\n"); + return false; + } + if (fib_add_entry(if_id, def.u8, sizeof(ipv6_addr_t), AF_INET6, dodag->parents->addr.u8, + sizeof(ipv6_addr_t), AF_INET6, + (dodag->default_lifetime * dodag->lifetime_unit) * SEC_IN_MS) != 0) { + DEBUG("RPL: error adding parent to FIB\n"); + ng_rpl_parent_remove(*parent); + return false; + } + } + return true; + } + + /* no space available to allocate a new parent */ + DEBUG("Could not allocate a new parent\n"); + *parent = NULL; + return false; +} + +ng_rpl_parent_t *ng_rpl_parent_get(ng_rpl_dodag_t *dodag, ipv6_addr_t *addr) +{ + if ((dodag == NULL) || (dodag->state == 0)) { + DEBUG("DODAG is NULL or unused\n"); + return NULL; + } + + ng_rpl_parent_t *parent = NULL; + LL_FOREACH(dodag->parents, parent) { + if (ipv6_addr_equal(&parent->addr, addr)) { + return parent; + } + } + return NULL; +} + +bool ng_rpl_parent_remove(ng_rpl_parent_t *parent) +{ + if (parent == parent->dodag->parents) { + ipv6_addr_t def = { .u64 = {{0}, {0}} }; + fib_remove_entry(def.u8, sizeof(ipv6_addr_t)); + } + ng_rpl_dodag_t *dodag = parent->dodag; + LL_DELETE(dodag->parents, parent); + memset(parent, 0, sizeof(ng_rpl_parent_t)); + return true; +} + +void ng_rpl_local_repair(ng_rpl_dodag_t *dodag) +{ + DEBUG("RPL: [INFO] Local Repair started\n"); + + dodag->dtsn++; + + if (dodag->parents) { + ng_rpl_dodag_remove_all_parents(dodag); + ipv6_addr_t def = IPV6_ADDR_UNSPECIFIED; + fib_remove_entry(def.u8, sizeof(ipv6_addr_t)); + } + + if (dodag->my_rank != NG_RPL_INFINITE_RANK) { + trickle_reset_timer(&dodag->trickle); + vtimer_remove(&dodag->cleanup_timer); + vtimer_set_msg(&dodag->cleanup_timer, dodag->cleanup_time, ng_rpl_pid, + NG_RPL_MSG_TYPE_CLEANUP_HANDLE, dodag); + } + + dodag->my_rank = NG_RPL_INFINITE_RANK; +} + +void ng_rpl_parent_update(ng_rpl_dodag_t *dodag, ng_rpl_parent_t *parent) +{ + uint16_t old_rank = dodag->my_rank; + timex_t now; + vtimer_now(&now); + ipv6_addr_t def = IPV6_ADDR_UNSPECIFIED; + + /* update Parent lifetime */ + if (parent != NULL) { + parent->lifetime.seconds = now.seconds + (dodag->default_lifetime * dodag->lifetime_unit); + parent->lifetime.microseconds = 0; + if (parent == dodag->parents) { + ipv6_addr_t all_RPL_nodes = NG_IPV6_ADDR_ALL_RPL_NODES; + kernel_pid_t if_id; + if ((if_id = ng_ipv6_netif_find_by_addr(NULL, &all_RPL_nodes)) != KERNEL_PID_UNDEF) { + fib_add_entry(if_id, def.u8, sizeof(ipv6_addr_t), AF_INET6, + dodag->parents->addr.u8, sizeof(ipv6_addr_t), AF_INET6, + (dodag->default_lifetime * dodag->lifetime_unit) * SEC_IN_MS); + } + } + } + + if (ng_rpl_find_preferred_parent(dodag) == NULL) { + ng_rpl_local_repair(dodag); + } + + if (dodag->parents && (old_rank != dodag->my_rank)) { + trickle_reset_timer(&dodag->trickle); + } +} + +int _compare_parents(ng_rpl_parent_t *p1, ng_rpl_parent_t *p2) +{ + return p1->dodag->instance->of->which_parent(p1, p2) == p1 ? -1 : 1; +} + +ng_rpl_parent_t *ng_rpl_find_preferred_parent(ng_rpl_dodag_t *dodag) +{ + ipv6_addr_t def = IPV6_ADDR_UNSPECIFIED; + ng_rpl_parent_t *old_best = dodag->parents; + + LL_SORT(dodag->parents, _compare_parents); + + if (dodag->parents == NULL) { + return NULL; + } + else if (dodag->parents->rank >= dodag->my_rank) { + ng_rpl_parent_remove(dodag->parents); + return NULL; + } + + dodag->my_rank = dodag->instance->of->calc_rank(dodag->parents, 0); + ng_rpl_parent_t *elt, *tmp; + LL_FOREACH_SAFE(dodag->parents, elt, tmp) { + if (dodag->parents->rank < elt->rank) { + ng_rpl_parent_remove(elt); + } + } + + if (old_best != dodag->parents) { + if (dodag->instance->mop != NG_RPL_MOP_NO_DOWNWARD_ROUTES) { + ng_rpl_send_DAO(dodag, &old_best->addr, 0); + ng_rpl_delay_dao(dodag); + } + trickle_reset_timer(&dodag->trickle); + + fib_remove_entry(def.u8, sizeof(ipv6_addr_t)); + ipv6_addr_t all_RPL_nodes = NG_IPV6_ADDR_ALL_RPL_NODES; + + kernel_pid_t if_id = ng_ipv6_netif_find_by_addr(NULL, &all_RPL_nodes); + + if (if_id == KERNEL_PID_UNDEF) { + DEBUG("RPL: no interface found for the parent addres\n"); + return NULL; + } + + fib_add_entry(if_id, def.u8, sizeof(ipv6_addr_t), AF_INET6, dodag->parents->addr.u8, + sizeof(ipv6_addr_t), AF_INET6, + (dodag->default_lifetime * dodag->lifetime_unit) * SEC_IN_MS); + } + + return dodag->parents; +} + +/** + * @} + */ diff --git a/sys/net/routing/ng_rpl/of0.c b/sys/net/routing/ng_rpl/of0.c new file mode 100644 index 000000000000..3f8fd393cbbd --- /dev/null +++ b/sys/net/routing/ng_rpl/of0.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Oliver Hahm + * + * 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 net_ng_rpl + * @{ + * @file + * @brief Objective Function Zero. + * + * Implementation of Objective Function Zero. + * + * @author Eric Engel + * @} + */ + +#include +#include "of0.h" +#include "net/ng_rpl.h" +#include "net/ng_rpl/structs.h" + +static uint16_t calc_rank(ng_rpl_parent_t *, uint16_t); +static ng_rpl_parent_t *which_parent(ng_rpl_parent_t *, ng_rpl_parent_t *); +static ng_rpl_dodag_t *which_dodag(ng_rpl_dodag_t *, ng_rpl_dodag_t *); +static void reset(ng_rpl_dodag_t *); + +static ng_rpl_of_t ng_rpl_of0 = { + 0x0, + calc_rank, + which_parent, + which_dodag, + reset, + NULL, + NULL, + NULL +}; + +ng_rpl_of_t *ng_rpl_get_of0(void) +{ + return &ng_rpl_of0; +} + +void reset(ng_rpl_dodag_t *dodag) +{ + /* Nothing to do in OF0 */ + (void) dodag; +} + +uint16_t calc_rank(ng_rpl_parent_t *parent, uint16_t base_rank) +{ + if (base_rank == 0) { + if (parent == NULL) { + return NG_RPL_INFINITE_RANK; + } + + base_rank = parent->rank; + } + + uint16_t add; + + if (parent != NULL) { + add = parent->dodag->instance->min_hop_rank_inc; + } + else { + add = NG_RPL_DEFAULT_MIN_HOP_RANK_INCREASE; + } + + if ((base_rank + add) < base_rank) { + return NG_RPL_INFINITE_RANK; + } + + return base_rank + add; +} + +/* We simply return the Parent with lower rank */ +ng_rpl_parent_t *which_parent(ng_rpl_parent_t *p1, ng_rpl_parent_t *p2) +{ + if (p1->rank <= p2->rank) { + return p1; + } + + return p2; +} + +/* Not used yet, as the implementation only makes use of one dodag for now. */ +ng_rpl_dodag_t *which_dodag(ng_rpl_dodag_t *d1, ng_rpl_dodag_t *d2) +{ + (void) d2; + return d1; +} diff --git a/sys/net/routing/ng_rpl/of0.h b/sys/net/routing/ng_rpl/of0.h new file mode 100644 index 000000000000..373a51bb3241 --- /dev/null +++ b/sys/net/routing/ng_rpl/of0.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 Oliver Hahm + * + * 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 net_ng_rpl + * @{ + * @file + * @brief Objective Function Zero. + * + * Header-file, which defines all functions for the implementation of Objective Function Zero. + * + * @author Eric Engel + */ + +#ifndef OF0_H +#define OF0_H + +#include "net/ng_rpl/structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return the address to the of0 objective function + * + * @return Address of the of0 objective function + */ +ng_rpl_of_t *ng_rpl_get_of0(void); + +#ifdef __cplusplus +} +#endif + +#endif /* OF0_H */ +/** + * @} + */ diff --git a/sys/net/routing/ng_rpl/rpl_of_manager.c b/sys/net/routing/ng_rpl/rpl_of_manager.c new file mode 100644 index 000000000000..8ea0d1595b0c --- /dev/null +++ b/sys/net/routing/ng_rpl/rpl_of_manager.c @@ -0,0 +1,54 @@ +/* +* RPL dodag implementation +* +* 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 rpl + * @{ + * @file + * @brief RPL Objective functions manager + * @author Fabian Brandt + * @} + */ + + + +#include "net/ng_rpl.h" +#include "net/ng_rpl/of_manager.h" +#include "of0.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" +/* !!! TODO: port etx/mrhof to the new network stack */ + +static ng_rpl_of_t *objective_functions[NG_RPL_IMPLEMENTED_OFS_NUMOF]; + +void ng_rpl_of_manager_init(void) +{ + /* insert new objective functions here */ + objective_functions[0] = ng_rpl_get_of0(); + /*objective_functions[1] = ng_rpl_get_of_mrhof(); */ +} + +/* find implemented OF via objective code point */ +ng_rpl_of_t *ng_rpl_get_of_for_ocp(uint16_t ocp) +{ + for (uint16_t i = 0; i < NG_RPL_IMPLEMENTED_OFS_NUMOF; i++) { + if (objective_functions[i] == NULL) { + /* fallback if something goes wrong */ + return ng_rpl_get_of0(); + } + else if (ocp == objective_functions[i]->ocp) { + return objective_functions[i]; + } + } + + return NULL; +}