安裝 Docker
在 Raspberry Pi 上:
安裝 Docker:
curl -sSL https://get.docker.com | sh
如果您想以非 root 使用者身分使用 Docker,且不必在每個指令前加上
sudo
,請修改使用者設定。登出以利變更生效:sudo usermod -aG docker $USER
啟動 Docker (如果尚未執行的話):
sudo dockerd
啟用 IP 轉送
Linux 通常會預設停用 IP 轉送。執行 setup-host
指令碼,在主機系統上啟用 IP 轉送功能。
curl -sSL
https://raw.githubusercontent.com/openthread/ot-br-posix/refs/heads/main/etc/docker/border-router/setup-host | bash
取得 OTBR Docker 映像檔
如要取得 OTBR Docker 映像檔,請直接從 OpenThread Docker Hub 擷取,或是複製 OTBR 存放區,並在本機建構內含的 Dockerfile。
從 Docker Hub 提取映像檔
提取映像檔:
docker pull openthread/border-router:latest
它現在應該會顯示在 Docker 映像檔清單中:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE openthread/border-router latest 08666d77013d 2 hours ago 171MB
建構 Dockerfile
如要自行建立映像檔,請複製 OpenThread Border Router 存放區,並建構所附 Dockerfile。
安裝 Git:
sudo apt install git
複製 OTBR 存放區:
git clone --depth=1 https://github.com/openthread/ot-br-posix
cd ot-br-posix
建構 Dockerfile:
docker build --no-cache -t openthread/border-router -f etc/docker/border-router/Dockerfile .
建立 OTBR 設定檔
建立 otbr-env.list
檔案,以便儲存 OTBR Docker 設定。
OT_RCP_DEVICE=spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=1000000 OT_INFRA_IF=wlan0 OT_THREAD_IF=wpan0 OT_LOG_LEVEL=7
OT_RCP_DEVICE
:指定與 Thread Radio 共用處理器 (RCP) 的連線。OT_INFRA_IF
:用於相鄰基礎架構網路 (通常為 Wi-Fi 或乙太網路) 的網路介面。OT_THREAD_IF
:用於 Thread 網路的網路介面名稱。OT_LOG_LEVEL
:OpenThread 產生的記錄詳細程度。
啟動 OTBR Docker 容器
使用 OTBR 映像檔建立及執行新的容器。
docker run --name=otbr --detach --network=host --cap-add=NET_ADMIN --device=/dev/ttyACM0 --device=/dev/net/tun --volume=/var/lib/otbr:/data --env-file=otbr-env.list --restart=always openthread/border-router
docker run
:執行 Docker 容器的基本指令。--name=otbr
:將「otbr」名稱指派給執行中的容器。這樣一來,您日後就能更輕鬆地參照容器,執行停止、啟動或檢查等動作。--detach
:在分離模式下執行容器,也就是在背景執行,且不會將終端機附加至容器的標準輸入、輸出或錯誤串流。--network=host
:讓容器直接使用主機機器的網路堆疊。這通常是 OTBR 的必要條件,因為 OTBR 需要直接存取網路介面。--cap-add=NET_ADMIN
:授予容器NET_ADMIN
功能。這是容器執行網路管理工作 (例如設定網路介面和路由) 的必要條件。--device=/dev/ttyACM0
:將主機的/dev/ttyACM0
裝置對應至容器。這個序列埠通常會連線至 Thread Radio 共同處理器 (RCP)。具體裝置名稱 (ttyACM0) 可能會因系統而異。--device=/dev/net/tun
:將主機的/dev/net/tun
裝置對應至容器。這是建立及使用虛擬網路介面 (由 OTBR 使用) 的必要條件。--volume=/var/lib/otbr:/data
:將主機目錄/var/lib/otbr
掛接至/data
的容器。這樣一來,即使容器已停止或重新啟動,仍可保留網路設定等資料。--env-file=otbr-env.list
:從指定檔案讀取環境變數,並在容器中設定這些變數。這些環境變數可能是 OTBR 的設定參數。--restart=always
:設定 Docker 精靈,讓系統在容器停止時自動重新啟動容器。這可確保 OTBR 一律處於執行狀態。openthread/border-router:指定要用於容器的 Docker 映像檔。在本例中,這是官方 OpenThread 邊界路由器映像檔。
查看 Docker 記錄檔
在主機上使用下列指令查看記錄:
docker logs otbr
如果 OTBR 執行成功,輸出內容應如下所示:
s6-rc: info: service mdns: starting s6-rc: info: service s6rc-oneshot-runner: starting Starting mDNSResponder... Default: mDNSResponder (Engineering Build) (Mar 26 2025 19:39:09) starting s6-rc: info: service mdns successfully started s6-rc: info: service s6rc-oneshot-runner successfully started s6-rc: info: service fix-attrs: starting s6-rc: info: service fix-attrs successfully started s6-rc: info: service legacy-cont-init: starting s6-rc: info: service legacy-cont-init successfully started s6-rc: info: service otbr-agent: starting Configuring OpenThread firewall... Configuring OpenThread NAT64... Starting otbr-agent... [NOTE]-AGENT---: Running 0.3.0-da4b5cf [NOTE]-AGENT---: Thread version: 1.4.0 [NOTE]-AGENT---: Thread interface: wpan0 [NOTE]-AGENT---: Radio URL: spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=1000000 [NOTE]-AGENT---: Radio URL: trel://wlan0 [NOTE]-ILS-----: Infra link selected: wlan0 [INFO]-RCP_HOS-: OpenThread log level changed to 5 49d.18:38:43.301 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:0, cmd:RESET 49d.18:38:43.301 [D] P-SpinelDrive-: Waiting response: key=0 49d.18:38:43.311 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:0, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:RESET_POWER_ON 49d.18:38:43.311 [I] P-SpinelDrive-: co-processor reset: RESET_POWER_ON 49d.18:38:43.311 [C] P-SpinelDrive-: Software reset co-processor successfully 49d.18:38:43.311 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:PROTOCOL_VERSION 49d.18:38:43.311 [D] P-SpinelDrive-: Waiting response: key=1 49d.18:38:43.312 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_IS, key:PROTOCOL_VERSION, major:4, minor:3 49d.18:38:43.312 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:NCP_VERSION 49d.18:38:43.312 [D] P-SpinelDrive-: Waiting response: key=2 49d.18:38:43.313 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_IS, key:NCP_VERSION, version:OPENTHREAD/7a25828-dirty; NRF52840; Mar 25 2025 15:51:02 49d.18:38:43.313 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:CAPS 49d.18:38:43.313 [D] P-SpinelDrive-: Waiting response: key=5 49d.18:38:43.314 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_IS, key:CAPS, caps:COUNTERS UNSOL_UPDATE_FILTER 802_15_4_2450MHZ_OQPSK CONFIG_RADIO MAC_RAW RCP_API_VERSION RCP_MIN_HOST_API_VERSION OPENTHREAD_LOG_METADATA 49d.18:38:43.376 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:HWADDR 49d.18:38:43.376 [D] P-RadioSpinel-: Wait response: tid=1 key=8 49d.18:38:43.376 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_IS, key:HWADDR, eui64:f4ce3693ab886040 49d.18:38:43.376 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:2, cmd:PROP_VALUE_GET, key:RCP_API_VERSION 49d.18:38:43.376 [D] P-RadioSpinel-: Wait response: tid=2 key=176 49d.18:38:43.377 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:2, cmd:PROP_VALUE_IS, key:RCP_API_VERSION, version:11 49d.18:38:43.377 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:3, cmd:PROP_VALUE_GET, key:RCP_MIN_HOST_API_VERSION 49d.18:38:43.377 [D] P-RadioSpinel-: Wait response: tid=3 key=177 49d.18:38:43.378 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:3, cmd:PROP_VALUE_IS, key:RCP_MIN_HOST_API_VERSION, min-host-version:4 49d.18:38:43.378 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:4, cmd:PROP_VALUE_GET, key:RADIO_CAPS 49d.18:38:43.378 [D] P-RadioSpinel-: Wait response: tid=4 key=4619 49d.18:38:43.379 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:4, cmd:PROP_VALUE_IS, key:RADIO_CAPS, caps:255 49d.18:38:43.410 [D] P-Trel--------: platformTrelInit(aTrelUrl:"trel://wlan0") 49d.18:38:43.410 [D] P-Trel--------: otSysTrelInit(aInterfaceName:"wlan0") [DEBG]-TrelDns-: Initialized on netif "wlan0" [DEBG]-TrelDns-: Netif wlan0 is ready: index = 3 49d.18:38:43.411 [I] P-Netif-------: Sent request#1 to set addr_gen_mode to 1 00:00:00.000 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:5, cmd:PROP_VALUE_GET, key:PHY_CHAN_SUPPORTED 00:00:00.000 [D] P-RadioSpinel-: Wait response: tid=5 key=34 00:00:00.001 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:5, cmd:PROP_VALUE_IS, key:PHY_CHAN_SUPPORTED, channelMask:0x07fff800 00:00:00.001 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:6, cmd:PROP_VALUE_SET, key:PHY_ENABLED, enabled:1 00:00:00.001 [D] P-RadioSpinel-: Wait response: tid=6 key=32 00:00:00.003 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:6, cmd:PROP_VALUE_IS, key:PHY_ENABLED, enabled:1 00:00:00.003 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:7, cmd:PROP_VALUE_SET, key:MAC_15_4_PANID, panid:0xffff 00:00:00.003 [D] P-RadioSpinel-: Wait response: tid=7 key=54 00:00:00.003 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:7, cmd:PROP_VALUE_IS, key:MAC_15_4_PANID, panid:0xffff 00:00:00.003 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:8, cmd:PROP_VALUE_SET, key:MAC_15_4_SADDR, saddr:0x0000 00:00:00.003 [D] P-RadioSpinel-: Wait response: tid=8 key=53 00:00:00.004 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:8, cmd:PROP_VALUE_IS, key:MAC_15_4_SADDR, saddr:0x0000 00:00:00.004 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:9, cmd:PROP_VALUE_GET, key:PHY_RX_SENSITIVITY 00:00:00.004 [D] P-RadioSpinel-: Wait response: tid=9 key=39 00:00:00.005 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:9, cmd:PROP_VALUE_IS, key:PHY_RX_SENSITIVITY, sensitivity:-100 00:00:00.005 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:10, cmd:PROP_VALUE_SET, key:RCP_MAC_KEY, keyIdMode:8, keyId:1, prevKey:***, currKey:***, nextKey:*** 00:00:00.005 [D] P-RadioSpinel-: Wait response: tid=10 key=2048 00:00:00.007 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:10, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK 00:00:00.007 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:11, cmd:PROP_VALUE_SET, key:MAC_15_4_LADDR, laddr:a2566e135ad5df32 00:00:00.007 [D] P-RadioSpinel-: Wait response: tid=11 key=52 00:00:00.008 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:11, cmd:PROP_VALUE_IS, key:MAC_15_4_LADDR, laddr:a2566e135ad5df32 00:00:00.008 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:12, cmd:PROP_VALUE_SET, key:MAC_15_4_SADDR, saddr:0xfffe 00:00:00.008 [D] P-RadioSpinel-: Wait response: tid=12 key=53 00:00:00.009 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:12, cmd:PROP_VALUE_IS, key:MAC_15_4_SADDR, saddr:0xfffe 00:00:00.009 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:13, cmd:PROP_VALUE_SET, key:MAC_SRC_MATCH_SHORT_ADDRESSES, saddr:none 00:00:00.009 [D] P-RadioSpinel-: Wait response: tid=13 key=4868 00:00:00.010 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:13, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK 00:00:00.011 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:14, cmd:PROP_VALUE_SET, key:MAC_SRC_MATCH_EXTENDED_ADDRESSES, extaddr:none 00:00:00.011 [D] P-RadioSpinel-: Wait response: tid=14 key=4869 00:00:00.012 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:14, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK 00:00:00.012 [I] CslTxScheduler: Set frame request ahead: 6200 usec 00:00:00.012 [I] ChildSupervsn-: Timeout: 0 -> 190 00:00:00.013 [D] P-Trel--------: PrepareSocket() [DEBG]-TrelDns-: Start browsing _trel._udp services ... 00:00:00.013 [I] TrelInterface-: Enabled interface, local port:52346 00:00:00.013 [I] RoutingManager: Initializing - InfraIfIndex:3 00:00:00.013 [I] InfraIf-------: Init infra netif 3 00:00:00.013 [N] RoutingManager: No valid /48 BR ULA prefix found in settings, generating new one 00:00:00.038 [I] Settings------: Saved BrUlaPrefix fd92:6043:f0e2::/48 00:00:00.038 [N] RoutingManager: BR ULA prefix: fd92:6043:f0e2::/48 (generated) 00:00:00.038 [I] RoutingManager: Generated local OMR prefix: fd92:6043:f0e2:1::/64 00:00:00.038 [I] RoutingManager: Generated local NAT64 prefix: fd92:6043:f0e2:2:0:0::/96 00:00:00.038 [N] RoutingManager: Local on-link prefix: fdde:ad00:beef:cafe::/64 00:00:00.038 [I] InfraIf-------: State changed: NOT RUNNING -> RUNNING 00:00:00.038 [I] RoutingManager: Enabling 00:00:00.038 [I] Nat64---------: IPv4 CIDR for NAT64: 192.168.255.0/24 (actual address pool: 192.168.255.1 - 192.168.255.254, 254 addresses) [INFO]-UTILS---: Set state callback: OK 00:00:00.039 [I] Nat64---------: NAT64 translator is now NotRunning [DEBG]-TrelDns-: mDNS Publisher is Ready [INFO]-TrelDns-: TREL DNS-SD Is Now Ready: Netif=wlan0(3), SubscriberId=1, Register=! [INFO]-MDNS----: Subscribe service ._trel._udp (total 1) [INFO]-MDNS----: DNSServiceBrowse _trel._udp [INFO]-BA------: Start Thread Border Agent [INFO]-ADPROXY-: Started [INFO]-DPROXY--: Started [INFO]-APP-----: Co-processor version: OPENTHREAD/7a25828-dirty; NRF52840; Mar 25 2025 15:51:02 00:00:00.039 [I] Notifier------: StateChanged (0x40038210) [MLAddr NetData PanId NetName ExtPanId Nat64] 00:00:00.041 [I] Platform------: Execute command `ipset flush otbr-ingress-allow-dst-swap` = 0 00:00:00.042 [I] Platform------: Execute command `ipset flush otbr-ingress-deny-src-swap` = 0 00:00:00.044 [I] Platform------: Execute command `ipset add otbr-ingress-deny-src-swap fdde:ad00:beef:0::/64 -exist` = 0 00:00:00.046 [I] Platform------: Execute command `ipset swap otbr-ingress-deny-src-swap otbr-ingress-deny-src` = 0 00:00:00.047 [I] Platform------: Execute command `ipset swap otbr-ingress-allow-dst-swap otbr-ingress-allow-dst` = 0 00:00:00.047 [I] P-Netif-------: NAT64 CIDR updated to 192.168.255.0/24. 00:00:00.047 [I] P-Netif-------: Sent request#2 to delete route 192.168.255.0/24 00:00:00.047 [I] P-Netif-------: Deleting route for NAT64 00:00:00.047 [I] RouterTable---: Route table 00:00:00.047 [I] TrelInterface-: Registering DNS-SD service: port:52346, txt:"xa=a2566e135ad5df32, xp=dead00beef00cafe" [DEBG]-TrelDns-: Register _trel._udp service: port=52346, TXT=24 bytes [DEBG]-TrelDns-: Using instance name a2566e135ad5df32 [INFO]-MDNS----: Registering service a2566e135ad5df32._trel._udp 00:00:00.058 [I] Settings------: Saved BorderAgentId {id:27a9a3c44dd733402e8a940a20fc1051} [INFO]-BA------: Result of decoding MeshCoP TXT data from OT: OK [INFO]-BA------: Publish meshcop service OpenThread BorderRouter #DF32._meshcop._udp.local. [INFO]-MDNS----: Registering service OpenThread BorderRouter #DF32._meshcop._udp 00:00:00.059 [I] P-Netif-------: Host netif is down 00:00:00.059 [I] P-Netif-------: Succeeded to process request#1 00:00:00.060 [W] P-Netif-------: Failed to process request#2: No such process s6-rc: info: service otbr-agent successfully started ...