在 Docker 中使用 OpenThread 模擬 Thread 網路

1. 簡介

26b7f4f6b3ea0700.png

Google 釋出的 OpenThreadThread 網路通訊協定的開放原始碼實作。Google Nest 已推出 OpenThread,讓開發人員能夠廣泛使用 Nest 產品採用的技術,藉此加快智慧聯網家庭的產品開發速度。

Thread 規格定義了以 IPv6 為基礎的可靠、安全且低功耗的無線裝置對裝置通訊通訊協定,適用於家用應用程式。OpenThread 執行所有 Thread 網絡層,包括 IPv6、6LoWPAN、IEEE 802.15.4,具有 MAC 安全、網絡鏈路建立和網格路由。

這個程式碼研究室將逐步引導您使用 Docker 模擬裝置中的 Thread 網路。

課程內容

  • 如何設定 OpenThread 建構工具鍊
  • 如何模擬 Thread 網路
  • 如何驗證 Thread 節點
  • 如何使用 OpenThread Daemon 管理 Thread 網路

軟硬體需求

  • Docker
  • Linux、網路轉送的基本知識

2. 設定 Docker

本程式碼研究室可在 Linux、Mac OS X 或 Windows 電腦上使用 Docker。建議您使用 Linux。

安裝 Docker

在您選擇的作業系統上安裝 Docker。

提取 Docker 映像檔

安裝 Docker 後,請開啟終端機視窗,然後提取 openthread/environment Docker 映像檔。這個映像檔具備預先建構且可用於此程式碼研究室的 OpenThread 和 OpenThread Daemon。

$ docker pull openthread/environment:latest

請注意,下載作業可能需要幾分鐘才能完成。

在終端機視窗中,從映像檔啟動 Docker 容器並連線到其 bash 殼層:

$ docker run --name codelab_otsim_ctnr -it --rm \
   --sysctl net.ipv6.conf.all.disable_ipv6=0 \
   --cap-add=net_admin openthread/environment bash

請注意此程式碼研究室所需的標記:

  • --sysctl net.ipv6.conf.all.disable_ipv6=0:這會啟用容器中的 IPv6
  • --cap-add=net_admin:啟用 NET_ADMIN 功能,可讓您執行網路相關作業,例如新增 IP 路徑

進入容器後,您應該會看到類似以下的提示:

root@c0f3912a74ff:/#

在上述範例中,c0f3912a74ff 是容器 ID。Docker 容器執行個體的容器 ID 會與這個程式碼研究室提示中顯示的容器 ID 不同。

使用 Docker

本程式碼研究室假設您已瞭解使用 Docker 的基本概念。程式碼的整個容器中應留在 Docker 容器中。

3. 模擬 Thread 網路

您將使用這個程式碼研究室的範例應用程式示範一個最低的 OpenThread 應用程式,透過基本指令列介面 (CLI) 來公開 OpenThread 設定和管理介面。

本練習將帶你完成幾個基本步驟,從另一個模擬的 Thread 裝置對一個模擬的 Thread 裝置進行連線偵測。

下圖說明基本的 Thread 網路拓撲。這次練習時,我們將模擬綠色圓圈中的兩個節點:Thread 領導者和 Thread Router,而兩者之間僅有一個連線。

6e3aa07675f902dc.png

建立網路

1. 啟動節點 1

如果您尚未完成這項作業,請在終端機視窗中啟動 Docker 容器並連線到其 bash 殼層:

$ 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 二進位檔為模擬的 Thread 裝置產生 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 + 文件描述符)時,也使用的值。在這個程式碼研究室中,模擬 Thread 裝置的每個執行個體都會使用不同的檔案描述元。

注意:為模擬裝置執行程序時,您只能使用本程式碼研究室所述的 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 通訊協定作業:

> thread start
Done

稍等幾秒鐘,確認裝置已成為 Thread 領導者。「領導者」是指負責處理路由器 ID 指派作業的裝置。

> state
leader
Done

查看分配到 Node 1's Thread 接口的 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 = 端點 ID (EID)

請在主控台輸出內容中找出 EID,並記下該 ID 供日後使用。在上述範例輸出中,EID 為:

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

2. 啟動節點 2

開啟新終端機,並在執行中的 Docker 容器中執行 bash 殼層,以便用於節點 2。

$ docker exec -it codelab_otsim_ctnr bash

在這個新的 bash 提示中,使用引數 2 來分割 CLI 程序。這是您第二個模擬的 Thread 裝置:

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

