使用 OTNS 模拟线程网络

1. 简介

Thread 网状网络拓扑的印象

什么是 Thread 和 OTNS

Thread 是一种基于 IP 的低功耗无线网状网络协议,可实现安全的设备间通信和设备与云端通信。Thread 网络可以适应拓扑变化,从而避免出现单点故障。

Google 发布的 OpenThread 是 Thread 的开源实现。尽管 OpenThread 的代码大小和内存占用空间很小,但它支持 Thread 规范中定义的所有功能。

OpenThread 网络模拟器 (OTNS) 可用于通过在 POSIX 平台上运行模拟的 OpenThread 节点来模拟 Thread 网络。OTNS 提供了一个简单易用的 Web 界面 (OTNS-Web),用于直观呈现和操作模拟的 Thread 网络。也可以进行脚本化模拟(使用 Python)。

学习内容

  • 安装 OTNS 及其依赖项
  • 了解 OTNS-CLI 的基础知识
  • 如何在 OTNS-Web 中添加/移动/删除 OpenThread 节点
  • 使用 OTNS-Web 的其他实用功能来控制网络模拟
  • 验证 OpenThread 的无单点故障特性
  • 在 Wireshark 中查看 OpenThread 节点之间的数据流量

此 Codelab 重点介绍如何以交互方式使用 OTNS-CLI 和 OTNS-Web。本文未介绍 OTNS 的其他功能,例如 Python 脚本。

所需条件

  • 线程入门。您需要了解线程的基本概念,才能理解此 Codelab 中介绍的内容。
  • 最好是 Linux x86_64,或者安装了 Homebrew 的 Mac OS。Windows WSL2 中的 Ubuntu 版本 24 或更高版本也应该可以正常运行,但可能需要手动调整一些设置。
  • Git
  • 网络浏览器。OTNS-Web 使用网络浏览器显示模拟。
  • Wireshark 网络协议分析器(可选)。
  • Go 1.23 版或更高版本。
    • 安装脚本将检查已安装的 Go 版本。
    • 如果未安装 Go,则会安装版本 >= 1.23(如果软件包管理系统中提供)。
    • 如果无法通过软件包管理系统获取,则需要手动安装。
    • 请注意,Ubuntu 24.04 或更低版本不支持自动安装 Go 1.23。如需了解详情,请参阅 Ubuntu 文档中的可用的 Golang 版本页面。您可以手动安装,也可以使用 snap 或其他程序进行安装。
  • Python 3.9 版或更高版本。
    • 安装脚本将检查已安装的 Python 版本。
    • 如果尚未安装 Python 3,则会安装软件包管理系统提供的版本(如果可用),且该版本必须高于或等于 3.9。
    • 如果无法通过软件包管理系统获取,则需要手动安装。

术语

“路由器”一词用作 Thread 网状网络扩展器的技术术语,该扩展器最初称为 Thread 路由器。“节点”是指 OTNS 模拟中的任何模拟 OpenThread 设备。

2. 安装

获取 OTNS 代码

$ git clone https://github.com/openthread/ot-ns.git ./otns
$ cd otns

本 Codelab 中的所有后续控制台命令都从 otns 目录运行。

引导和安装

bootstrap 脚本将安装依赖项(包括 Python 3 和 Go/Golang,如果需要)并安装 OTNS。请注意,如果脚本无法自动安装某些依赖项(例如 Python 版本 >= 3.9 或 Go 版本 >= 1.23),则可能会停止。自动安装要求可以在操作系统配置的软件包代码库中找到相应软件包。

该脚本还会构建可在模拟中直接使用的各种 OT 节点类型,并执行一些基本测试。由于这些节点构建,可能需要几分钟的时间。

$ ./script/bootstrap
....
....
OTNS installed - use 'otns' to start it.
$

在执行脚本期间,系统可能会要求您输入 sudo 的密码。

如果 otns 未正确安装

脚本可能会报告如下错误:

....
OTNS installed - please add ~/go/bin to your PATH variable first, to use it.
$

在这种情况下,您需要将 $(go env GOPATH)/bin 添加到 $PATH 变量中。

如果出现其他错误,您可以创建 GitHub 问题

3. 首次运行 OTNS

运行 otns

$ otns
>_ ← OTNS-CLI prompt

成功启动后,OTNS 将进入 CLI 控制台 (OTNS-CLI),并启动用于网络可视化和管理的 Web 浏览器 (OTNS-Web):

启动时的 OTNS-Web 窗口

