OpenThread released by Google is an open-source implementation of the Thread networking protocol. Google Nest has released OpenThread to make the technology used in Nest products broadly available to developers to accelerate the development of products for the connected home.
The Thread specification defines an IPv6-based reliable, secure and low-power wireless device-to-device communication protocol for home applications. OpenThread implements all Thread networking layers including IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, and Mesh Routing.
This Codelab will walk you through simulating a Thread network on emulated devices.
wpantund
Git is required to complete this Codelab. Download and install it before continuing.
Once installed, follow the instructions for your specific OS to download and build OpenThread.
XCode is required to install and build OpenThread on Mac OS X.
After XCode is installed, install the XCode Command Line Tools:
$ xcode-select --install
These installation instructions have been tested on Ubuntu Server 14.04 LTS and Mac OS X Sierra 10.12.6.
Install OpenThread. The bootstrap
commands make sure the toolchain is installed and the environment is properly configured:
$ mkdir -p ~/src $ cd ~/src $ git clone --recursive https://github.com/openthread/openthread.git $ cd openthread $ ./script/bootstrap $ ./bootstrap
Install wpantund
:
$ cd ~/src $ git clone --recursive https://github.com/openthread/wpantund.git $ cd wpantund $ ./script/bootstrap $ ./bootstrap.sh $ ./configure $ make $ sudo make install
Sometimes there are issues with dbus
when it comes to wpantund
. Force a restart of dbus
to be safe:
$ sudo service dbus restart
If you prefer Windows, we recommend trying the Docker version of this Codelab.
Once your installation is complete, build the example OpenThread application. For this Codelab we are using the emulated POSIX example.
$ cd ~/src/openthread $ make -f examples/Makefile-posix
The example application you'll use for this Codelab demonstrates a minimal OpenThread application that exposes the OpenThread configuration and management interfaces via a basic command-line interface (CLI).
This exercise takes you through the minimal steps required to ping one emulated Thread device from another emulated Thread device.
The figure below describes a basic Thread network topology. For this exercise, we'll emulate the two nodes within the green circle: a Thread Leader and Thread Router with a single connection between them.
Navigate to the openthread
directory and spawn the CLI process for an emulated Thread device using the ot-cli-ftd
binary.
$ cd ~/src/openthread $ ./output/x86_64-unknown-linux-gnu/bin/ot-cli-ftd 1
This binary implements an OpenThread device emulated on top of POSIX. The IEEE 802.15.4 radio driver is implemented on top of UDP (IEEE 802.15.4 frames are passed within UDP payloads).
The argument of 1
is a file descriptor that represents the least-significant bits of the "factory-assigned" IEEE EUI-64 for the emulated device. This value is also used when binding to a UDP port for IEEE 802.15.4 radio emulation (port = 9000 + file descriptor). Each instance of an emulated Thread device in this Codelab will use a different file descriptor.
Note: Only use file descriptors of 1
or greater as noted in this Codelab when spawning the process for an emulated device. A file descriptor of 0
is reserved for other use.
If you don't see the >
prompt after running this command, press enter
.
Create a new Operational Dataset and commit it as the active one. The Operational Dataset is the configuration for the Thread network you are creating.
> 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 Master Key: e4344ca17d1dca2a33f064992f31f786 Network Name: OpenThread-c169 PAN ID: 0xc169 PSKc: ebb4f2f8a68026fc55bcf3d7be3e6fe4 Security Policy: 0, onrcb Done > dataset commit active Done
Bring up the IPv6 interface:
> ifconfig up Done
Start Thread protocol operation:
> thread start Done
Wait a few seconds and verify that the device has become the Thread Leader. The Leader is the device responsible for managing router ID assignment.
> state leader Done
View the IPv6 addresses assigned to Node 1's Thread interface (your output will be different):
> 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
Note the specific IPv6 address types:
fd
= mesh-localfe80
= link-localMesh-local address types are classified further:
ff:fe00
= Router Locator (RLOC)ff:fe00
= Endpoint Identifier (EID)Identify the EID in your console output make a note of it for later use. In the sample output above, the EID is:
fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6
Open a new terminal and navigate to the openthread
directory and spawn the CLI process. This is your second emulated Thread device:
$ cd ~/src/openthread $ ./output/x86_64-unknown-linux-gnu/bin/ot-cli-ftd 2
If you don't see the >
prompt after running this command, press enter
.
Configure the Thread Master Key and PAN ID, using the same values as Node 1's Operational Dataset:
> dataset masterkey e4344ca17d1dca2a33f064992f31f786 Done > dataset panid 0xc169 Done > dataset commit active Done
Bring up the IPv6 interface:
> ifconfig up Done
Start Thread protocol operation:
> thread start Done
The device will initialize itself as a Child. A Thread Child is equivalent to an End Device, which is a Thread device that transmits and receives unicast traffic only with a Parent device.
> state child Done
Within 2 minutes you should see the state switch from child
to router
. A Thread Router is capable of routing traffic between Thread devices. It is also referred to as a Parent.
> state router Done
An easy way to verify the mesh network is to look at the router table.
On Node 2, get the RLOC16. The RLOC16 is the last 16 bits of the device's RLOC IPv6 address.
> rloc16 5800 Done
On Node 1, check the router table for Node 2's RLOC16. Make sure Node 2 has switched to the router state first.
> router table | ID | RLOC16 | Next Hop | Path Cost | LQI In | LQI Out | Age | Extended MAC | +----+--------+----------+-----------+--------+---------+-----+------------------+ | 20 | 0x5000 | 63 | 0 | 0 | 0 | 0 | 96da92ea13534f3b | | 22 | 0x5800 | 63 | 0 | 3 | 3 | 23 | 5a4eb647eb6bc66c |
Node 1's RLOC of 0xa800
is found in the table, confirming that it is connected to the mesh.
Verify connectivity between the two emulated Thread devices. In Node 2, ping
the EID assigned to Node 1:
> 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
Press enter
to return to the >
CLI prompt.
Now that you can successfully ping between two emulated Thread devices, test the mesh network by taking one node offline.
Return to Node 1 and stop Thread:
> thread stop Done
Switch to Node 2 and check the state. Within two minutes, Node 2 detects that the leader (Node 1) is offline, and you should see Node 2 transition to be the leader
of the network:
> state router Done ... > state leader Done
Once confirmed, stop Thread and factory reset Node 2 before exiting. A factory reset is done to ensure that the Thread network credentials we used in this exercise are not carried over to the next exercise.
> thread stop Done > factoryreset > > exit
Also factory reset and exit Node 1:
> factoryreset > > exit
See the OpenThread CLI Reference to explore all available CLI commands.
In the previous exercise, you set up a Thread network with two simulated devices and verified connectivity. However, this only allows unauthenticated IPv6 link-local traffic to pass between devices. To route global IPv6 traffic between them (and the Internet via a Thread border router), nodes must be authenticated.
In order to authenticate, one device must act as a Commissioner. The Commissioner is the currently elected authentication server for new Thread devices, and the authorizer for providing the network credentials required for the devices to join the network.
In this exercise, we will use the same two-node topology as before. For authentication, the Thread Leader will act as the Commissioner, the Thread Router as a Joiner.
If continuing from the previous exercise, you should already have two terminal windows open. If not, make sure two are open and ready to use. One will serve as Node 1, the other as Node 2.
In Node 1, spawn the CLI process:
$ cd ~/src/openthread $ ./output/x86_64-unknown-linux-gnu/bin/ot-cli-ftd 1
If you don't see the >
prompt after running this command, hit enter
.
Create a new Operational Dataset, commit it as the active one, and start 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 Master Key: a77fe1d03b0e8028a4e13213de38080e Network Name: OpenThread-8f37 PAN ID: 0x8f37 PSKc: f9debbc1532487984b17f92cd55b21fc Security Policy: 0, onrcb Done > dataset commit active Done > ifconfig up Done > thread start Done
Wait a few seconds and verify that the device has become a Thread Leader:
> state leader Done
While still on Node 1, start the Commissioner role:
> commissioner start Done
Allow any Joiner (by using the *
wildcard) with the J01NME
Joiner Credential to commission onto the network. A Joiner is a device that is added by a human administrator to a commissioned Thread Network.
> commissioner joiner add * J01NME Done
In a second terminal window, spawn a new CLI process. This is Node 2.
$ cd ~/src/openthread $ ./output/x86_64-unknown-linux-gnu/bin/ot-cli-ftd 2
On Node 2, enable the Joiner role using the J01NME
Joiner Credential.
> ifconfig up Done > joiner start J01NME Done
... wait a few seconds for confirmation ...
Join success
As a Joiner, the device (Node 2) has successfully authenticated itself with the Commissioner (Node 1) and received the Thread Network credentials.
Now that Node 2 is authenticated, start Thread:
> thread start Done
Check the state
on Node 2, to validate that it has now joined the network. Within two minutes, Node 2 transitions from child
to router
:
> state child Done ... > state router Done
To prepare for the next exercise, reset the configuration. On each Node, stop Thread, do a factory reset, and exit the emulated Thread device:
> thread stop Done > factoryreset > > exit
You may have to press enter
a few times to bring the >
prompt back after a factoryreset
command.
For this exercise, we are going to simulate one CLI instance (a single embedded SoC Thread device) and one Network Co-Processor (NCP) instance.
wpantund
is a user-space network interface driver/daemon that provides a native IPv6 network interface to a low-power wireless NCP. It can be used to simplify supporting Thread connectivity on Unix-like operating systems, and is designed to marshall all access to the NCP, ensuring that it always remains in a consistent and well-defined state.
wpanctl
is a CLI provided by wpantund
to manage and configure the NCP. Using this, we'll connect the NCP to the network created by the Thread device.
This exercise will use three terminal windows, corresponding to the following:
wpantund
processwpanctl
CLI instanceIf continuing from the previous exercise you should already have two terminal windows open. Open a third to ensure you have three terminal windows available for this exercise.
In the first terminal window, spawn the CLI process for your emulated Thread device:
$ cd ~/src/openthread $ ./output/x86_64-unknown-linux-gnu/bin/ot-cli-ftd 1
If you don't see the >
prompt after running this command, hit enter
.
Create a new Operational Dataset, commit it as the active one, and start 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 Master Key: ba6e886c7af50598df1115fa07658a83 Network Name: OpenThread-34e4 PAN ID: 0x34e4 PSKc: 38d6fd32c866927a4dfcc06d79ae1192 Security Policy: 0, onrcb Done > dataset commit active Done > ifconfig up Done > thread start Done
View the IPv6 addresses assigned to Node 1's Thread interface:
> 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 >
As explained in the Simulate a Thread network step, one address is link-local (fe80
) and three are mesh-local (fd
). The EID is the mesh-local address that does not contain ff:fe00
in the address. In this sample output, the EID is fd55:cf34:dea5:7994:460:872c:e807:c4ab
.
Identify the specific EID from your ipaddr
output, which will be used to communicate with the node.
In the second terminal window, navigate to the openthread
directory, and start wpantund
with an interface of utun6
:
$ cd ~/src/openthread $ sudo /usr/local/sbin/wpantund -o Config:NCP:SocketPath \ "system:output/x86_64-unknown-linux-gnu/bin/ot-ncp-ftd 2" \ -o Daemon:SyslogMask " -info" \ -o Config:TUN:InterfaceName utun6
When successful, wpantund
generates output similar to the following:
wpantund[66]: Starting wpantund 0.08.00d (May 24 2019 23:14:00) . . . wpantund[66]: SOURCE_VERSION = 0.07.01-124-g038e8b0 wpantund[66]: BUILD_VERSION = 0.07.01-153-g6752c40 wpantund[66]: Configuration file "/etc/wpantund.conf" read. wpantund[66]: Ready. Using DBUS bus ":1.0" wpantund[66]: Running as root without dropping privileges! wpantund[67]: About to exec "output/x86_64-unknown-linux-gnu/bin/ot-ncp-ftd 2" wpantund[66]: [-NCP-]: NCP was reset (STATUS_RESET_POWER_ON, 112) wpantund[66]: State change: "uninitialized" -> "offline" wpantund[66]: NCP is running "OPENTHREAD/20180926-00638-g4aca0256; POSIX; May 24 2019 23:17:23" wpantund[66]: Driver is running "0.08.00d (0.07.01-124-g038e8b0/0.07.01-153-g6752c40; May 24 2019 23:14:00)" wpantund[66]: Network is not joinable wpantund[66]: Resetting interface(s). . . wpantund[66]: Finished initializing NCP
The network interface is currently down because we haven't told wpantund
to connect to anything yet. This is where wpanctl
comes in.
In a third terminal window, start wpanctl
for the interface utun6
:
$ sudo /usr/local/bin/wpanctl -I utun6 wpanctl:utun6>
Check the status
of the interface you just created:
wpanctl:utun6> status utun6 => [ "NCP:State" => "offline" "Daemon:Enabled" => true "NCP:Version" => "OPENTHREAD/20180926-00638-g4aca0256; POSIX; May 24 2019 23:17:23" "Daemon:Version" => "0.08.00d (0.07.01-124-g038e8b0/0.07.01-153-g6752c40; May 24 2019 23:14:00)" "Config:NCP:DriverName" => "spinel" "NCP:HardwareAddress" => [18B4300000000002] ]
Use the scan
command to see the network you created earlier on Node 1:
wpanctl:utun6> scan | Joinable | NetworkName | PAN ID | Ch | XPanID | HWAddr | RSSI ---+----------+--------------------+--------+----+------------------+------------------+------ 1 | NO | "OpenThread-34e4" | 0x34E4 | 13 | 97D584BCD493B824 | 9ED8AAB6482F4CDC | -20
Note the row index of 1
(the first column in the table). You'll need this to join the network.
Add the network key you specified on Node 1 to the interface:
wpanctl:utun6> set Network:Key --data ba6e886c7af50598df1115fa07658a83
Join the network using the join
command and the row index from the earlier scan
:
wpanctl:utun6> join 1 Joining "OpenThread-34e4" 97D584BCD493B824 as node type "end-device" Successfully Joined!
A quick status
check now yields more information:
wpanctl:utun6> status utun6 => [ "NCP:State" => "associated" "Daemon:Enabled" => true "NCP:Version" => "OPENTHREAD/20180926-00638-g4aca0256; POSIX; May 24 2019 23:17:23" "Daemon:Version" => "0.08.00d (0.07.01-124-g038e8b0/0.07.01-153-g6752c40; May 24 2019 23:14:00)" "Config:NCP:DriverName" => "spinel" "NCP:HardwareAddress" => [18B4300000000002] "NCP:Channel" => 13 "Network:NodeType" => "end-device" "Network:Name" => "OpenThread-34e4" "Network:XPANID" => 0x97D584BCD493B824 "Network:PANID" => 0x34E4 "IPv6:LinkLocalAddress" => "fe80::544f:440:b6e:9339" "IPv6:MeshLocalAddress" => "fd55:cf34:dea5:7994:8bd3:3d1d:da77:abe3" "IPv6:MeshLocalPrefix" => "fd55:cf34:dea5:7994::/64" "com.nestlabs.internal:Network:AllowingJoin" => false ]
Quit wpanctl
and ping Node 1, using its EID with the ping6
command. If the wpantund
interface is successfully joined to and communicating with the Thread network, the ping succeeds.
wpanctl:utun6> quit $ ping6 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
You can also check the wpantund
log in the second terminal window to confirm the scan by wpanctl
, as well as the successful join:
wpantund[66]: Scan -> Name:OpenThread-34e4 , PanId:0x34E4, Ch:13, Joinable:NO , XPanId:0x97D584BCD493B824, HwAddr:0x9ED8AAB6482F4C24, RSSI:-20 , LQI:0 , ProtoId:3 , Version: 0, ShortAddr:0xFFFF wpantund[66]: State change: "offline" -> "associating" wpantund[66]: State change: "associating" -> "associated" wpantund[66]: Node type change: "unknown" -> "end-device" wpantund[66]: Adding address "fe80::4a52:6216:6eb7:98fc/64" to NCP
You've successfully simulated your first Thread network using OpenThread. Awesome!
In this Codelab you learned how to:
wpantund
If you want to learn more, explore these references: