Exemplo n.º 1
0
bool rpl_data_remember_outer(buffer_t *buf)
{
    /* We're stripping the IP header - need the HBH header for future reference */
     uint8_t *hbh;
     rpl_data_locate_info(buf, &hbh, NULL);
     if (hbh) {
         uint8_t instance_id = hbh[3];
         /* For local instances, also need to extract the DODAG ID from src/dst */
         bool local = rpl_instance_id_is_local(instance_id);
         /* Copy the length byte and the option data (and optionally DODAG ID) */
         buf->rpl_option = ns_dyn_mem_temporary_alloc(hbh[1] + 1 + (local ? 16 : 0));
         if (buf->rpl_option) {
             memcpy(buf->rpl_option, hbh + 1, hbh[1] + 1);
             if (local) {
                 uint8_t *dodagid = instance_id & RPL_INSTANCE_DEST ? buf->dst_sa.address : buf->src_sa.address;
                 memcpy(buf->rpl_option + hbh[1] + 1, dodagid, 16);
             }
         }
     }

     if ((buf->options.ip_extflags & IPEXT_HBH_RPL) && !buf->rpl_option) {
         tr_warn("RPL tunnel exit HbH fail");
         return false;
     }

     return true;
}
Exemplo n.º 2
0
uint8_t rpl_instance_list_read(uint8_t *buffer_ptr, uint8_t buffer_size)
{
    uint8_t ret_val = 0;
    struct rpl_instance *instance = NULL;

    if (!protocol_6lowpan_rpl_domain) {
        return 0;
    }

    while (buffer_size) {
        instance = rpl_control_enumerate_instances(protocol_6lowpan_rpl_domain, instance);
        if (!instance) {
            break;
        }
        rpl_dodag_info_t dodag_info;
        if (rpl_control_read_dodag_info(instance, &dodag_info)) {
            /* First the instance ID */
            *buffer_ptr++ = dodag_info.instance_id;
            buffer_size--;
            /* Then, if local, the DODAG ID */
            if (rpl_instance_id_is_local(dodag_info.instance_id)) {
                if (buffer_size < 16) {
                    break;
                }
                memcpy(buffer_ptr, dodag_info.dodag_id, 16);
                buffer_ptr += 16;
                buffer_size -= 16;
            }
            ret_val++;
        }
    }

    return ret_val;
}
Exemplo n.º 3
0
/*
 *   0                   1                   2                   3
 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 *                                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *                                  |  Option Type  |  Opt Data Len |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |O|R|F|0|0|0|0|0| RPLInstanceID |          SenderRank           |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |                         (sub-TLVs)                            |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *                     Figure 1: RPL Option
 */
