You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Contiki-NG RPL非存储模式下如何通过代码获取DODAG根节点路由

Contiki-NG非存储模式RPL根节点原生获取全网路由信息实现方案

非存储模式下RPL根节点本身会维护全网所有节点的源路由转发表,不需要额外开发节点上报、全网探测逻辑,直接调用内核已有接口即可读取所有直连、多跳可达节点信息,和官方shell命令获取的结果完全一致。


前置依赖

在根节点业务代码中引入以下头文件:

#include "net/routing/rpl-classic/rpl.h"
#include "net/ipv6/uip-sr.h"
#include "net/ipv6/uiplib.h"

如果你工程中使用的是RPL Lite而非RPL Classic,把rpl头文件路径替换为net/routing/rpl-lite/rpl.h即可,源路由模块接口两个版本通用。
编译前确认配置宏UIP_CONF_IPV6_SR值为1,Contiki-NG非存储RPL默认配置已开启该选项,无需额外修改。


核心实现逻辑

1. 校验当前节点DODAG身份

先判断当前节点是否为DODAG根,非根节点没有全网路由信息,直接终止逻辑:

rpl_dag_t *cur_dag = rpl_get_any_dag();
if(cur_dag == NULL || cur_dag->rank != ROOT_RANK(cur_dag->instance)) {
  return; // 非根节点,无全网路由信息
}

2. 遍历源路由表读取所有可达节点

非存储模式下全网节点的路由条目统一存储在uip_sr模块维护的全局链表中,和shellrpl-nodes命令读取的是同一份数据源,遍历示例:

struct uip_sr_link *sr_entry;
printf("=== 全网RPL可达节点列表 ===\n");
printf("%-32s %-6s %s\n", "节点IPv6地址", "跳数", "路径父节点");

for(sr_entry = uip_sr_head(); sr_entry != NULL; sr_entry = sr_entry->next) {
  // 跳过根节点自身的路由条目
  if(uip_ipaddr_cmp(&sr_entry->dest, &cur_dag->dag_id)) {
    continue;
  }

  char ip_str[48] = {0};
  char parent_ip_str[48] = {0};
  uip_ipaddr_sprintf(ip_str, &sr_entry->dest);

  // 判断是否为直连节点
  if(sr_entry->from == NULL) {
    snprintf(parent_ip_str, sizeof(parent_ip_str), "直连(根节点一跳可达)");
  } else {
    uip_ipaddr_sprintf(parent_ip_str, &sr_entry->from->dest);
  }

  printf("%-32s %-6u %s\n", ip_str, sr_entry->cost, parent_ip_str);
}

遍历拿到的sr_entry->cost字段就是根到该节点的跳数,sr_entry->from字段存储该节点往根方向的上一跳节点地址,可直接用来判断连通性、绘制网络拓扑

3. 可选:监听节点上下线实时事件

如果不需要定时轮询路由表,可以注册RPL路由变更回调,节点加入/离网时自动触发处理逻辑:

// 节点入网回调
static void node_join_cb(rpl_dag_t *dag, const uip_ipaddr_t *node_ip, int prefix_len) {
  char ip_str[48] = {0};
  uip_ipaddr_sprintf(ip_str, node_ip);
  printf("[事件] 新节点入网:%s\n", ip_str);
  // 自定义业务处理逻辑
}

// 节点离网回调
static void node_leave_cb(rpl_dag_t *dag, const uip_ipaddr_t *node_ip, int prefix_len) {
  char ip_str[48] = {0};
  uip_ipaddr_sprintf(ip_str, node_ip);
  printf("[事件] 节点离网:%s\n", ip_str);
  // 自定义业务处理逻辑
}

// 协议栈初始化阶段注册回调
RPL_SET_ROUTE_ADDED_CALLBACK(node_join_cb);
RPL_SET_ROUTE_REMOVED_CALLBACK(node_leave_cb);

避坑说明

  • 非存储模式下不要读取uip_ds6_route路由表,该表仅存储根节点的直连邻居条目,多跳节点信息全部存在uip_sr源路由表中,读错表会拿不全节点信息。
  • 遍历源路由表的逻辑可以放在定时器里周期执行,也可以放在回调里触发,执行时不需要关中断,Contiki-NG的源路由表操作本身是进程安全的。

内容的提问来源于stack exchange,提问作者Javi Martínez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 13:12:17