1. 简介
Google 发布的 OpenThread 是 Thread 网络协议的开源实现。Google Nest 发布了 OpenThread,让开发者广泛使用 Nest 产品中使用的技术,以加快智能互联家居产品的开发速度。
线程规范定义了适用于家居应用的基于 IPv6 的可靠、安全、低功耗的无线设备到设备通信协议。OpenThread 实现所有 Thread 网络层,包括具有 MAC 安全性的 IPv6、6LoWPAN、IEEE 802.15.4、网格链接建立和网格路由。
此 Codelab 会引导您使用 Docker 模拟模拟设备上的 Thread 网络。
学习内容
- 如何设置 OpenThread 构建工具链
- 如何模拟 Thread 网络
- 如何对线程节点进行身份验证
- 如何使用 OpenThread 守护程序管理 Thread 网络
所需条件
- Docker
- 具备 Linux 和网络路由方面的基础知识
2. 设置 Docker
此 Codelab 旨在在 Linux、Mac OS X 或 Windows 计算机上使用 Docker。建议使用 Linux 环境。
安装 Docker
在您选择的操作系统上安装 Docker。
拉取 Docker 映像
安装 Docker 后,打开一个终端窗口并拉取 openthread/environment
Docker 映像。此图预构建了 OpenThread 和 OpenThread 守护程序,并可用于此 Codelab。
$ docker pull openthread/environment:latest
请注意,这可能需要几分钟时间才能完成下载。
在终端窗口中,从映像启动 Docker 容器并连接到其 bash
shell:
$ docker run --name codelab_otsim_ctnr -it --rm \ --sysctl net.ipv6.conf.all.disable_ipv6=0 \ --cap-add=net_admin openthread/environment bash
请注意此 Codelab 所需的标志:
--sysctl net.ipv6.conf.all.disable_ipv6=0
- 这会在容器内启用 IPv6--cap-add=net_admin
- 启用 NET_ADMIN 功能,让您能够执行与网络相关的操作,例如添加 IP 路由
进入容器后,您应该会看到如下所示的提示:
root@c0f3912a74ff:/#
在上面的示例中,c0f3912a74ff
是容器 ID。Docker 容器实例的容器 ID 将不同于本 Codelab 的提示中显示的 ID。
使用 Docker
本 Codelab 假定您了解使用 Docker 的基础知识。整个 Codelab 都应留在 Docker 容器中。
3. 模拟线程网络
您将在本 Codelab 中使用的示例应用演示了一个极简 OpenThread 应用,该应用可通过基本命令行界面 (CLI) 公开 OpenThread 配置和管理界面。
本练习将引导您完成对一个模拟线程设备从另一个模拟线程设备进行 ping 操作所需的最少步骤。
下图介绍了基本的 Thread 网络拓扑。在本练习中,我们将模拟绿色圆圈内的两个节点:线程主管和线程路由器,两者之间的单一连接。
创建网络
1. 启动节点 1
如果您尚未在终端窗口中启动 Docker 容器并连接到其 bash
shell,请执行以下操作:
$ docker run --name codelab_otsim_ctnr -it --rm \ --sysctl net.ipv6.conf.all.disable_ipv6=0 \ --cap-add=net_admin openthread/environment bash
在 Docker 容器中,使用 ot-cli-ftd
二进制文件为模拟线程设备生成 CLI 进程。
root@c0f3912a74ff:/# /openthread/build/examples/apps/cli/ot-cli-ftd 1
注意:运行此命令后,如果系统未显示 >
提示,请按 enter
。
此二进制文件实现了 OpenThread 设备。IEEE 802.15.4 无线装置驱动程序基于 UDP 实现(IEEE 802.15.4 帧在 UDP 载荷内传递)。
1
的参数是一个文件描述符,表示针对模拟设备的“工厂分配”IEEE EUI-64 的最低有效位。在绑定至 IEEE 802.15.4 无线装置模拟(端口 = 9000 + 文件描述符)的 UDP 端口时,也会使用此值。此 Codelab 中的模拟 Thread 设备的每个实例都将使用不同的文件描述符。
注意:在为模拟设备生成进程时,请仅使用本 Codelab 中所述的 1
或更高版本的文件描述符。0
的文件描述符已预留用于其他用途。
创建一个新的操作数据集,并将其作为活跃数据集提交。操作数据集是您创建的 Thread 网络的配置。
> dataset init new Done > dataset Active Timestamp: 1 Channel: 20 Channel Mask: 07fff800 Ext PAN ID: d6263b6d857647da Mesh Local Prefix: fd61:2344:9a52:ede0/64 Network Key: e4344ca17d1dca2a33f064992f31f786 Network Name: OpenThread-c169 PAN ID: 0xc169 PSKc: ebb4f2f8a68026fc55bcf3d7be3e6fe4 Security Policy: 0, onrcb Done
将此数据集提交为活跃数据集:
> dataset commit active Done
启动 IPv6 接口:
> ifconfig up Done
启动线程协议操作:
> thread start Done
等待几秒钟,并验证设备是否已成为线程主要副本。主要副本是负责管理路由器 ID 分配的设备。
> state leader Done
查看分配给节点 1 的线程接口的 IPv6 地址(输出会有所不同):
> ipaddr fd61:2344:9a52:ede0:0:ff:fe00:fc00 fd61:2344:9a52:ede0:0:ff:fe00:5000 fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6 fe80:0:0:0:94da:92ea:1353:4f3b Done
请注意特定的 IPv6 地址类型:
- 以
fd
开头 = Mesh-local - 以
fe80
开头 = 链接本地
网格本地地址类型会进一步分类:
- 包含
ff:fe00
= 路由器定位器 (RLOC) - 不包含
ff:fe00
= 端点标识符 (EID)
确定控制台输出中的 EID,并记下此 ID 以供日后使用。在上面的示例输出中,EID 为:
fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6
2. 启动节点 2
打开新终端,并在当前运行的 Docker 容器中执行 bash
shell 以将其用于节点 2。
$ docker exec -it codelab_otsim_ctnr bash
在新的 bash
提示符处,使用参数 2
生成 CLI 进程。这是您的第二个模拟 Thread 设备:
root@c0f3912a74ff:/# /openthread/build/examples/apps/cli/ot-cli-ftd 2
注意:运行此命令后,如果系统未显示 >
提示,请按 enter
。
使用与节点 1 的运营数据集相同的值配置线程网络密钥和 PAN ID:
> dataset networkkey e4344ca17d1dca2a33f064992f31f786 Done > dataset panid 0xc169 Done
将此数据集提交为活跃数据集:
> dataset commit active Done
启动 IPv6 接口:
> ifconfig up Done
启动线程协议操作:
> thread start Done
设备会将自身初始化为子项。Thread Child 等同于最终设备,End Device 是仅通过父设备传输和接收单播流量的 Thread 设备。
> state child Done
在 2 分钟内,您应该会看到状态从 child
切换到 router
。Thread Router 能够在 Thread 设备之间路由流量。也称为父级。
> state router Done
验证网络
验证网格网络的一种简单方法是查看路由器表。
1. 检查连接性
在 Node 2 上,获取 RLOC16。RLOC16 是设备的 RLOC IPv6 地址的最后 16 位。
> rloc16 5800 Done
在节点 1 上,检查节点 2 的 RLOC16 的路由器表。请先确保节点 2 已切换到路由器状态。
> router table | ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC | +----+--------+----------+-----------+--------+-------+---+--------------------+ | 20 | 0x5000 | 63 | 0 | 0 | 0 | 0 | 96da92ea13534f3b | | 22 | 0x5800 | 63 | 0 | 3 | 3 | 23 | 5a4eb647eb6bc66c |
表中发现 0x5800
的节点 2 的 RLOC,确认它已连接到网格。
2. 从节点 2 对节点 1 进行 ping 操作
验证两个模拟线程设备之间的连接。在节点 2 中,ping
为节点 1 分配的 EID:
> ping fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6 > 16 bytes from fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6: icmp_seq=1 hlim=64 time=12ms
按 enter
返回 >
CLI 提示符。
测试网络
现在,您可以在两台模拟的线程设备之间成功执行 ping 操作,接下来,您可以将一个节点离线,以测试网状网络。
返回节点 1 并停止线程:
> thread stop Done
切换到节点 2 并检查状态。在两分钟内,节点 2 检测到主要节点(节点 1)处于离线状态,您应该会看到节点 2 转换为网络的 leader
:
> state router Done ... > state leader Done
确认线程 2 并恢复出厂设置后,再将其返回 Docker bash
提示符。恢复出厂设置可确保我们在本练习中使用的线程网络凭据不会转移到下一个练习。
> thread stop Done > factoryreset > > exit root@c0f3912a74ff:/#
使用 factoryreset
命令后,您可能需要按几次 enter
才能返回 >
提示符。请勿退出 Docker 容器。
同时恢复出厂设置和退出节点 1:
> factoryreset > > exit root@c0f3912a74ff:/#
请参阅 OpenThread CLI 参考文档,了解所有可用的 CLI 命令。
4. 使用“调试”对节点进行身份验证
在之前的练习中,您设置了一个包含 2 个模拟设备和一个已验证连接的 Thread 网络。但是,这只允许未经身份验证的 IPv6 链路本地流量在设备之间传递。如需在它们之间(以及通过线程边界路由器在互联网之间)路由全局 IPv6 流量,您必须对节点进行身份验证。
要进行身份验证,必须有一个设备作为专员。Commissioner 是当前当选新 Thread 设备的身份验证服务器,也是用于提供设备加入网络所需的网络凭据的授权方。
在本练习中,我们将使用与之前相同的双节点拓扑。对于身份验证,Thread Leader 将充当 Commissioner,Thread Router 作为 Joiner。
Docker
对于其余练习中的每个节点(终端窗口),请确保您使用的是 OpenThread 构建运行 Docker 容器。如果继续上一个练习,则同一 Docker 容器中仍应有两个 bash
提示。否则,请参阅 Docker 问题排查步骤,或直接重做模拟线程网络练习。
1. 创建网络
在节点 1 中,衍生 CLI 进程:
root@c0f3912a74ff:/# /openthread/build/examples/apps/cli/ot-cli-ftd 1
注意:运行此命令后,如果系统未显示 >
提示,请按 enter
。
创建一个新的操作数据集,将其作为活跃数据集提交,然后启动线程:
> dataset init new Done > dataset Active Timestamp: 1 Channel: 12 Channel Mask: 07fff800 Ext PAN ID: e68d05794bf13052 Mesh Local Prefix: fd7d:ddf7:877b:8756/64 Network Key: a77fe1d03b0e8028a4e13213de38080e Network Name: OpenThread-8f37 PAN ID: 0x8f37 PSKc: f9debbc1532487984b17f92cd55b21fc Security Policy: 0, onrcb Done
将此数据集提交为活跃数据集:
> dataset commit active Done
启动 IPv6 接口:
> ifconfig up Done
启动线程协议操作:
> thread start Done
等待几秒钟,并验证设备是否已成为线程主要副本:
> state leader Done
2. 启动“专员”角色
仍在 Node 1 上,启动 Commissioner 角色:
> commissioner start Done
允许任何具有 J01NME
联接器凭据的联接器(通过使用 *
通配符)委托到网络。联接器是由真人管理员添加到委托线程网络的设备。
> commissioner joiner add * J01NME Done
3. 启动 Joiner 角色
在第二个终端窗口的 Docker 容器中,生成新的 CLI 进程。这是节点 2。
root@c0f3912a74ff:/# /openthread/build/examples/apps/cli/ot-cli-ftd 2
在 Node 2 上,使用 J01NME
Joiner 凭据启用 Joiner 角色。
> ifconfig up Done > joiner start J01NME Done
... 等待几秒钟进行确认 ...
Join success
作为联接者,设备(节点 2)已成功通过专员(节点 1)对自身进行身份验证,并收到线程网络凭据。
现在,节点 2 已通过身份验证,接下来可以启动线程:
> thread start Done
4. 验证网络身份验证
检查节点 2 上的 state
,验证其现已加入网络。在两分钟内,节点 2 会从 child
转换到 router
:
> state child Done ... > state router Done
5. 重置配置
为了准备下一个练习,请重置配置。在每个节点上,停止线程,恢复出厂设置,然后退出模拟的线程设备:
> thread stop Done > factoryreset > > exit root@c0f3912a74ff:/#
使用 factoryreset
命令后,您可能需要按几次 enter
才能返回 >
提示符。
5. 使用 OpenThread 守护程序管理网络
在本练习中,我们将模拟一个 CLI 实例(单个嵌入式 SoC 线程设备)和一个无线电处理器 (RCP) 实例。
ot-daemon
是 OpenThread Posix 应用的模式,它使用 UNIX 套接字作为输入和输出,以便 OpenThread 核心可以作为服务运行。客户端可以通过使用 OpenThread CLI 作为协议连接到套接字来与此服务进行通信。
ot-ctl
是 ot-daemon
提供的用于管理和配置 RCP 的 CLI。使用此功能,我们会将 RCP 连接到 Thread 设备创建的网络。
Docker
对于本练习中的每个节点(终端窗口),请确保您使用 OpenThread 构建运行 Docker 容器。如果继续上一个练习,则同一 Docker 容器中应该有两个 bash
提示。否则,请参阅 Docker 问题排查步骤。
使用 ot-daemon
本练习将使用三个终端窗口,它们对应于以下各项:
- 模拟 Thread 设备的 CLI 实例(节点 1)
ot-daemon
个进程ot-ctl
个 CLI 实例
1. 启动节点 1
在第一个终端窗口中,为模拟 Thread 设备生成 CLI 进程:
root@c0f3912a74ff:/# /openthread/build/examples/apps/cli/ot-cli-ftd 1
注意:运行此命令后,如果系统未显示 >
提示,请按 enter
。
创建一个新的操作数据集,将其作为活跃数据集提交,然后启动线程:
> dataset init new Done > dataset Active Timestamp: 1 Channel: 13 Channel Mask: 07fff800 Ext PAN ID: 97d584bcd493b824 Mesh Local Prefix: fd55:cf34:dea5:7994/64 Network Key: ba6e886c7af50598df1115fa07658a83 Network Name: OpenThread-34e4 PAN ID: 0x34e4 PSKc: 38d6fd32c866927a4dfcc06d79ae1192 Security Policy: 0, onrcb Done
将此数据集提交为活跃数据集:
> dataset commit active Done
启动 IPv6 接口:
> ifconfig up Done
启动线程协议操作:
> thread start Done
查看分配给节点 1 的线程接口的 IPv6 地址:
> ipaddr fd55:cf34:dea5:7994:0:ff:fe00:fc00 fd55:cf34:dea5:7994:0:ff:fe00:d000 fd55:cf34:dea5:7994:460:872c:e807:c4ab fe80:0:0:0:9cd8:aab6:482f:4cdc Done >
如模拟线程网络步骤中所述,一个地址是链路本地地址 (fe80
),三个地址是网格本地地址 (fd
)。EID 是网址中不含 ff:fe00
的网格本地地址。在此示例输出中,EID 为 fd55:cf34:dea5:7994:460:872c:e807:c4ab
。
从您的 ipaddr
输出中确定特定的 EID,此 ID 将用于与节点通信。
2. 启动 OT 守护程序
在第二个终端窗口中,创建一个 tun
设备节点并设置读写权限:
root@c0f3912a74ff:/# mkdir -p /dev/net && mknod /dev/net/tun c 10 200 root@c0f3912a74ff:/# chmod 600 /dev/net/tun
此设备用于在虚拟设备中传输和接收数据包。如果设备已创建,则系统可能会出现错误。这是正常现象,可以忽略。
为 RCP 节点(我们将该节点称为节点 2)启动 ot-daemon
。使用 -v
详细标志,以查看日志输出并确认其正在运行:
root@c0f3912a74ff:/# /openthread/build/posix/src/posix/ot-daemon -v \ 'spinel+hdlc+forkpty:///openthread/build/examples/apps/ncp/ot-rcp?forkpty-arg=2'
成功后,详细模式下的 ot-daemon
会生成类似于以下内容的输出:
ot-daemon[31]: Running OPENTHREAD/297a880; POSIX; Feb 1 2022 04:43:39 ot-daemon[31]: Thread version: 3 ot-daemon[31]: Thread interface: wpan0 ot-daemon[31]: RCP version: OPENTHREAD/297a880; SIMULATION; Feb 1 2022 04:42:50
使此终端保持打开状态并在后台运行。您无需再在其中输入任何命令。
3. 使用 ot-ctl 加入网络
我们尚未向任何 Thread 网络委托节点 2 (ot-daemon
RCP)。这正是ot-ctl
的用武之地。ot-ctl
使用与 OpenThread CLI 应用相同的 CLI。因此,您可以使用与其他模拟 Thread 设备相同的方式控制 ot-daemon
节点。
打开第三个终端窗口并执行现有容器:
$ docker exec -it codelab_otsim_ctnr bash
进入容器后,启动 ot-ctl
:
root@c0f3912a74ff:/# /openthread/build/posix/src/posix/ot-ctl >
在此第三个终端窗口中,您将使用 ot-ctl
来管理您通过 ot-daemon
在第二个终端窗口中启动的节点 2(RCP 节点)。检查节点 2 的 state
:
> state disabled Done
获取节点 2 的 eui64
,限制联接到特定联接器:
> eui64 18b4300000000001 Done
在 Node 1(第一个终端窗口)上,启动 Commissioner,并仅将该 eui64 加入:
> commissioner start Done > commissioner joiner add 18b4300000000001 J01NME Done
在第三个终端窗口中,调出节点 2 的网络接口并加入网络:
> ifconfig up Done > joiner start J01NME Done
... 等待几秒钟进行确认 ...
Join success
作为联接成员,RCP(节点 2)已成功通过委员(节点 1)对自身进行身份验证,并收到线程网络凭据。
现在,将节点 2 加入 Thread 网络(同样是在第三个终端窗口中):
> thread start Done
4. 验证网络身份验证
在第三个终端中,检查节点 2 上的 state
,以验证其现已加入网络。在两分钟内,节点 2 会从 child
转换到 router
:
> state child Done ... > state router Done
5. 验证连接
在第三个终端窗口中,使用 Ctrl+D 或 exit
命令退出 ot-ctl
,然后返回容器的 bash
控制台。在此控制台中,使用 ping6
命令通过 EID 对其 ping 操作。如果 ot-daemon
RCP 实例成功加入并连接到 Thread 网络,ping 将成功:
root@c0f3912a74ff:/# ping6 -c 4 fd55:cf34:dea5:7994:460:872c:e807:c4ab PING fd55:cf34:dea5:7994:460:872c:e807:c4ab (fd55:cf34:dea5:7994:460:872c:e807:c4ab): 56 data bytes 64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=0 ttl=64 time=4.568 ms 64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=1 ttl=64 time=6.396 ms 64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=2 ttl=64 time=7.594 ms 64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=3 ttl=64 time=5.461 ms --- fd55:cf34:dea5:7994:460:872c:e807:c4ab ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max/stddev = 4.568/6.005/7.594/1.122 ms
6. Docker 问题排查
如果您已退出 Docker 容器
bash
提示,您可能需要检查它是否正在运行,并根据需要重启/重新进入。
如需显示哪些 Docker 容器正在运行,请执行以下操作:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 505fc57ffc72 environment "bash" 10 minutes ago Up 10 minutes codelab_otsim_ctnr
如需显示所有 Docker 容器(正在运行和已停止),请执行以下操作:
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 505fc57ffc72 environment "bash" 10 minutes ago Up 10 minutes codelab_otsim_ctnr
如果您在任一 docker ps
命令的输出中都没有看到容器 codelab_otsim_ctnr
,请再次运行:
$ docker run --name codelab_otsim_ctnr -it --rm \ --sysctl net.ipv6.conf.all.disable_ipv6=0 \ --cap-add=net_admin openthread/environment bash
如果容器已停止(在 docker ps -a
中列出,但不在 docker ps
中),请重启它:
$ docker start -i codelab_otsim_ctnr
如果 Docker 容器已在运行(在 docker ps
中列出),请在每个终端中重新连接到容器:
$ docker exec -it codelab_otsim_ctnr bash
“不允许的操作”错误
如果您在创建新的 OpenThread 节点(使用 mknod
命令)时遇到 Operation not permitted
错误,请确保根据此 Codelab 中提供的命令以根用户身份运行 Docker。此 Codelab 不支持在无根模式下运行 Docker。
7. 恭喜!
您已成功使用 OpenThread 模拟您的第一个 Thread 网络。棒极了!
在此 Codelab 中,你学习了如何:
- 启动和管理 OpenThread 模拟 Docker 容器
- 模拟 Thread 网络
- 对线程节点进行身份验证
- 使用 OpenThread 守护程序管理 Thread 网络
如需详细了解 Thread 和 OpenThread,请参阅以下参考: