Thread Border Router - Provide Internet access via NAT64

1. Introduction

7299534792dd9439.png

What is Thread?

Thread is an IP-based low-power wireless mesh networking protocol that enables secure device-to-device and device-to-cloud communications. Thread networks can adapt to topology changes to avoid single-point failures.

What is OpenThread?

OpenThread released by Google is an open-source implementation of Thread®.

What is an OpenThread Border Router?

OpenThread Border Router (OTBR) released by Google is an open-source implementation of the Thread Border Router.

NAT64

NAT64 is a mechanism that enables hosts in IPv6-only networks to access resources in IPv4 networks. The NAT64 gateway is a translator between IPv4 protocols and IPv6 protocols.

The NAT64 translator, as a part of OpenThread Border Router, supports translating TCP, UDP, and ICMP (ICMPv6) protocols.

What you'll build

In this codelab, you are going to set up a OpenThread Border Router and a Thread device, then enable and verify communication between Thread devices and IPv4 hosts on the Internet via OpenThread Border Router.

What you'll learn

  • How to build an OpenThread Border Router with NAT64 features.
  • How to communicate with IPv4 hosts from Thread end devices.

What you'll need

  • A Linux workstation, for building and flashing a Thread NCP, the OpenThread CLI, and testing IPv4 connectivity.
  • A Raspberry Pi 4 with 4GB RAM for the Thread border router. Your Linux workstation should be reachable over IPv4 from this device.
  • 2 Nordic Semiconductor nRF52840 DK boards.

The network topology for this codelab:

c3cd2e081bc052fd.png

2. Setup OpenThread Border Router

Follow the Setup OTBR step of the Thread Border Router - Bidirectional IPv6 Connectivity and DNS-Based Service Discovery codelab to build the OpenThread border router, with the following change:

In Build and install OTBR, you need to tell the script to enable the NAT64 translator in OpenThread by setting the environment variable NAT64 to 1 and NAT64_SERVICE to openthread. Run the following command before the step:

$ export NAT64=1 NAT64_SERVICE=openthread

Continue with the Thread Border Router - Bidirectional IPv6 Connectivity and DNS-Based Service Discovery codelab as written. After Form a Thread network, you can verify the border router is publishing a NAT64 prefix by the OpenThread CLI commands.

Firstly, ensure our border router is up and running, and NAT64 is enabled on the border router:

$ sudo ot-ctl state
leader
Done
$ sudo ot-ctl nat64 enable
Done
$ sudo ot-ctl nat64 state
PrefixManager: Active
Translator: Active
Done

We should be able to see that OTBR is acting as a Thread leader and there is a NAT64 prefix (fd4c:9574:3720:2:0:0::/96 in our case) in the Thread Network Data:

$ sudo ot-ctl netdata show
Prefixes:
fd4c:9574:3720:1::/64 paos low 0800
Routes:
fd49:7770:7fc5:0::/64 s med 0800
fd4c:9574:3720:2:0:0::/96 sn low 0800
Services:
44970 01 41000500000e10 s 0800
44970 5d fdd20e532b87b93f50ad4eea0450f1bfd11f s 0800
Done

The NAT64 prefix will be used by Thread devices when communicating with an IPv4 host.

3. Setup Thread end device

Follow the Set up the FTDs step of the Build a Thread network with nRF52840 boards and OpenThread codelab to build and flash a nRF52840 CLI end device, with a change to the following step:

In Build and flash, you have to append -DOT_DNS_CLIENT=ON, -DOT_SRP_CLIENT=ON and -DOT_ECDSA=ON to the command line when calling script/build:

$ cd ~/src/ot-nrf528xx
$ rm -rf build
$ script/build nrf52840 USB_trans -DOT_JOINER=ON -DOT_COMMISSIONER=ON -DOT_DNS_CLIENT=ON -DOT_SRP_CLIENT=ON -DOT_ECDSA=ON

Continue with the Build a Thread network with nRF52840 boards and OpenThread codelab as written. After the end device is flashed with the CLI image, follow Thread Border Router - Bidirectional IPv6 Connectivity and DNS-Based Service Discovery to set up the Thread end device.