注意:如果在執行指令後沒有看到 > 提示,請按下 enter

使用與 Node 1's 作業資料集相同的值來設定執行緒網路金鑰和 PAN ID:

> dataset networkkey e4344ca17d1dca2a33f064992f31f786
Done
> dataset panid 0xc169
Done

將這個資料集做為使用中的資料集:

> dataset commit active
Done

啟動 IPv6 介面:

> ifconfig up
Done

啟動 Thread 通訊協定作業:

> thread start
Done

裝置會自行初始化為子項。Thread Child 等同於最終裝置,也就是僅透過家長裝置傳輸及接收單向流量的 Thread 裝置。

> state
child
Done

2 分鐘內,您應該會在「child」狀態切換至「router」。Thread 路由器可在 Thread 裝置之間轉送流量。也稱為「上層」。

> state
router
Done

驗證網路

如要驗證網狀網路,最簡單的方法是查看路由器表格。

1. 檢查連線狀態

在節點 2 上取得 RLOC16。RLOC16 是設備的 RLOC IPv6 地址的最後 16 位。

> rloc16
5800
Done

在 Node 1 上,檢查 Node 2's 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

驗證兩個模擬 Thread 裝置之間的連線。在節點 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 提示。

測試網路

現在您可以成功對兩部模擬的 Thread 裝置進行連線偵測 (ping),先將一個節點設為可測試,以便測試網狀網路。

返回節點 1 並停止執行緒:

> thread stop
Done

切換至節點 2 並查看狀態。在兩分鐘內,節點 2 偵測到主要節點 (節點 1) 處於離線狀態,您應該會看到節點 2 的轉場效果是網路的 leader

> state
router
Done
...
> state
leader
Done

確認後,請停止 Thread 並將節點 2 恢復原廠設定,然後再返回 Docker bash 提示。手機已恢復原廠設定,以確保這項練習作業所使用的 Thread 網路憑證不會移轉至下一個練習階段。

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

factoryreset 指令過後,您可能需要按 enter 幾次才能恢復顯示 > 提示。請勿結束 Docker 容器。

一併將節點恢復原廠設定並結束節點 1:

> factoryreset
>
> exit
root@c0f3912a74ff:/#

請參閱 OpenThread CLI 參考資料來探索所有可用的 CLI 指令。

4. 使用佣金驗證節點

在先前的練習中,您建立了 Thread 網路,其中包含兩部模擬裝置和經過驗證的連線。不過,這種做法只會允許未經驗證的 IPv6 連結本機流量在裝置之間傳送。如要在兩者之間傳輸全域 IPv6 流量 (並透過 Thread 邊界路由器進行網際網路),您必須驗證節點。

只有一部裝置可以做為佣金,才能進行驗證。委員是目前選擇的新 Thread 裝置驗證伺服器,以及提供裝置加入網路所需的網路憑證的授權服務供應商。

在本練習中,我們會採用與之前相同的雙節點拓撲。驗證時,執行緒主管將擔任顧問,是 Thread 路由器中的一員。

d6a67e8a0d0b5dcb.png

Docker

針對其餘練習中的每個節點 (終端機視窗),請確認您執行的是包含 OpenThread 版本的 Docker 容器。如果繼續先前的練習,同一個 Docker 容器中應該仍會出現兩個 bash 提示。否則,請參閱 Docker 疑難排解步驟,或是直接重做 Simulate a Thread network 練習。

1. 建立網路

在節點 1 中,啟動 CLI 程序:

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

注意:如果在執行指令後沒有看到 > 提示,請按下 enter

建立新的作業資料集,將其視為使用中的資料集並啟動 Thread:

> 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 通訊協定作業:

> thread start
Done

稍等幾秒鐘,確認裝置已成為 Thread 領導者:

> state
leader
Done

2. 啟動「主持人」角色

在 Node 1 上,啟動啟動器角色:

> commissioner start
Done

允許具有 J01NME 聯結憑證的所有加入者 (使用 * 萬用字元) 連線至網路。「人機」是一種由真人管理員新增至已委託 Thread Network 的裝置。

> commissioner joiner add * J01NME
Done

3. 啟動「加入者」角色

在第二個終端機視窗中,在 Docker 容器中啟動新的 CLI 程序。這是 Node 2。

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

在節點 2 上,使用 J01NME 連接者憑證啟用彙整者角色。

> ifconfig up
Done
> joiner start J01NME
Done

