diff --git a/sys/include/net/gnrc/rpl.h b/sys/include/net/gnrc/rpl.h index f1c560e8c851431509c83d0d3c978548bb368d05..ed6cde3e22aee9343db472045e2bfe194e5bd00f 100644 --- a/sys/include/net/gnrc/rpl.h +++ b/sys/include/net/gnrc/rpl.h @@ -72,6 +72,12 @@ * interface exists (`GNRC_NETIF_NUMOF > 1`) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.mk} * CFLAGS += -DGNRC_RPL_DEFAULT_NETIF=6 + * + * - By default, all incoming control messages get checked for validation. + * This validation can be disabled in case the involved RPL implementations + * are known to produce valid messages. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.mk} + * CFLAGS += -DGNRC_RPL_WITHOUT_VALIDATION * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * @{ diff --git a/sys/include/net/gnrc/rpl/structs.h b/sys/include/net/gnrc/rpl/structs.h index 7554621460a021fbd82f22c85225074938d6b33c..06f7bd6c24884da58dd069a647854232b5e15e82 100644 --- a/sys/include/net/gnrc/rpl/structs.h +++ b/sys/include/net/gnrc/rpl/structs.h @@ -32,6 +32,31 @@ extern "C" { #include "xtimer.h" #include "trickle.h" +/** + * @name Option lengths + * @{ + */ +#define GNRC_RPL_OPT_DODAG_CONF_LEN (14) +#define GNRC_RPL_OPT_PREFIX_INFO_LEN (30) +#define GNRC_RPL_OPT_TARGET_LEN (18) +#define GNRC_RPL_OPT_TRANSIT_INFO_LEN (4) +/** @} */ + +/** + * @name DAO flag macros + * @{ + */ +#define GNRC_RPL_DAO_D_BIT (1 << 6) +#define GNRC_RPL_DAO_K_BIT (1 << 7) +/** @} */ + +/** + * @name DAO-ACK flag macros + * @{ + */ +#define GNRC_RPL_DAO_ACK_D_BIT (1 << 7) +/** @} */ + /** * @anchor GNRC_RPL_REQ_DIO_OPTS * @name DIO Options for gnrc_rpl_dodag_t::dio_opts diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c index 46ee07e3ac9d35c2de04eece096a12d3f9fa708c..947d0d30a3caf91b6071b8438d977213ddbbe2b8 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c @@ -24,6 +24,9 @@ #include "net/eui64.h" #include "net/gnrc/rpl.h" +#ifndef GNRC_RPL_WITHOUT_VALIDATION +#include "gnrc_rpl_internal/validation.h" +#endif #ifdef MODULE_GNRC_RPL_P2P #include "net/gnrc/rpl/p2p_structs.h" @@ -40,18 +43,11 @@ static char addr_str[IPV6_ADDR_MAX_STR_LEN]; #define GNRC_RPL_GROUNDED_SHIFT (7) #define GNRC_RPL_MOP_SHIFT (3) -#define GNRC_RPL_OPT_DODAG_CONF_LEN (14) -#define GNRC_RPL_OPT_PREFIX_INFO_LEN (30) -#define GNRC_RPL_OPT_TARGET_LEN (18) #define GNRC_RPL_OPT_TRANSIT_E_FLAG_SHIFT (7) #define GNRC_RPL_OPT_TRANSIT_E_FLAG (1 << GNRC_RPL_OPT_TRANSIT_E_FLAG_SHIFT) -#define GNRC_RPL_OPT_TRANSIT_INFO_LEN (4) #define GNRC_RPL_SHIFTED_MOP_MASK (0x7) #define GNRC_RPL_PRF_MASK (0x7) #define GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT (1 << 6) -#define GNRC_RPL_DAO_D_BIT (1 << 6) -#define GNRC_RPL_DAO_K_BIT (1 << 7) -#define GNRC_RPL_DAO_ACK_D_BIT (1 << 7) void gnrc_rpl_send(gnrc_pktsnip_t *pkt, kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst, ipv6_addr_t *dodag_id) @@ -253,28 +249,20 @@ void gnrc_rpl_send_DIS(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination) gnrc_rpl_send(pkt, KERNEL_PID_UNDEF, NULL, destination, (inst? &(inst->dodag.dodag_id) : NULL)); } -static bool _gnrc_rpl_check_DIS_validity(gnrc_rpl_dis_t *dis, uint16_t len) -{ - uint16_t expected_len = sizeof(*dis) + sizeof(icmpv6_hdr_t); - - if (expected_len <= len) { - return true; - } - - DEBUG("RPL: wrong DIS len: %d, expected: %d\n", len, expected_len); - - return false; -} - void gnrc_rpl_recv_DIS(gnrc_rpl_dis_t *dis, kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst, uint16_t len) { /* TODO handle Solicited Information Option */ (void)iface; - if (!_gnrc_rpl_check_DIS_validity(dis, len)) { +#ifndef GNRC_RPL_WITHOUT_VALIDATION + if (!gnrc_rpl_validation_DIS(dis, len)) { return; } +#else + (void) dis; + (void) len; +#endif if (ipv6_addr_is_multicast(dst)) { for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { @@ -301,92 +289,6 @@ void gnrc_rpl_recv_DIS(gnrc_rpl_dis_t *dis, kernel_pid_t iface, ipv6_addr_t *src } } -static bool _gnrc_rpl_check_options_validity(int msg_type, gnrc_rpl_instance_t *inst, - gnrc_rpl_opt_t *opt, uint16_t len) -{ - uint16_t expected_len = 0; - - while(expected_len < len) { - switch(opt->type) { - case (GNRC_RPL_OPT_PAD1): - expected_len += 1; - opt = (gnrc_rpl_opt_t *) (((uint8_t *) opt) + 1); - continue; - - case (GNRC_RPL_OPT_DODAG_CONF): - if (msg_type != GNRC_RPL_ICMPV6_CODE_DIO) { - DEBUG("RPL: DODAG CONF DIO option not expected\n"); - return false; - } - - if (opt->length != GNRC_RPL_OPT_DODAG_CONF_LEN) { - DEBUG("RPL: wrong DIO option (DODAG CONF) len: %d, expected: %d\n", - opt->length, GNRC_RPL_OPT_DODAG_CONF_LEN); - return false; - } - break; - - case (GNRC_RPL_OPT_PREFIX_INFO): - if (msg_type != GNRC_RPL_ICMPV6_CODE_DIO) { - DEBUG("RPL: PREFIX INFO DIO option not expected\n"); - return false; - } - - if (opt->length != GNRC_RPL_OPT_PREFIX_INFO_LEN) { - DEBUG("RPL: wrong DIO option (PREFIX INFO) len: %d, expected: %d\n", - opt->length, GNRC_RPL_OPT_PREFIX_INFO_LEN); - return false; - } - break; - - case (GNRC_RPL_OPT_TARGET): - if (msg_type != GNRC_RPL_ICMPV6_CODE_DAO) { - DEBUG("RPL: RPL TARGET DAO option not expected\n"); - return false; - } - - if (opt->length > GNRC_RPL_OPT_TARGET_LEN) { - DEBUG("RPL: wrong DAO option (RPL TARGET) len: %d, expected (max): %d\n", - opt->length, GNRC_RPL_OPT_TARGET_LEN); - return false; - } - break; - - case (GNRC_RPL_OPT_TRANSIT): - if (msg_type != GNRC_RPL_ICMPV6_CODE_DAO) { - DEBUG("RPL: RPL TRANSIT INFO DAO option not expected\n"); - return false; - } - - uint8_t parent_addr = 0; - if (inst->mop == GNRC_RPL_MOP_NON_STORING_MODE) { - parent_addr = sizeof(ipv6_addr_t); - } - - if (opt->length != (GNRC_RPL_OPT_TRANSIT_INFO_LEN + parent_addr)) { - DEBUG("RPL: wrong DAO option (TRANSIT INFO) len: %d, expected: %d\n", - opt->length, (GNRC_RPL_OPT_TRANSIT_INFO_LEN + parent_addr)); - return false; - } - break; - - default: - break; - - } - expected_len += opt->length + sizeof(gnrc_rpl_opt_t); - opt = (gnrc_rpl_opt_t *) (((uint8_t *) (opt + 1)) + opt->length); - } - - if (expected_len == len) { - return true; - } - - DEBUG("RPL: wrong options len: %d, expected: %d\n", len, expected_len); - - return false; -} - /** @todo allow target prefixes in target options to be of variable length */ bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt_t *opt, uint16_t len, ipv6_addr_t *src, uint32_t *included_opts) @@ -398,9 +300,13 @@ bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt_t *opt *included_opts = 0; ipv6_addr_t *me; - if (!_gnrc_rpl_check_options_validity(msg_type, inst, opt, len)) { +#ifndef GNRC_RPL_WITHOUT_VALIDATION + if (!gnrc_rpl_validation_options(msg_type, inst, opt, len)) { return false; } +#else + (void) msg_type; +#endif while(l < len) { switch(opt->type) { @@ -531,27 +437,16 @@ bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt_t *opt return true; } -static bool _gnrc_rpl_check_DIO_validity(gnrc_rpl_dio_t *dio, uint16_t len) -{ - uint16_t expected_len = sizeof(*dio) + sizeof(icmpv6_hdr_t); - - if (expected_len <= len) { - return true; - } - - DEBUG("RPL: wrong DIO len: %d, expected: %d\n", len, expected_len); - - return false; -} - void gnrc_rpl_recv_DIO(gnrc_rpl_dio_t *dio, kernel_pid_t iface, ipv6_addr_t *src, uint16_t len) { gnrc_rpl_instance_t *inst = NULL; gnrc_rpl_dodag_t *dodag = NULL; - if (!_gnrc_rpl_check_DIO_validity(dio, len)) { +#ifndef GNRC_RPL_WITHOUT_VALIDATION + if (!gnrc_rpl_validation_DIO(dio, len)) { return; } +#endif len -= (sizeof(gnrc_rpl_dio_t) + sizeof(icmpv6_hdr_t)); @@ -977,23 +872,6 @@ void gnrc_rpl_send_DAO_ACK(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination, gnrc_rpl_send(pkt, dodag->iface, NULL, destination, &dodag->dodag_id); } -static bool _gnrc_rpl_check_DAO_validity(gnrc_rpl_dao_t *dao, uint16_t len) -{ - uint16_t expected_len = sizeof(*dao) + sizeof(icmpv6_hdr_t); - - if ((dao->k_d_flags & GNRC_RPL_DAO_D_BIT)) { - expected_len += sizeof(ipv6_addr_t); - } - - if (expected_len <= len) { - return true; - } - - DEBUG("RPL: wrong DAO len: %d, expected: %d\n", len, expected_len); - - return false; -} - void gnrc_rpl_recv_DAO(gnrc_rpl_dao_t *dao, kernel_pid_t iface, ipv6_addr_t *src, uint16_t len) { (void)iface; @@ -1001,9 +879,11 @@ void gnrc_rpl_recv_DAO(gnrc_rpl_dao_t *dao, kernel_pid_t iface, ipv6_addr_t *src gnrc_rpl_instance_t *inst = NULL; gnrc_rpl_dodag_t *dodag = NULL; - if (!_gnrc_rpl_check_DAO_validity(dao, len)) { +#ifndef GNRC_RPL_WITHOUT_VALIDATION + if (!gnrc_rpl_validation_DAO(dao, len)) { return; } +#endif gnrc_rpl_opt_t *opts = (gnrc_rpl_opt_t *) (dao + 1); @@ -1052,23 +932,6 @@ void gnrc_rpl_recv_DAO(gnrc_rpl_dao_t *dao, kernel_pid_t iface, ipv6_addr_t *src gnrc_rpl_delay_dao(dodag); } -static bool _gnrc_rpl_check_DAO_ACK_validity(gnrc_rpl_dao_ack_t *dao_ack, uint16_t len) -{ - uint16_t expected_len = sizeof(*dao_ack) + sizeof(icmpv6_hdr_t); - - if ((dao_ack->d_reserved & GNRC_RPL_DAO_ACK_D_BIT)) { - expected_len += sizeof(ipv6_addr_t); - } - - if (expected_len == len) { - return true; - } - - DEBUG("RPL: wrong DAO-ACK len: %d, expected: %d\n", len, expected_len); - - return false; -} - void gnrc_rpl_recv_DAO_ACK(gnrc_rpl_dao_ack_t *dao_ack, kernel_pid_t iface, uint16_t len) { (void)iface; @@ -1076,9 +939,13 @@ void gnrc_rpl_recv_DAO_ACK(gnrc_rpl_dao_ack_t *dao_ack, kernel_pid_t iface, uint gnrc_rpl_instance_t *inst = NULL; gnrc_rpl_dodag_t *dodag = NULL; - if (!_gnrc_rpl_check_DAO_ACK_validity(dao_ack, len)) { +#ifndef GNRC_RPL_WITHOUT_VALIDATION + if (!gnrc_rpl_validation_DAO_ACK(dao_ack, len)) { return; } +#else + (void) len; +#endif if ((inst = gnrc_rpl_instance_get(dao_ack->instance_id)) == NULL) { DEBUG("RPL: DAO-ACK with unknown instance id (%d) received\n", dao_ack->instance_id); diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/validation.h b/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/validation.h new file mode 100644 index 0000000000000000000000000000000000000000..ec649f90fa843584ccab766b369dc927b2e5e832 --- /dev/null +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/validation.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2016 Cenk Gündoğan <mail@cgundogan.de> + * + * 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_gnrc_rpl + * @{ + * + * @file + * @brief RPL control message validation functions + * + * @author Cenk Gündoğan <mail@cgundogan.de> + */ + +#ifndef GNRC_RPL_VALIDATION_H_ +#define GNRC_RPL_VALIDATION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/gnrc/rpl/structs.h" +#include "net/gnrc/icmpv6.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Checks validity of DIS control messages + * + * @param[in] dis The DIS control message + * @param[in] len Length of the DIS control message + * + * @return true, if @p dis is valid + * @return false, otherwise + */ +static inline bool gnrc_rpl_validation_DIS(gnrc_rpl_dis_t *dis, uint16_t len) +{ + uint16_t expected_len = sizeof(*dis) + sizeof(icmpv6_hdr_t); + + if (expected_len <= len) { + return true; + } + + DEBUG("RPL: wrong DIS len: %d, expected: %d\n", len, expected_len); + + return false; +} + +/** + * @brief Checks validity of control message options + * + * @param[in] msg_type Type of the control message + * @param[in] inst The RPL instance + * @param[in] opt Options of the control message + * @param[in] len Length of the options + * + * @return true, if @p opt is valid + * @return false, otherwise + */ +bool gnrc_rpl_validation_options(int msg_type, gnrc_rpl_instance_t *inst, + gnrc_rpl_opt_t *opt, uint16_t len); + +/** + * @brief Checks validity of DIO control messages + * + * @param[in] dio The DIO control message + * @param[in] len Length of the DIO control message + * + * @return true, if @p dio is valid + * @return false, otherwise + */ +static inline bool gnrc_rpl_validation_DIO(gnrc_rpl_dio_t *dio, uint16_t len) +{ + uint16_t expected_len = sizeof(*dio) + sizeof(icmpv6_hdr_t); + + if (expected_len <= len) { + return true; + } + + DEBUG("RPL: wrong DIO len: %d, expected: %d\n", len, expected_len); + + return false; +} + +/** + * @brief Checks validity of DAO control messages + * + * @param[in] dao The DAO control message + * @param[in] len Length of the DAO control message + * + * @return true, if @p dao is valid + * @return false, otherwise + */ +static inline bool gnrc_rpl_validation_DAO(gnrc_rpl_dao_t *dao, uint16_t len) +{ + uint16_t expected_len = sizeof(*dao) + sizeof(icmpv6_hdr_t); + + if ((dao->k_d_flags & GNRC_RPL_DAO_D_BIT)) { + expected_len += sizeof(ipv6_addr_t); + } + + if (expected_len <= len) { + return true; + } + + DEBUG("RPL: wrong DAO len: %d, expected: %d\n", len, expected_len); + + return false; +} + +/** + * @brief Checks validity of DAO-ACK control messages + * + * @param[in] dao_ack The DAO-ACK control message + * @param[in] len Length of the DAO-ACK control message + * + * @return true, if @p dao_ack is valid + * @return false, otherwise + */ +static inline bool gnrc_rpl_validation_DAO_ACK(gnrc_rpl_dao_ack_t *dao_ack, uint16_t len) +{ + uint16_t expected_len = sizeof(*dao_ack) + sizeof(icmpv6_hdr_t); + + if ((dao_ack->d_reserved & GNRC_RPL_DAO_ACK_D_BIT)) { + expected_len += sizeof(ipv6_addr_t); + } + + if (expected_len == len) { + return true; + } + + DEBUG("RPL: wrong DAO-ACK len: %d, expected: %d\n", len, expected_len); + + return false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_RPL_VALIDATION_H_ */ +/** @} */ diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_validation.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_validation.c new file mode 100644 index 0000000000000000000000000000000000000000..43ef51f10dcefd72ff007cb7d5434ceea7ac0a6d --- /dev/null +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_validation.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Cenk Gündoğan <mail@cgundogan.de> + * + * 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 <mail@cgundogan.de> + */ + +#include "net/gnrc/icmpv6.h" +#include "net/gnrc/ipv6.h" + +#include "net/gnrc/rpl.h" +#include "gnrc_rpl_internal/validation.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +bool gnrc_rpl_validation_options(int msg_type, gnrc_rpl_instance_t *inst, + gnrc_rpl_opt_t *opt, uint16_t len) +{ + uint16_t expected_len = 0; + + while(expected_len < len) { + switch(opt->type) { + case (GNRC_RPL_OPT_PAD1): + expected_len += 1; + opt = (gnrc_rpl_opt_t *) (((uint8_t *) opt) + 1); + continue; + + case (GNRC_RPL_OPT_DODAG_CONF): + if (msg_type != GNRC_RPL_ICMPV6_CODE_DIO) { + DEBUG("RPL: DODAG CONF DIO option not expected\n"); + return false; + } + + if (opt->length != GNRC_RPL_OPT_DODAG_CONF_LEN) { + DEBUG("RPL: wrong DIO option (DODAG CONF) len: %d, expected: %d\n", + opt->length, GNRC_RPL_OPT_DODAG_CONF_LEN); + return false; + } + break; + + case (GNRC_RPL_OPT_PREFIX_INFO): + if (msg_type != GNRC_RPL_ICMPV6_CODE_DIO) { + DEBUG("RPL: PREFIX INFO DIO option not expected\n"); + return false; + } + + if (opt->length != GNRC_RPL_OPT_PREFIX_INFO_LEN) { + DEBUG("RPL: wrong DIO option (PREFIX INFO) len: %d, expected: %d\n", + opt->length, GNRC_RPL_OPT_PREFIX_INFO_LEN); + return false; + } + break; + + case (GNRC_RPL_OPT_TARGET): + if (msg_type != GNRC_RPL_ICMPV6_CODE_DAO) { + DEBUG("RPL: RPL TARGET DAO option not expected\n"); + return false; + } + + if (opt->length > GNRC_RPL_OPT_TARGET_LEN) { + DEBUG("RPL: wrong DAO option (RPL TARGET) len: %d, expected (max): %d\n", + opt->length, GNRC_RPL_OPT_TARGET_LEN); + return false; + } + break; + + case (GNRC_RPL_OPT_TRANSIT): + if (msg_type != GNRC_RPL_ICMPV6_CODE_DAO) { + DEBUG("RPL: RPL TRANSIT INFO DAO option not expected\n"); + return false; + } + + uint8_t parent_addr = 0; + if (inst->mop == GNRC_RPL_MOP_NON_STORING_MODE) { + parent_addr = sizeof(ipv6_addr_t); + } + + if (opt->length != (GNRC_RPL_OPT_TRANSIT_INFO_LEN + parent_addr)) { + DEBUG("RPL: wrong DAO option (TRANSIT INFO) len: %d, expected: %d\n", + opt->length, (GNRC_RPL_OPT_TRANSIT_INFO_LEN + parent_addr)); + return false; + } + break; + + default: + break; + + } + expected_len += opt->length + sizeof(gnrc_rpl_opt_t); + opt = (gnrc_rpl_opt_t *) (((uint8_t *) (opt + 1)) + opt->length); + } + + if (expected_len == len) { + return true; + } + + DEBUG("RPL: wrong options len: %d, expected: %d\n", len, expected_len); + + return false; +} + +/** + * @} + */