Wait for a few seconds after setting up the Thread end device and verify if joining the Thread network is successful. You should be able to find a NAT64 prefix from the network data (fd4c:9574:3720:2:0:0::/96 in our case):

> netdata show
Prefixes:
fd4c:9574:3720:1::/64 paos low 0800
Routes:
fd49:7770:7fc5:0::/64 s med 0800
fd4c:9574:3720:2:0:0::/96 sn low 0800
Services:
44970 01 41000500000e10 s 0800
44970 5d fdd20e532b87b93f50ad4eea0450f1bfd11f s 0800
Done

Make sure that the network data matches the one from OTBR.

4. Communicate with IPv4 hosts from the Thread end device

You can now communicate with hosts on the IPv4 network from the end device we just set up.

Send ICMP echo requests to IPv4 hosts

From the CLI of our Thread end device:

> ping 8.8.8.8
Pinging synthesized IPv6 address: fd4c:9574:3720:2:0:0:808:808
16 bytes from fd4c:9574:3720:2:0:0:808:808: icmp_seq=15 hlim=119 time=48ms
1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 48/48.0/48 ms.
Done

The border router creates a NAT64 mapping item for this device by the nat64 mappings command:

$ sudo ot-ctl nat64 mappings
|                  | Address                                                     |        | 4 to 6                  | 6 to 4                  |
+------------------+-------------------------------------------------------------+--------+-------------------------+-------------------------+
| ID               | IPv6                                     | IPv4             | Expiry | Pkts     | Bytes        | Pkts     | Bytes        |
+------------------+------------------------------------------+------------------+--------+----------+--------------+----------+--------------+
| 377ee63dd3127f1a |     fd4c:9574:3720:1:1d61:b4c1:494f:f975 |  192.168.255.254 |  7190s |        1 |           16 |        1 |           16 |
|                  |                                                                  TCP |        0 |            0 |        0 |            0 |
|                  |                                                                  UDP |        0 |            0 |        0 |            0 |
|                  |                                                                 ICMP |        1 |           16 |        1 |           16 |
Done

The fd4c:9574:3720:1:1d61:b4c1:494f:f975 should be the IPv6 address of your Thread device.

Run this command on the border router at any time to see how it counts the traffic.

Send DNS queries to IPv4 DNS servers

Use dns resolve4 to resolve a hostname on the IPv4 network. The DNS server address can also be an IPv4 address:

> dns resolve4 example.com 8.8.8.8
Synthesized IPv6 DNS server address: fd4c:9574:3720:2:0:0:808:808
DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456 
Done

Communicate via TCP

It is possible to establish TCP connections between the end device and hosts in the IPv4 network.

Assume the IP address of your Linux IPv4 host is 192.168.0.2.

On your Linux IPv4 host, use nc to listen for TCP connections:

$ nc -l 0.0.0.0 12345

From your Thread end device, establish a TCP connection and send messages to your Linux IPv4 host:

> tcp init
Done
> tcp connect 192.168.0.2 12345
Connecting to synthesized IPv6 address: fd4c:9574:3720:2:0:0:c0a8:2
Done
> tcp send hello

Your Linux IPv4 host outputs:

hello

You can also send messages from your Linux IPv4 host to Thread end device. Type "world" and press Enter on your Linux IPv4 host running nc, and your Thread end device outputs:

TCP: Received 6 bytes: world

Communicate via UDP

It is possible to communicate using UDP between Thread devices and hosts in the IPv4 network.

Assume the IP address of your Linux IPv4 host is 192.168.0.2.

Use nc to listen for UDP connections:

$ nc -u -l 0.0.0.0 12345

From your Thread end device, establish a UDP connection and send messages to your Linux IPv4 host:

> udp open
Done
> udp connect 192.168.0.2 12345
Connecting to synthesized IPv6 address: fd4c:9574:3720:2:0:0:c0a8:2
Done
> udp send hello
Done

Your Linux IPv4 host outputs:

hello

