使用 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

在您選擇的 OS 上安裝 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

--rm 選項會在您離開容器時刪除容器。如果您不想刪除容器,請勿使用這個選項。

請留意本程式碼研究室的必要標記:

  • --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 裝置執行連線偵測 (ping) 模擬的 Thread 裝置。

下圖說明基本的 Thread 網路拓撲。在本練習中,我們會模擬綠色圓圈內的兩個節點:Thread 領導者和 Thread 路由器,這兩者之間使用單一連線。

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

等待幾秒鐘,然後確認該裝置已成為執行緒領導者。領導者是負責管理路由器 ID 指派作業的裝置,

> state
leader
Done

查看指派給節點 1 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,請記下這個 EID 供日後使用。以上範例的輸出內容中的 EID 為:

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

2. 啟動節點 2

開啟新的終端機,然後在目前執行中的 Docker 容器中執行 bash 殼層,用於 Node 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 作業資料集相同的值,設定 Thread 網路金鑰和 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

在節點 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 的連線偵測節點 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:

> thread stop
Done

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

> state
router
Done
...
> state
leader
Done

確認後,請停止 Thread 並恢復原廠設定 Node 2,再結束顯示 Docker bash 提示。系統已恢復原廠設定,確保我們在此練習中使用的 Thread 網路憑證不會轉移到下一次練習。

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

您可能需要多次按下 enter,才能在執行 factoryreset 指令後再次顯示 > 提示。請勿結束 Docker 容器。

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

> factoryreset
>
> exit
root@c0f3912a74ff:/#

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

4. 透過調校功能驗證節點

在先前的練習中,您設定了包含兩部模擬裝置和已驗證連線的 Thread 網路。不過,這項設定只允許未經驗證的 IPv6 連結本機流量在裝置之間傳遞。如要在節點之間轉送全域 IPv6 流量 (透過 Thread 邊界路由器和網際網路),必須驗證節點。

必須有一部裝置擔任委員會驗證者,才能進行驗證。委員會是目前為新 Thread 裝置選取的驗證伺服器,以及授權者提供裝置加入網路所需的網路憑證。

在本練習中,我們將使用和之前一樣使用相同的雙節點拓撲。在驗證方面,Thread 主管會擔任委員會 (Commissioner),Thread Router 做為彙整者。

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

建立新的作業資料集,將其修訂為有效資料集,並啟動執行緒:

> 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. 啟動「佣金」角色

在節點 1 中,啟動「佣金」角色:

> commissioner start
Done

允許所有加入者 (使用*萬用字元) 搭配J01NME彙整憑證,以允許加入網路。「彙整者」是指由系統管理員新增至受委託 Thread 網路中的裝置。

> commissioner joiner add * J01NME
Done

3. 啟動彙整角色

在第二個終端機視窗中,前往 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) 已成功透過 Commissioner (Node 1) 驗證本身,並收到 Thread 網路憑證。

節點 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:/#

您可能需要多次按下 enter,才能在執行 factoryreset 指令後再次顯示 > 提示。

5. 使用 OpenThread Daemon 管理網路

在這個練習中,我們將模擬一個 CLI 執行個體 (一個嵌入式 SoC Thread 裝置) 和一個無線電合作處理器 (RCP) 執行個體。

ot-daemon 是 OpenThread Posix 應用程式的模式,使用 UNIX 通訊端做為輸入和輸出內容,因此 OpenThread 核心能以服務形式執行。用戶端可以使用 OpenThread CLI 做為通訊協定連線至通訊端,藉此與這項服務通訊。

ot-ctlot-daemon 提供的 CLI,用於管理及設定 RCP。這樣就能將 RCP 連線到 Thread 裝置建立的網路。

Docker

在這個練習中,每個節點 (終端機視窗) 都必須以 OpenThread 版本執行 Docker 容器。如果繼續先前的練習,同一個 Docker 容器中應該有兩個 bash 提示。如果仍未解決,請查看 Docker 疑難排解步驟。

使用 ot-daemon

這項練習會使用三個終端機視窗,對應以下項目:

  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

建立新的作業資料集,將其修訂為有效資料集,並啟動執行緒:

> 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

查看指派給節點 1 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
>

如「模擬 Thread 網路」步驟所述,一個是連結本機位址 (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,我們將將其稱為 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。因此,您可以採用與其他模擬的 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

在節點 1 (第一個終端機視窗) 中,啟動 Agenter,然後限制只有該 eui64 能加入會議:

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

在第三個終端機視窗中,開啟節點 2 的網路介面並加入網路:

> ifconfig up
Done
> joiner start J01NME
Done

... 請稍待片刻的確認 ...

Join success

RCP (Node 2) 作為彙整工具,已成功透過委員會 (Node 1) 驗證自身,並收到 Thread 網路憑證。

現在,在第三個終端機視窗中將節點 2 加入 Thread 網路:

> thread start
Done

4. 驗證網路驗證

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

> state
child
Done
...
> state
router
Done

5. 驗證連線能力

在第三個終端機視窗中,使用 Ctrl+Dexit 指令結束 ot-ctl,然後返回容器的 bash 控制台。在這個控制台中,對節點 1 進行連線偵測 (ping),方法是搭配使用節點 1 和 ping6 指令。如果 ot-daemon RCP 執行個體已成功加入 Thread 網路並與 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 提示:您可能需要確認應用程式是否正在運作,並視需要重新啟動 / 重新輸入。您未曾使用 --rm 選項建立的所有 Docker 容器仍應存在。

如要顯示正在執行的 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

只有在您想在離開容器時刪除容器的情況下,才需要使用 --rm 選項。

如果容器停止 (列在 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 指令),請務必根據本程式碼研究室提供的指令,以超級使用者的身分執行 Docker。這個程式碼研究室不支援在無根模式中執行 Docker。

7. 恭喜!

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

在本程式碼研究室中,您已瞭解如何:

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

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

或者,您也可以嘗試使用 Docker 容器中的 OpenThread 邊界路由器