Google 致力于为黑人社区推动种族平等。查看具体行动

在 Docker 中使用 OpenThread 模拟线程网络

1. 简介

26b7f4f6b3ea0700.png

Google 发布的 OpenThreadThread 网络协议的开源实现。Google Nest 发布了 OpenThread,以便让开发者广泛使用 Nest 产品中使用的技术,从而加速开发智能互联家居产品。

线程规范为家庭应用定义了基于 IPv6 的可靠、低功耗的无线设备到设备通信协议。OpenThread 实现了所有线程网络层,包括 IPv6、6LoWPAN、IEEE 802.15.4(MAC 安全)、网格链接建立和网格路由。

此 Codelab 将引导您使用 Docker 在模拟设备上模拟线程网络。

学习内容

  • 如何设置 OpenThread 构建工具链
  • 如何模拟线程网络
  • 如何对线程节点进行身份验证
  • 如何使用 OpenThread 守护程序管理线程网络

所需条件

  • Docker
  • Linux 和网络路由基础知识

2. 设置 Docker

此 Codelab 设计为在 Linux、Mac OS X 或 Windows 计算机上使用 Docker。建议使用 Linux。

安装 Docker

在您选择的操作系统上安装 Docker。

拉取 Docker 映像

安装 Docker 后,打开一个终端窗口并拉取 openthread/environment Docker 映像。此映像包含已预构建并可用于此 Codelab 的 OpenThread 和 OpenThread 守护程序。

$ 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 操作所需的最少步骤。

下图介绍了基本的线程网络拓扑。在本练习中,我们将模拟绿色圆圈中的两个节点:线程主节点和线程路由器,它们之间的具有单个连接。

6e3aa07675f902dc.png

创建网络

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 无线模拟的 UDP 端口(端口 = 9000 + 文件描述符)时也会使用该值。此 Codelab 中每个模拟线程设备的实例将使用不同的文件描述符。

注意:在为模拟设备生成进程时,只能使用此 Codelab 中所述的 1 或更大的文件描述符。0 的文件描述符已预留,供其他用途使用。

创建新的操作数据集并将其作为活动数据集提交。Operational Dataset 在您要创建的线程网络进行配置。

> 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 开头 = link-local

网格本地地址类型进一步分类:

  • 包含 ff:fe00 = 路由器定位器 (RLOC)
  • 不包含 ff:fe00 = 端点标识符 (EID)

识别控制台输出中的 EID,并记下该 ID 以备日后使用。在上面的示例输出中,EID 为:

fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6

2. 启动节点 2

打开新终端,并在当前正在运行的 Docker 容器中执行 bash shell 以用于 Node 2。

$ docker exec -it codelab_otsim_ctnr bash

在这个新的 bash 提示符下,使用参数 2 生成 CLI 进程。这是您的第二个模拟线程设备:

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

设备将自己初始化为子级。线程子项相当于终端设备,而终端设备是仅通过父设备发送和接收单播流量的线程设备。

> state
child
Done

在 2 分钟内,您应该会看到状态从 child 变为 router。线程路由器能够在线程设备之间路由流量。也称为父级。

> state
router
Done

验证网络

验证网状网络的一种简单方法是查看路由器表。

1. 检查连接

在节点 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 |

在表格中找到了节点 2 的 0x5800 的 RLOC,以确认它已连接到网格。

2. 从节点 2 中 Ping 节点 1

验证两个模拟线程设备之间的连接。在节点 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:/#

您可能需要按 enter 几次,才能在 factoryreset 命令之后再次显示 > 提示。请勿退出 Docker 容器。

同时恢复出厂设置并退出节点 1:

> factoryreset
>
> exit
root@c0f3912a74ff:/#

如需了解所有可用的 CLI 命令,请参阅 OpenThread CLI 参考

4.通过调试对节点进行身份验证

在上一练习中,您设置了具有两个模拟设备和已验证连接的线程网络。但是,这只允许未通过身份验证的 IPv6 链路本地流量在设备之间传递。如需在这些地址(以及通过线程边界路由器的互联网)之间路由全球 IPv6 流量,您必须对节点进行身份验证。

为了进行身份验证,一台设备必须充当专员。委员是目前选对新线程设备的身份验证服务器,授权者提供加入网络所需的网络凭据。

在本练习中,我们将使用与之前相同的双节点拓扑。对于身份验证,线程领导者将充当专员,即线程路由器成为连接者。

d6a67e8a0d0b5dcb.png

Docker

对于其余练习中的每个节点(终端窗口),请确保使用 OpenThread build 运行 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 start
Done

允许任何具有 J01NME 连接器凭据的连接器(使用 * 通配符)向网络进行调试。连接符是由人类管理员添加到某个委托线程网络的设备。

> commissioner joiner add * J01NME
Done

3.启动 Joiner 角色

在第二个终端窗口的 Docker 容器中生成新的 CLI 进程。这是节点 2。

root@c0f3912a74ff:/# /openthread/build/examples/apps/cli/ot-cli-ftd 2

在节点 2 上,使用 J01NME 连接器凭据启用“连接者”角色。

> ifconfig up
Done
> joiner start J01NME
Done

... 等待几秒钟进行确认 ...

Join success

作为联接者,设备 (Node 2) 已成功通过专员 (Node 1) 验证自身身份,并且获得了线程网络凭据。

现在 Node 2 已通过身份验证,请启动线程:

> thread start
Done

4.验证网络身份验证

检查节点 2 上的 state,以验证其现已加入网络。在两分钟内,节点 2 从 child 转换到 router

> state
child
Done
...
> state
router
Done

5. 重置配置

如需准备下一个练习,请重置配置。在每个节点上,停止线程,恢复出厂设置,然后退出模拟的线程设备:

> thread stop
Done
> factoryreset
>
> exit
root@c0f3912a74ff:/#

您可能需要按 enter 几次,才能在 factoryreset 命令之后再次显示 > 提示。

5. 使用 OpenThread 守护程序管理网络

在本练习中,我们将模拟一个 CLI 实例(一个嵌入式 SoC 线程设备)和一个无线电协处理器 (RCP) 实例。

ot-daemon 是 OpenThread Posix 应用的模式,该模式使用 UNIX 套接字作为输入和输出,以便 OpenThread 核心可以作为服务运行。客户端可以使用 OpenThread CLI 作为协议连接到套接字,以便与该服务通信。

ot-ctlot-daemon 提供的 CLI,用于管理和配置 RCP。通过使用此网址,我们会将 RCP 连接到线程设备创建的网络。

Docker

对于本练习中的每个节点(终端窗口),请确保运行包含 OpenThread build 的 Docker 容器。 如果继续之前的练习,您应该已经打开同一 Docker 容器中的两条 bash 提示。如果没有,请参阅 Docker 问题排查步骤。

使用 ot-daemon

本练习将使用三个终端窗口,分别对应于:

  1. 模拟线程设备的 CLI 实例(节点 1)
  2. ot-daemon 个进程
  3. ot-ctl CLI 实例

1. 启动节点 1

在第一个终端窗口中,为模拟线程设备生成 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。

2. 启动 ot-daemon

在第二个终端窗口中,创建 tun 设备节点并设置读/写权限:

root@c0f3912a74ff:/# mkdir -p /dev/net && mknod /dev/net/tun c 10 200
root@c0f3912a74ff:/# chmod 600 /dev/net/tun

此设备用于在虚拟设备中传输和接收数据包。如果设备已创建,则可能会收到错误消息,这是正常现象,可以忽略。

为 RCP 节点启动 ot-daemon,我们将称为节点 2。使用 -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 加入网络

我们尚未向任何线程网络委托节点 2 (ot-daemon RCP)。这就是 ot-ctl 发挥作用的地方。ot-ctl 使用与 OpenThread CLI 应用相同的 CLI。因此,您可以使用与其他模拟线程设备相同的方式控制 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

在节点 1(第一个终端窗口)上,启动专员,并将其限制为仅加入该 eui64:

> commissioner start
Done
> commissioner joiner add 18b4300000000001 J01NME
Done

在第三个终端窗口中,启动节点 2 的网络接口并加入网络:

> ifconfig up
Done
> joiner start J01NME
Done

... 等待几秒钟进行确认 ...

Join success

作为联接者,RCP(节点 2)已成功向专员(节点 1)验证自身的身份并获得了线程网络凭据。

现在,将 Node 2 加入线程网络(同样在第三个终端窗口中):

> thread start
Done

4.验证网络身份验证

在第三个终端中,检查节点 2 上的 state,以验证其现已加入网络。在两分钟内,节点 2 从 child 转换到 router

> state
child
Done
...
> state
router
Done

5. 验证连接

在第三个终端窗口中,按 Ctrl+D 退出 ot-ctl 并返回容器的 bash 控制台。在此控制台中,使用 ping6 命令对其节点的 EID 进行 ping 操作。如果 ot-daemon RCP 实例成功连接到线程网络并与之通信,则 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 节点时遇到 Operation not permitted 错误(使用 mknod 命令),请确保根据此 Codelab 中提供的命令以根用户身份运行 Docker。此 Codelab 不支持在无根节点模式下运行 Docker。

7. 恭喜!

您已成功使用 OpenThread 模拟了第一个线程网络。棒极了!

在此 Codelab 中,您学习了如何执行以下操作:

  • 启动和管理 OpenThread Simulation Docker 容器
  • 模拟线程网络
  • 对线程节点进行身份验证
  • 使用 OpenThread 守护程序管理线程网络

如需详细了解线程和 OpenThread,请浏览以下参考文档:

或者,尝试在 Docker 容器中使用 OpenThread 边界路由器