You can also send messages from your Linux IPv4 host to Thread end device. Type "world" and press Enter on your Linux IPv4 host running nc, and your Thread end device outputs:

6 bytes from fd4c:9574:3720:2:0:0:c0a8:2 12345 world

5. Toggle NAT64 on Border Router

You can enable or disable NAT64 any time you want. Use nat64 disable to disable NAT64. And use nat64 state to check the state of NAT64.

$ sudo ot-ctl nat64 disable
Done
$ sudo ot-ctl nat64 state
PrefixManager: Disabled
Translator: Disabled
Done

After disabling, the device is no longer publishing a NAT64 prefix:

$ sudo ot-ctl netdata show
Prefixes:
fd4c:9574:3720:1::/64 paos low 0800
Routes:
fd49:7770:7fc5:0::/64 s med 0800
Services:
44970 01 41000500000e10 s 0800
44970 5d fdd20e532b87b93f50ad4eea0450f1bfd11f s 0800
Done

Also the devices in the Thread network can no longer access the IPv4 host via this border router.

From the CLI of our Thread end device:

> ping 8.8.8.8
Error 13: InvalidState

Use nat64 enable to enable NAT64. It may take a while before the prefix manager starts advertising a NAT64 prefix:

$ sudo ot-ctl nat64 enable
Done
$ sudo ot-ctl nat64 state
PrefixManager: Idle
Translator: NotWorking
Done

After a few seconds, the NAT64 components should be up and running:

$ sudo ot-ctl nat64 state
PrefixManager: Active
Translator: Active
Done
$ sudo ot-ctl netdata show
Prefixes:
fd4c:9574:3720:1::/64 paos low 0800
Routes:
fd49:7770:7fc5:0::/64 s med 0800
fd4c:9574:3720:2:0:0::/96 sn low 0800
Services:
44970 01 41000500000e10 s 0800
44970 5d fdd20e532b87b93f50ad4eea0450f1bfd11f s 0800
Done

Note that disabling NAT64 will clear the mapping table:

$ sudo ot-ctl nat64 mappings
|                  | Address                                                     |        | 4 to 6                  | 6 to 4                  |
+------------------+-------------------------------------------------------------+--------+-------------------------+-------------------------+
| ID               | IPv6                                     | IPv4             | Expiry | Pkts     | Bytes        | Pkts     | Bytes        |
+------------------+------------------------------------------+------------------+--------+----------+--------------+----------+--------------+
Done

6. Forward DNS queries to upstream DNS servers

When NAT64 is enabled on the border router, OpenThread will try to forward the DNS queries for internet domains to upstream DNS servers.

This function is supported by the internal DNS-SD Server, so you need to ensure the DNS-SD server is enabled.

$ sudo ot-ctl srp server state
running
Done

If it is not running, then enable it:

$ sudo ot-ctl srp server enable
Done

Ensure the upstream DNS proxy is enabled:

$ sudo ot-ctl dns server upstream
Enabled
Done

If it is not Enabled, then enable it:

$ sudo ot-ctl dns server upstream enable
Done

On end devices, ensure the SRP client is enabled so it will send DNS queries to the border router:

> srp client state
Enabled
Done

If it is not Enabled, then enable it:

> srp client autostart enable
Done

On your end device, ensure the default DNS server is the border router:

> dns config
Server: [fdd2:0e53:2b87:b93f:50ad:4eea:0450:f1bf]:53
ResponseTimeout: 6000 ms
MaxTxAttempts: 3
RecursionDesired: yes
Done

The server IPv6 address (fdd2:0e53:2b87:b93f:50ad:4eea:0450:f1bf in the example above), should be one of the addresses of your OpenThread Border Router.

Now you can send DNS queries for internet domains from the end device:

> dns resolve example.com
DNS response for example.com. - 2606:2800:220:1:248:1893:25c8:1946 TTL:8720 
Done
> dns resolve4 example.com
DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456 
Done

7. Congratulations

Congratulations, you've successfully set up a border router with NAT64 support and used it to provide internet access to Thread end devices!

Further reading

Reference docs