如果您只能看到 OTNS-Web 的空白页面,很可能是因为您的浏览器未启用 WebGL。请参阅 https://superuser.com/a/836833,了解如何启用 WebGL。

在以下各部分中,您将学习如何通过 OTNS-CLIOTNS-Web 管理 OTNS 模拟。

4. 了解 OTNS-CLI 和 OTNS-Web

OTNS-CLI

OTNS-CLI 是用于管理 OTNS 模拟的命令行界面 (CLI)。

$ otns
>_ ← OTNS-CLI prompt

您可以通过 OTNS-CLI 输入命令。如需查看完整的命令列表,请参阅 OTNS CLI 参考文档。别担心,您只需在此 Codelab 中使用其中的几个命令。

输入 help 命令,查看 CLI 命令概览。此列表与 CLI 参考文档完全相同。

> help
add             Add a node to the simulation and get the node ID.
....
....
Done
> 

如需获取有关特定命令的更多帮助,请使用该命令的名称,例如:

> help add
add
  Add a node to the simulation and get the node ID.
  
Definition:
....
....
> 

OTNS-Web

OTNS-Web 是 OTNS 的网络可视化和管理工具。它以直观的方式呈现模拟 Thread 网络的节点、消息和链接。请注意 OTNS-Web 的各种元素:

OTNS-Web 元素说明

5. 添加节点

通过 OTNS-CLI 添加节点

在模拟中添加 Thread 路由器:

> add router
1
Done

您应该会在 OTNS-Web 中看到已创建的节点。节点以路由器的身份启动,并在几秒钟内成为领导者:

一个节点,角色为“领导者”

为了便于以交互方式开始模拟,每个新的 OpenThread 节点默认情况下都会使用一组标准网络参数进行调试。

通过 OTNS-CLI 添加更多节点

现在,我们将添加一些不同类型的节点。

> add fed
2
Done
> add med
3
Done
> add sed
4
Done

等待几秒钟,让节点合并为一个分区。您应该会在 OTNS-Web 中看到节点:

包含 4 个节点的 Thread 网络

OTNS-Web 中,您还可以选择任意节点,以打开一个面板,其中包含有关该节点的更多信息。例如,在下图所示的节点 1 中,面板中的“角色”条目确认该变体是领先变体。

包含 4 个节点的 Thread 网络,其中节点 1 处于选中状态

OTNS-Web 添加节点

您还可以通过 OTNS-Web 添加节点。点击 Action BarNew Router 按钮。您应该会在所选节点的右侧看到正在创建的节点。新路由器应加入现有的 Thread 分区:

添加了一个路由器,总共 5 个节点

您还可以点击操作栏上的 FED、MED、SSED 和 BR 按钮来创建其他类型的节点。现在应该有 9 个节点。您可以根据需要将一些节点拖动到其他位置,以创建不同的物理网络拓扑。

添加了多个新节点,总共 9 个节点

现在,您已创建了一个包含多个节点的单分区 Thread 网络。在下一部分中,我们将调整模拟速度,使模拟运行得更快。

6. 调整速度

目前,模拟应以 1X 的速度运行,这意味着自我们创建第一个节点以来,模拟时间与实际时间相同。

通过 OTNS-CLI 调整速度

您可以通过 OTNS-CLI 调整模拟速度。

将模拟速度设为 100X

> speed 100
Done

您应该会看到节点发送消息的频率比之前高得多。

将模拟速度设为 MAX

> speed max
Done

现在,OTNS 会尽力以尽可能快的速度进行模拟,因此您应该会看到节点发送大量消息。

暂停模拟

> speed 0
Done

将模拟速度设置为 0 会暂停模拟。

以正常速度恢复模拟

> speed 1
Done

将模拟速度设置为大于 0 的值会恢复模拟。

通过 OTNS-Web 调整速度

速度控制按钮

Action Bar 上找到速度控制按钮 速度控制按钮。这些按钮会显示当前的模拟速度,可用于调整模拟速度以及暂停/恢复模拟。

加快模拟速度

您可以点击 提速按钮 按钮,直到速度达到 MAX最大模拟速度指示器,从而加快模拟速度。

减慢模拟速度

您可以点击 调低速度按钮 按钮来减慢模拟速度。

暂停模拟

点击 “暂停”按钮 按钮可在模拟运行时暂停模拟。该按钮将变为 “播放”按钮

继续模拟

当模拟处于暂停状态时,点击 “播放”按钮 按钮可恢复模拟。该按钮将恢复为 “暂停”按钮

将模拟速度设为 10X

为了节省时间,请使用

OTNS-CLI 可将模拟速度调整为