... 請稍候片刻確認 ...

Join success

As a Joiner, the device (Node 2) has successfully authenticated itself with the Commissioner (Node 1) and received the Thread Network credentials.

由於節點 2 已完成驗證,請啟動 Thread:

> thread start
Done

4. 驗證網路驗證

請查看節點 2 上的 state,以驗證節點是否已加入該網路。節點 2 會在兩分鐘內從 child 轉換到 router

> state
child
Done
...
> state
router
Done

5. 重新設定

為準備下一項運動,請重設設定。在每個節點上停止 Thread 並將恢復原廠設定,然後結束模擬的 Thread 裝置:

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

factoryreset 指令過後,您可能需要按 enter 幾次才能恢復顯示 > 提示。

5. 使用 OpenThread Daemon 管理網路

在本練習中,我們將模擬一個 CLI 執行個體 (單一內嵌 SoC Thread 裝置) 和一個無線電輔助處理器 (RCP) 執行個體。

ot-daemon 是 OpenThread Posix 應用程式的模式,使用 UNIX 通訊端做為輸入和輸出方式,讓 OpenThread 以服務形式執行。用戶端可以使用 OpenThread CLI 做為通訊協定,來連線至通訊端。

ot-ctl 是由 ot-daemon 提供的 CLI,可管理及設定 RCP。使用這項功能時,我們會將 RCP 連接到 Thread 裝置所建立的網路。

Docker

針對此練習中的每個節點 (終端機視窗),請確認您執行的是包含 OpenThread 版本的 Docker 容器。如果繼續先前的練習,同一個 Docker 容器中應有 bash 個提示已開啟。如果沒有,請參閱 Docker 疑難排解步驟。

使用 Dam-ememon

這個運動將使用三個終端機視窗,對應的內容如下:

  1. 模擬 Thread 裝置的 CLI 執行個體 (節點 1)
  2. ot-daemon 項程序
  3. ot-ctl 個 CLI 執行個體

1. 啟動節點 1

在第一個終端機視窗中,為模擬的 Thread 裝置啟動 CLI 程序:

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

注意:如果在執行指令後沒有看到 > 提示,請按下 enter

建立新的作業資料集,將其視為使用中的資料集並啟動 Thread:

> 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 通訊協定作業:

> thread start
Done

查看分配到 Node 1's Thread 接口的 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. 啟動 Da-ememon

在第二個終端機視窗中,建立 tun 裝置節點並設定讀取/寫入權限:

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

這個裝置是用來在虛擬裝置上進行封包傳輸和接收作業。如果裝置已建立完成,系統會顯示錯誤訊息,這是正常現象,您可以予以忽略。

啟動 RCP 節點的 ot-daemon,我們將呼叫 Node 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) 委任給任何 Thread 網路。這時「ot-ctl」就能派上用場。ot-ctl 使用與 OpenThread CLI 應用程式相同的 CLI。因此,您可以控制 ot-daemon 節點,方法與其他模擬的 Thread 裝置相同。

開啟第三個終端機視窗並執行現有的容器:

$ 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

As a Joiner, the RCP (Node 2) has successfully authenticated itself with the Commissioner (Node 1) and received the Thread Network credentials.

現在將 Node 2 加入 Thread 網路 (再次在第三個終端機視窗中):

> thread start
Done

4. 驗證網路驗證

在第三個終端機中,查看節點 2 上的 state,確認該網路現已加入網路。節點 2 會在兩分鐘內從 child 轉換到 router

> state
child
Done
...
> state
router
Done

5. 驗證連線

在第三個終端機視窗中,使用 Ctrl+Dexit 指令退出 ot-ctl,然後返回容器的 bash 主控台。使用此控制台,在 ping6 命令中使用其 EID 連接 Node 1。如果 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 錯誤,請務必按照本程式碼研究室提供的指令,以根使用者的身分執行 Docker。這個程式碼研究室不支援在無根模式中執行 Docker。

7. 恭喜!

您已成功使用 OpenThread 模擬第一個執行緒網路。太棒了!

在這個程式碼研究室中,您已學到以下內容:

  • 啟動及管理 OpenThread 模擬 Docker 容器
  • 模擬 Thread 網路
  • 驗證 Thread 節點
  • 透過 OpenThread Daemon 管理 Thread 網路

如要進一步瞭解 Thread 和 OpenThread,請參閱以下參考資料:

或者,您也可以嘗試在 Docker 容器中使用 OpenThread Border Router