1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-28 16:01:18 +01:00

rpl: add DIS Solicited Information option

This commit is contained in:
BytesGalore 2017-04-18 09:52:34 +02:00
parent 42d58373f5
commit 0c034c9220
5 changed files with 237 additions and 64 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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
};
/* 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
};
int size = sizeof(icmpv6_hdr_t) + sizeof(gnrc_rpl_dis_t) + sizeof(padding);
if ((pkt = gnrc_icmpv6_build(NULL, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DIS, size)) == NULL) {
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 ((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
}

View File

@ -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