10X 以便我们能够更快地观察网络中的拓扑变化。

> speed 10
Done

7. 开启/关闭电台

现在,模拟应包含至少 2 个路由器(六边形)、可能包含 1 个边界路由器(正方形)和许多子设备,并以 10 倍速度运行。

找到 2 个路由器的当前 Leader(红色边框),然后单击以选中它:

已选择领导者节点 1 的 Thread 网络

关闭无线通信

点击操作栏中的 “关闭”单选按钮 按钮,关闭 Leader 节点的无线装置。如果关闭了无线装置,领导者将无法发送或接收消息。

等待大约 12 秒(模拟时间为 120 秒),让其他路由器或边界路由器成为新的领导者:

以节点 9 为新 Leader 形成新分区

Thread 网络通过形成具有新领导者的新分区,自动从领导者故障中恢复。新分区还具有新的分区颜色。

打开无线通信

选择无线电已关闭的领导者。点击 Action Bar 上的 “开启”单选按钮 按钮以恢复无线电连接:

节点 1 在无线装置重新开启后加入分区

在无线电连接恢复后,Leader 应重新连接到网络。

8. 移动节点

借助 OTNS,用户可以通过 OTNS-CLIOTNS-Web 轻松移动节点。

通过 OTNS-CLI 移动节点

将边境路由器节点 9 移至新位置:

> move 9 50 50
Done

通过 OTNS-Web 移动节点

通过拖动将节点 5 一直移动到右下角。由于节点 5 现在超出了其他路由器的无线电覆盖范围,因此它会形成自己的分区,并具有新的分区 ID。点击节点,即可在节点信息面板中查看分区 ID。

节点 5 移开,与其他节点形成新的分区

请注意,节点 5 和节点 9 之间仍然绘制了一条绿色线。这通常是因为前父级的子级表中仍保留着有关子级的过时信息。或者,它可能是有关节点 9 和节点 5 之间之前存在的路由器到路由器链接的过时信息。(或者,在这种情况下,甚至可能是渲染 bug。)最终,过时的信息会在适当的超时时间过后在节点上清理掉。

9. 删除节点

通过 OTNS-CLI 删除节点

删除节点 5:

> del 5
Done

节点 5 应从模拟中消失:

节点 5 已从模拟中删除

通过 OTNS-Web 删除节点

选择边框路由器节点 9,然后点击 Action Bar 上的 “删除”按钮 按钮以删除节点 9:

已删除边框路由器节点 9

Node 1 应成为新分区的主节点,所有剩余节点将作为子节点附加到节点 1。

10. OTNS-CLI 节点上下文

OTNS-CLI 提供节点上下文模式,以便轻松与节点互动,帮助开发者诊断节点的状态。此外,还可以在此模式下启动节点操作。

进入节点上下文模式

输入节点 1 的节点上下文:

> node 1
Done
node 1>

CLI 提示符已更改为 node 1>,表示当前节点上下文。您可以输入要在节点上执行的 OpenThread CLI 命令,就像直接与节点互动一样。

在节点上下文中执行命令

node 1> state
leader
Done
node 1> channel
11
Done
node 1> panid
0xface
Done
node 1> networkname
otns
Done
node 1> ipaddr
fdde:ad00:beef:0:0:ff:fe00:fc00
fdde:ad00:beef:0:0:ff:fe00:b400
fd00:f00d:cafe:0:2505:8719:3685:ebfb
fdde:ad00:beef:0:4fd9:b9ba:44e0:96cb
fe80:0:0:0:e86a:e07:ec97:777
Done

切换到其他节点上下文

node 1> node 2
Done
node 2> 

退出节点上下文

node 1> exit
Done
>

退出节点上下文的另一种方法是使用 node 0 命令。

11. 查看节点日志和数据包捕获

OpenThread 节点日志

默认情况下,OTNS 会为所有模拟的 OpenThread 节点生成详细的日志文件。您可以在 ./tmp 目录中查看这些文件。文件名为 0_.log。例如,以下是日志文件摘录:

7616488 00:00:06.326 [I] MeshForwarder-: Received IPv6 UDP msg, len:90, chksum:5915, ecn:no, from:ca72650db7b856af, sec:no, prio:net, rss:-58.0
7616488 00:00:06.326 [I] MeshForwarder-:     src:[fe80:0:0:0:c872:650d:b7b8:56af]:19788
7616488 00:00:06.326 [I] MeshForwarder-:     dst:[ff02:0:0:0:0:0:0:1]:19788
7616488 00:00:06.326 [D] Mle-----------: Receive MLE message
7616488 00:00:06.326 [D] Mac-----------: Idle mode: Radio receiving on channel 11
7657544 00:00:06.367 [D] Mac-----------: ==============================[RX len=063]==============================
7657544 00:00:06.367 [D] Mac-----------: | 41 D8 7F CE FA FF FF 46 | 74 5A 33 9E 76 51 4E 7F | A......FtZ3.vQN. |
7657544 00:00:06.367 [D] Mac-----------: | 3B 02 F0 4D 4C 4D 4C 81 | E6 00 15 03 00 00 00 00 | ;..MLML......... |
7657544 00:00:06.367 [D] Mac-----------: | 00 00 00 01 46 86 7D FE | 06 CC DB 94 86 9C 88 0B | ....F.}......... |
7657544 00:00:06.367 [D] Mac-----------: | 1C 1E 26 9B 8D 21 2E 65 | 53 5A 43 4E A2 59 D6    | ..&..!.eSZCN.Y.  |
7657544 00:00:06.367 [D] Mac-----------: ------------------------------------------------------------------------
7657544 00:00:06.367 [I] MeshForwarder-: Received IPv6 UDP msg, len:84, chksum:81e6, ecn:no, from:4e51769e335a7446, sec:no, prio:net, rss:-48.0
7657544 00:00:06.367 [I] MeshForwarder-:     src:[fe80:0:0:0:4c51:769e:335a:7446]:19788
7657544 00:00:06.367 [I] MeshForwarder-:     dst:[ff02:0:0:0:0:0:0:2]:19788
7657544 00:00:06.367 [D] Mac-----------: Idle mode: Radio receiving on channel 11
7833912 00:00:06.543 [I] Mle-----------: AttachState ParentReq -> Idle
7833912 00:00:06.543 [N] RouterTable---: Allocate router id 12
7833912 00:00:06.543 [N] Mle-----------: RLOC16 fffe -> 3000
7833912 set node RLOC16: fffe -> 3000
7833912 00:00:06.543 [D] SubMac--------: RadioShortAddress: 0x3000
7833912 00:00:06.543 [N] Mle-----------: Role detached -> leader
7833912 00:00:06.543 [N] Mle-----------: Partition ID 0x24c35f10
7833912 00:00:06.543 [I] RouterTable---: Route table
7833912 00:00:06.543 [I] RouterTable---:     12 0x3000 - me - leader

左侧显示的是以微秒为单位的绝对模拟时间。hh:mm:ss 时间戳显示的是 OpenThread 节点自身的日志时间戳,可能与绝对模拟时间不同。

Wireshark 数据包捕获

默认情况下,所有传输的 IEEE 802.15.4 帧都会捕获到 PCAP 文件 current.pcap 中。Wireshark 可以在模拟期间或之后读取此文件。由于 Thread 具有链路层加密功能,因此需要在 Wireshark 中执行一次性配置操作,以正确设置 OTNS 的解密密钥。默认情况下,使用一个众所周知的网络密钥,以便 Wireshark 轻松进行帧解密。

如需查看 Wireshark 中 OpenThread 数据包检查的示例,请参阅以下屏幕截图。

Wireshark 中 OpenThread 数据包分析的屏幕截图

如需配置解密密钥,请在菜单中选择修改 -> 偏好设置。然后在偏好设置窗口中,选择 Protocols -> IEEE 802.15.4。点击“解密密钥”旁边的修改…按钮。点击 + 以创建新条目,然后输入密钥 00112233445566778899aabbccddeeff(32 个字符),并在“密钥哈希”字段中选择“线程哈希”。“解密密钥索引”可以保留为 0。然后依次点击确定确定。现在,加载 OTNS PCAP 文件时,应该可以正确解密。

“时间”列中显示的时间戳(以秒为单位)对应于 OpenThread 节点日志中显示的绝对模拟时间值。这样可以更轻松地将日志消息与已发送或接收的无线电帧相关联。不过,这些值通常不会完全相同(精确到微秒级):在 OpenThread 堆栈请求发送无线电帧后,模拟的 IEEE 802.15.4 无线电硬件可能会增加一些额外的延迟。

12. 恭喜

恭喜,您已成功执行首次 OTNS 模拟!

您已了解如何安装 OTNS 及其依赖项。您已使用 OpenThread 模拟节点启动 OTNS 模拟。您已经学习了如何通过 OTNS-CLIOTNS-Web 以各种方式操纵模拟。

您现在已了解 OTNS 是什么,以及如何使用 OTNS 模拟 OpenThread 网络。

后续操作

查看下列 Codelab…

参考文档