diff --git a/sys/include/net/gnrc/rpl.h b/sys/include/net/gnrc/rpl.h index 35a1bf463031aee2093490bb36315d60cb918c06..5fb31cba4a9da0f5e2be235ea0c48de0c9ccb24c 100644 --- a/sys/include/net/gnrc/rpl.h +++ b/sys/include/net/gnrc/rpl.h @@ -450,6 +450,19 @@ static inline bool GNRC_RPL_COUNTER_GREATER_THAN(uint8_t A, uint8_t B) #define GNRC_RPL_INSTANCE_D_FLAG_MASK (1 << 6) /** @} */ +/** + * @brief DIS Solicited Information option (numbers) + * @see <a href="https://tools.ietf.org/html/rfc6550#section-6.7.9"> + * RFC6550, section 6.7.9, Solicited Information + * </a> + * @{ + */ +#define GNRC_RPL_DIS_SOLICITED_INFO_LENGTH (19) +#define GNRC_RPL_DIS_SOLICITED_INFO_FLAG_V (1 << 7) +#define GNRC_RPL_DIS_SOLICITED_INFO_FLAG_I (1 << 6) +#define GNRC_RPL_DIS_SOLICITED_INFO_FLAG_D (1 << 5) +/** @} */ + /** * @brief PID of the RPL thread. */ @@ -513,8 +526,11 @@ void gnrc_rpl_send_DIO(gnrc_rpl_instance_t *instance, ipv6_addr_t *destination); * * @param[in] instance Pointer to the RPL instance, optional. * @param[in] destination IPv6 addres of the destination. + * @param[in] options Pointer to the first option to be attached. + * @param[in] num_opts The number of options to attach. */ -void gnrc_rpl_send_DIS(gnrc_rpl_instance_t *instance, ipv6_addr_t *destination); +void gnrc_rpl_send_DIS(gnrc_rpl_instance_t *instance, ipv6_addr_t *destination, + gnrc_rpl_internal_opt_t **options, size_t num_opts); /** * @brief Send a DAO of the @p dodag to the @p destination. @@ -530,7 +546,7 @@ void gnrc_rpl_send_DAO(gnrc_rpl_instance_t *instance, ipv6_addr_t *destination, * * @param[in] instance Pointer to the RPL instance. * @param[in] destination IPv6 addres of the destination. - * @param[in] seq Sequence number to be acknowledged. + * @param[in] seq Sequence number to be acknowledged. */ void gnrc_rpl_send_DAO_ACK(gnrc_rpl_instance_t *instance, ipv6_addr_t *destination, uint8_t seq); diff --git a/sys/include/net/gnrc/rpl/structs.h b/sys/include/net/gnrc/rpl/structs.h index f0e4118e9e2abe31d51a89acbe90b9f04887401e..bb56a80f6b35884d39d91a97a0c46c4b5b4d97f8 100644 --- a/sys/include/net/gnrc/rpl/structs.h +++ b/sys/include/net/gnrc/rpl/structs.h @@ -130,6 +130,21 @@ typedef struct __attribute__((packed)) { uint8_t reserved; /**< reserved */ } gnrc_rpl_dis_t; +/** + * @brief DIS Solicited Information option + * @see <a href="https://tools.ietf.org/html/rfc6550#section-6.7.9"> + * RFC6550, section 6.7.9, Solicited Information + * </a> + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< Option Type: 0x07 */ + uint8_t length; /**< Option Length: 19 bytes*/ + uint8_t instance_id; /**< id of the instance */ + uint8_t VID_flags; /**< V|I|D predicate options followed by 5 bit unused flags */ + ipv6_addr_t dodag_id; /**< DODAG ID predicate */ + uint8_t version_number; /**< version number of the DODAG */ +} gnrc_rpl_opt_dis_solicited_t; + /** * @brief Destination Advertisement Object * @see <a href="https://tools.ietf.org/html/rfc6550#section-6.4"> @@ -315,6 +330,26 @@ struct gnrc_rpl_instance { * @endcond */ +/** + * @brief internal unpacked struct type for option insertion + */ +typedef struct { + uint8_t type; /**< Option Type */ + uint8_t length; /**< Option Length, does not include the first two byte */ +} gnrc_rpl_internal_opt_t; + +/** + * @brief internal unpacked struct type for DIS solicited option insertion + */ +typedef struct { + uint8_t type; /**< Option Type: 0x07 */ + uint8_t length; /**< Option Length: 19 bytes*/ + uint8_t instance_id; /**< id of the instance */ + uint8_t VID_flags; /**< V|I|D predicate options followed by 5 bit unused flags */ + ipv6_addr_t dodag_id; /**< DODAG ID predicate */ + uint8_t version_number; /**< version number of the DODAG */ +} gnrc_rpl_internal_opt_dis_solicited_t; + #ifdef __cplusplus } #endif diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl.c b/sys/net/gnrc/routing/rpl/gnrc_rpl.c index a82a166c01ae90b3aa23ffd6efb6288e6a7e853b..a6a0b2a96cafa1e750ecd9c7b49f6b5b7ab76f4d 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl.c @@ -97,7 +97,7 @@ kernel_pid_t gnrc_rpl_init(kernel_pid_t if_pid) gnrc_netif_ipv6_group_join_internal(gnrc_netif_get_by_pid(if_pid), &ipv6_addr_all_rpl_nodes); - gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes); + gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes, NULL, 0); return gnrc_rpl_pid; } @@ -224,7 +224,7 @@ static void _parent_timeout(gnrc_rpl_parent_t *parent) if ((parent->state >= GNRC_RPL_PARENT_STALE) && (parent->state < GNRC_RPL_PARENT_TIMEOUT)) { parent->state++; - gnrc_rpl_send_DIS(parent->dodag->instance, &parent->addr); + gnrc_rpl_send_DIS(parent->dodag->instance, &parent->addr, NULL, 0); } else { gnrc_rpl_dodag_t *dodag = parent->dodag; 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 1e454ecbe2df2f15c384cdd4969c7237402377e8..bf7d30ff75e1127015648ff01c6e30317048ee4c 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c @@ -149,6 +149,32 @@ gnrc_pktsnip_t *_dio_dodag_conf_build(gnrc_pktsnip_t *pkt, gnrc_rpl_dodag_t *dod return opt_snip; } +gnrc_pktsnip_t *_dis_solicited_opt_build(gnrc_pktsnip_t *pkt, gnrc_rpl_internal_opt_dis_solicited_t *opt) +{ + gnrc_pktsnip_t *opt_snip; + size_t snip_size = sizeof(gnrc_rpl_opt_dis_solicited_t); + + if ((opt_snip = gnrc_pktbuf_add(pkt, NULL, snip_size, + GNRC_NETTYPE_UNDEF)) == NULL) { + DEBUG("RPL: BUILD SOLICITED OPT - no space left in packet buffer\n"); + gnrc_pktbuf_release(pkt); + return NULL; + } + + gnrc_rpl_opt_dis_solicited_t* solicited_information; + solicited_information = opt_snip->data; + + solicited_information->type = GNRC_RPL_OPT_SOLICITED_INFO; + solicited_information->length = GNRC_RPL_DIS_SOLICITED_INFO_LENGTH; + solicited_information->instance_id = opt->instance_id; + + solicited_information->VID_flags = opt->VID_flags; + solicited_information->dodag_id = opt->dodag_id; + solicited_information->version_number = opt->version_number; + + return opt_snip; +} + #ifndef GNRC_RPL_WITHOUT_PIO static bool _get_pl_entry(unsigned iface, ipv6_addr_t *pfx, unsigned pfx_len, gnrc_ipv6_nib_pl_t *ple) @@ -279,34 +305,68 @@ void gnrc_rpl_send_DIO(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination) gnrc_rpl_send(pkt, dodag->iface, NULL, destination, &dodag->dodag_id); } -void gnrc_rpl_send_DIS(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination) +void gnrc_rpl_send_DIS(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination, + gnrc_rpl_internal_opt_t **options, size_t num_opts) { - gnrc_pktsnip_t *pkt; + gnrc_pktsnip_t *pkt = NULL, *tmp; icmpv6_hdr_t *icmp; gnrc_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 */ - uint8_t padding[] = { - 0x01, 0x02, 0x00, 0x00 - }; - - int size = sizeof(icmpv6_hdr_t) + sizeof(gnrc_rpl_dis_t) + sizeof(padding); + /* No options provided to be attached to the DIS, so we PadN 2 bytes */ + if (options == NULL || num_opts == 0) { + assert(!options); + gnrc_pktsnip_t *opt_snip; + size_t snip_size = 0; + /* The DIS is too small so that wireshark complains about an incorrect + * ethernet frame check sequence. + * To trick it we PadN 2 additional bytes, i.e. 4 bytes in sum. */ + uint8_t padding[] = { + GNRC_RPL_OPT_PADN, /* Option Type */ + 0x02, /* Number of extra padding bytes */ + 0x00, 0x00 + }; + + snip_size = sizeof(padding); + if ((opt_snip = gnrc_pktbuf_add(NULL, NULL, snip_size, + GNRC_NETTYPE_UNDEF)) == NULL) { + DEBUG("RPL: BUILD PadN OPT - no space left in packet buffer\n"); + gnrc_pktbuf_release(pkt); + return; + } + memcpy(opt_snip->data, padding, snip_size); + pkt = opt_snip; + } + else { + assert(options); + for (size_t i = 0; i < num_opts; ++i) { + if (options[i]->type == GNRC_RPL_OPT_SOLICITED_INFO) { + if ((pkt = _dis_solicited_opt_build(pkt, + (gnrc_rpl_internal_opt_dis_solicited_t*)options[i])) == NULL) { + return; + } + } + } + } + if ((tmp = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_dis_t), GNRC_NETTYPE_UNDEF)) == NULL) { + DEBUG("RPL: Send DIS - no space left in packet buffer\n"); + gnrc_pktbuf_release(pkt); + return; + } + pkt = tmp; - if ((pkt = gnrc_icmpv6_build(NULL, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DIS, size)) == NULL) { + if ((tmp = gnrc_icmpv6_build(pkt, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DIS, + sizeof(icmpv6_hdr_t))) == NULL) { DEBUG("RPL: Send DIS - no space left in packet buffer\n"); + gnrc_pktbuf_release(pkt); return; } + pkt = tmp; icmp = (icmpv6_hdr_t *)pkt->data; dis = (gnrc_rpl_dis_t *)(icmp + 1); dis->flags = 0; dis->reserved = 0; - /* TODO add padding may be removed if packet size grows */ - memcpy((dis + 1), padding, sizeof(padding)); - #ifdef MODULE_NETSTATS_RPL gnrc_rpl_netstats_tx_DIS(&gnrc_rpl_netstats, gnrc_pkt_len(pkt), (destination && !ipv6_addr_is_multicast(destination))); @@ -315,49 +375,6 @@ 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)); } -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; - (void)dis; - (void)len; - -#ifdef MODULE_NETSTATS_RPL - gnrc_rpl_netstats_rx_DIS(&gnrc_rpl_netstats, len, (dst && !ipv6_addr_is_multicast(dst))); -#endif - -#ifndef GNRC_RPL_WITHOUT_VALIDATION - if (!gnrc_rpl_validation_DIS(dis, len)) { - return; - } -#endif - - if (ipv6_addr_is_multicast(dst)) { - for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { - if ((gnrc_rpl_instances[i].state != 0) - /* a leaf node should only react to unicast DIS */ - && (gnrc_rpl_instances[i].dodag.node_status != GNRC_RPL_LEAF_NODE)) { -#ifdef MODULE_GNRC_RPL_P2P - if (gnrc_rpl_instances[i].mop == GNRC_RPL_P2P_MOP) { - DEBUG("RPL: Not responding to DIS for P2P-RPL DODAG\n"); - continue; - } -#endif - trickle_reset_timer(&(gnrc_rpl_instances[i].dodag.trickle)); - } - } - } - else { - for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { - if (gnrc_rpl_instances[i].state != 0) { - gnrc_rpl_instances[i].dodag.dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF; - gnrc_rpl_send_DIO(&gnrc_rpl_instances[i], src); - } - } - } -} - static inline uint32_t _sec_to_ms(uint32_t sec) { if (sec == UINT32_MAX) { @@ -455,7 +472,39 @@ bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt_t *opt _sec_to_ms(byteorder_ntohl(pi->pref_lifetime))); break; + case (GNRC_RPL_OPT_SOLICITED_INFO): + DEBUG("RPL: RPL SOLICITED INFO option parsed\n"); + *included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_SOLICITED_INFO; + gnrc_rpl_opt_dis_solicited_t* sol = (gnrc_rpl_opt_dis_solicited_t *) opt; + + /* check expected length */ + if (sol->length != GNRC_RPL_DIS_SOLICITED_INFO_LENGTH) { + DEBUG("RPL: RPL SOLICITED INFO option, unexpected length: %d\n", sol->length); + return false; + } + + /* check the DODAG Version */ + if ((sol->VID_flags & GNRC_RPL_DIS_SOLICITED_INFO_FLAG_V) + && (sol->version_number != inst->dodag.version)) { + DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: DODAG Version mismatch\n"); + return false; + } + /* check the Instance ID */ + if ((sol->VID_flags & GNRC_RPL_DIS_SOLICITED_INFO_FLAG_I) + && (sol->instance_id != inst->id)) { + DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: InstanceID mismatch\n"); + return false; + } + + /* check the DODAG ID */ + if (sol->VID_flags & GNRC_RPL_DIS_SOLICITED_INFO_FLAG_D) { + if (memcmp(&sol->dodag_id, &inst->dodag.dodag_id, sizeof(ipv6_addr_t)) != 0) { + DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: DODAGID mismatch\n"); + return false; + } + } + break; case (GNRC_RPL_OPT_TARGET): DEBUG("RPL: RPL TARGET DAO option parsed\n"); *included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_TARGET; @@ -517,6 +566,53 @@ bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt_t *opt return true; } +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) +{ + (void)iface; + +#ifdef MODULE_NETSTATS_RPL + gnrc_rpl_netstats_rx_DIS(&gnrc_rpl_netstats, len, (dst && !ipv6_addr_is_multicast(dst))); +#endif + +#ifndef GNRC_RPL_WITHOUT_VALIDATION + if (!gnrc_rpl_validation_DIS(dis, len)) { + return; + } +#endif + + if (ipv6_addr_is_multicast(dst)) { + for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { + if ((gnrc_rpl_instances[i].state != 0) + /* a leaf node should only react to unicast DIS */ + && (gnrc_rpl_instances[i].dodag.node_status != GNRC_RPL_LEAF_NODE)) { +#ifdef MODULE_GNRC_RPL_P2P + if (gnrc_rpl_instances[i].mop == GNRC_RPL_P2P_MOP) { + DEBUG("RPL: Not responding to DIS for P2P-RPL DODAG\n"); + continue; + } +#endif + trickle_reset_timer(&(gnrc_rpl_instances[i].dodag.trickle)); + } + } + } + else { + for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { + if (gnrc_rpl_instances[i].state != 0) { + + uint32_t included_opts = 0; + if(!_parse_options(GNRC_RPL_ICMPV6_CODE_DIS, &gnrc_rpl_instances[i], + (gnrc_rpl_opt_t *)(dis + 1), len, src, &included_opts)) { + DEBUG("RPL: DIS option parsing error - skip processing the DIS\n"); + continue; + } + gnrc_rpl_instances[i].dodag.dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF; + gnrc_rpl_send_DIO(&gnrc_rpl_instances[i], src); + } + } + } +} + void gnrc_rpl_recv_DIO(gnrc_rpl_dio_t *dio, kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst, uint16_t len) { @@ -590,11 +686,11 @@ void gnrc_rpl_recv_DIO(gnrc_rpl_dio_t *dio, kernel_pid_t iface, ipv6_addr_t *src #ifndef GNRC_RPL_DODAG_CONF_OPTIONAL_ON_JOIN DEBUG("RPL: DIO without DODAG_CONF option - remove DODAG and request new DIO\n"); gnrc_rpl_instance_remove(inst); - gnrc_rpl_send_DIS(NULL, src); + gnrc_rpl_send_DIS(NULL, src, NULL, 0); return; #else DEBUG("RPL: DIO without DODAG_CONF option - use default trickle parameters\n"); - gnrc_rpl_send_DIS(NULL, src); + gnrc_rpl_send_DIS(NULL, src, NULL, 0); #endif } diff --git a/sys/shell/commands/sc_gnrc_rpl.c b/sys/shell/commands/sc_gnrc_rpl.c index b16d533de5e6d2414b3ad21411109fff18358942..c592c5a6b8573872c6161952151b3aedea239b54 100644 --- a/sys/shell/commands/sc_gnrc_rpl.c +++ b/sys/shell/commands/sc_gnrc_rpl.c @@ -171,9 +171,31 @@ int _gnrc_rpl_trickle_start(char *arg1) return 0; } +int _gnrc_rpl_send_dis_w_sol_opt(char* VID, char* version, char* instance, char* dodag) +{ + uint8_t VID_flags = atoi(VID); + uint8_t version_number = atoi(version); + uint8_t instance_id = atoi(instance); + + gnrc_rpl_internal_opt_dis_solicited_t sol; + sol.type = GNRC_RPL_OPT_SOLICITED_INFO; + sol.length = GNRC_RPL_DIS_SOLICITED_INFO_LENGTH; + sol.VID_flags = htons(VID_flags); + sol.version_number = version_number; + sol.instance_id = instance_id; + + if (ipv6_addr_from_str(&sol.dodag_id, dodag)) + { + gnrc_rpl_internal_opt_t* opt[] = {(gnrc_rpl_internal_opt_t*)&sol}; + gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes, opt, 1); + puts("success: send a DIS with SOL option\n"); + } + return 0; +} + int _gnrc_rpl_send_dis(void) { - gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes); + gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes, NULL, 0); puts("success: send a DIS\n"); return 0; @@ -372,6 +394,9 @@ int _gnrc_rpl(int argc, char **argv) if ((argc == 3) && (strcmp(argv[2], "dis") == 0)) { return _gnrc_rpl_send_dis(); } + if ((argc == 7) && (strcmp(argv[2], "dis") == 0)) { + return _gnrc_rpl_send_dis_w_sol_opt(argv[3], argv[4], argv[5], argv[6]); + } } else if (strcmp(argv[1], "leaf") == 0) { if (argc == 3) { @@ -423,6 +448,7 @@ int _gnrc_rpl(int argc, char **argv) puts("* root <inst_id> <dodag_id>\t\t- add a dodag to a new or existing instance"); puts("* router <instance_id>\t\t\t- operate as router in the instance"); puts("* send dis\t\t\t\t- send a multicast DIS"); + puts("* send dis <VID_flags> <version> <instance_id> <dodag_id> - send a multicast DIS with SOL option"); #ifndef GNRC_RPL_WITHOUT_PIO puts("* set pio <on/off> <instance_id>\t- (de-)activate PIO transmissions in DIOs"); #endif