static buffer_t *rpl_data_exthdr_provider_hbh_2(buffer_t *buf, rpl_instance_t *instance, rpl_neighbour_t *neighbour, ipv6_exthdr_stage_t stage, int16_t *result)
{
    ipv6_route_info_t *route_info = &buf->route->route_info;

    /* This can be called both for routes which only use HbH headers (eg DIO)
     * as well as one-hop DAO_SR routes which would normally use source routing
     * headers, if there was more than one hop. For DAO_SR, neighbour will be
     * NULL.
     */

    rpl_dodag_t *dodag = rpl_instance_current_dodag(instance);
    if (!dodag) {
        *result = -1;
        return buf;
    }

    bool destination_in_instance = false;
    uint16_t ext_size = 0;
    if (addr_ipv6_equal(route_info->next_hop_addr, buf->dst_sa.address) ||
        addr_ipv6_equal(buf->dst_sa.address, dodag->id)) {
        destination_in_instance = true;

        if (buf->rpl_option) {
            /* Forwarding an existing option - preserve it */
            uint8_t opt_size = buf->rpl_option[0];
            ext_size = 2 + opt_size;
            ext_size = (ext_size + 7) &~ 7;
        } else {
            /* Generating our own option - fixed size, no TLVs */
            ext_size = 8;
        }
    }

    switch (stage) {
        case IPV6_EXTHDR_SIZE:
            *result = ext_size;
            return buf;

        case IPV6_EXTHDR_INSERT: {
            if (!destination_in_instance) {
                /* We don't add a header - we'll do it on the tunnel */
                *result = 0;
                return buf;
            }
            buf = buffer_headroom(buf, ext_size);
            if (!buf) {
                return NULL;
            }
            uint8_t *ext = buffer_data_reserve_header(buf, ext_size);
            ext[0] = buf->options.type;
            buf->options.type = IPV6_NH_HOP_BY_HOP;
            ext[1] = ext_size / 8 - 1;
            uint8_t *opt = ext + 2;
            opt[0] = IPV6_OPTION_RPL;
            if (buf->rpl_option) {
                /* Get back the RPL option we stripped off an outer IP header */
                memcpy(opt + 1, buf->rpl_option, 1 + buf->rpl_option[0]);
                ns_dyn_mem_free(buf->rpl_option);
                buf->rpl_option = NULL;
            } else {
                opt[1] = 4; // option length
                opt[2] = 0; // placeholder
                opt[3] = instance->id;
                /* For upwards routes we can deduce that DODAGID must be
                 * the destination, so set the D flag.
                 */
                if (rpl_instance_id_is_local(instance->id) && !rpl_data_is_rpl_downward_route(route_info->source)) {
                    opt[3] |= RPL_INSTANCE_DEST;
                }
                common_write_16_bit(RPL_RANK_INFINITE, opt + 4); // SenderRank (placeholder)
            }
            /* Pad HbH header if necessary. */
            uint8_t pad_len = ext + ext_size - (opt + 2 + opt[1]);
            if (pad_len == 1) {
                opt[0] = IPV6_OPTION_PAD1;
            } else if (pad_len > 1) {
                opt[0] = IPV6_OPTION_PADN;
                opt[1] = pad_len - 2;
                memset(opt + 2, 0, pad_len - 2);
            }
            // don't forget to set the "RPL option present" marker
            buf->options.ip_extflags |= IPEXT_HBH_RPL;
            *result = 0;
            return buf;
        }

        case IPV6_EXTHDR_MODIFY: {
            uint8_t *opt;
            uint16_t sender_rank;

            rpl_data_locate_info(buf, &opt, NULL);
            if (!opt) {
                *result = IPV6_EXTHDR_MODIFY_TUNNEL;
                // Tunnel to next hop in general case, but if going to DODAGID,
                // it can tunnel all the way (and it HAS to if it is a local
                // DODAG).
                if (!addr_ipv6_equal(buf->dst_sa.address, dodag->id)) {
                    memcpy(buf->dst_sa.address, route_info->next_hop_addr, 16);
                }
                buf->src_sa.addr_type = ADDR_NONE; // force auto-selection
                return buf;
            }

            if (buf->ip_routed_up) {
                /* Check for rank errors - RFC 6550 11.2.2.2. */
                /* Note that RPL spec does not say that packets from nodes of
                 * equal rank are errors, but we treat them as such to get
                 * reliable sibling loop detection - we require sender rank to be
                 * strictly less for Down packets and strictly greater for Up.
                 */
                sender_rank = common_read_16_bit(opt + 4);
                rpl_cmp_t cmp = rpl_rank_compare_dagrank_rank(dodag, sender_rank, instance->current_rank);
                rpl_cmp_t expected_cmp = (opt[2] & RPL_OPT_DOWN) ? RPL_CMP_LESS : RPL_CMP_GREATER;
                if (cmp != expected_cmp) {
                    /* Set the Rank-Error bit; if already set, drop */
                    if (opt[2] & RPL_OPT_RANK_ERROR) {
                        protocol_stats_update(STATS_RPL_ROUTELOOP, 1);
                        tr_info("Forwarding inconsistency R");
                        rpl_instance_inconsistency(instance);
                        *result = -1;
                        return buf;
                    } else {
                        opt[2] |= RPL_OPT_RANK_ERROR;
                    }
                }
            }

            if (buf->rpl_flag_error & RPL_OPT_FWD_ERROR) {
                opt[2] |= RPL_OPT_FWD_ERROR;
            } else if (rpl_data_is_rpl_downward_route(route_info->source)) {
                opt[2] |= RPL_OPT_DOWN;
            } else {
                opt[2] &= ~RPL_OPT_DOWN;
            }

            /* Set the D flag for local instances */
            if (rpl_instance_id_is_local(instance->id)) {
                if (addr_ipv6_equal(dodag->id, buf->dst_sa.address)) {
                    opt[3] |= RPL_INSTANCE_DEST;
                } else if (addr_ipv6_equal(dodag->id, buf->src_sa.address)) {
                    opt[3] &=~ RPL_INSTANCE_DEST;
                } else {
                    tr_error("Local instance invalid %s[%d]: %s -> %s", trace_ipv6(dodag->id), instance->id, trace_ipv6(buf->src_sa.address), trace_ipv6(buf->dst_sa.address));
                    *result = -1;
                    return buf;
                }
            }

            /* RPL 11.2.2.2. says we set SenderRank to infinite when forwarding
             * across a version discontinuity. (Must be up - we don't know versions
             * of downward routes).
             */
            if ((buf->rpl_flag_error & RPL_OPT_FWD_ERROR) || rpl_data_is_rpl_downward_route(route_info->source) || !neighbour || neighbour->dodag_version == instance->current_dodag_version) {
                sender_rank = nrpl_dag_rank(dodag, instance->current_rank);
            } else {
                sender_rank = RPL_RANK_INFINITE;
            }
            common_write_16_bit(sender_rank, opt + 4);
            *result = 0;
            return buf;
        }
        default:
            return buffer_free(buf);
    }
}