Simular uma rede Thread usando OpenThread no Docker

1. Introdução

26b7f4f6b3ea0700.png

O OpenThread lançado pelo Google é uma implementação de código aberto do protocolo de rede Thread. O Google Nest lançou o OpenThread para disponibilizar aos desenvolvedores a tecnologia usada em produtos Nest para acelerar o desenvolvimento de produtos para a casa conectada.

A especificação do Thread define um protocolo de comunicação dispositivo a dispositivo sem fio confiável, seguro e de baixo consumo de energia para aplicativos domésticos. O OpenThread implementa todas as camadas de rede do Thread, incluindo IPv6, 6LoWPAN, IEEE 802.15.4 com segurança MAC, estabelecimento de vinculação de malha e roteamento de malha.

Este codelab orientará você na simulação de uma rede Thread em dispositivos emulados usando o Docker.

O que você vai aprender

  • Como configurar o conjunto de ferramentas de compilação do OpenThread
  • Como simular uma rede Thread
  • Como autenticar nós do Thread
  • Como gerenciar uma rede Thread com o OpenThread Daemon

Pré-requisitos

  • Docker
  • Conhecimento básico sobre Linux, roteamento de rede

2. Configurar o Docker

Este codelab foi desenvolvido para usar o Docker em uma máquina Linux, Mac OS X ou Windows. O Linux é o ambiente recomendado.

Instalar o Docker

Instale o Docker no sistema operacional de sua escolha.

Extrair a imagem do Docker

Depois que o Docker for instalado, abra uma janela de terminal e extraia a imagem do Docker openthread/environment. Esta imagem apresenta o OpenThread e o OpenThread Daemon pré-criados e prontos para uso neste codelab.

$ docker pull openthread/environment:latest

O download pode levar alguns minutos.

Em uma janela de terminal, inicie um contêiner do Docker a partir da imagem e conecte-se ao shell bash:

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

A opção --rm exclui o contêiner quando você sai dele. Não use essa opção se não quiser que o contêiner seja excluído.

Observe as sinalizações, que são necessárias para este codelab:

  • --sysctl net.ipv6.conf.all.disable_ipv6=0: ativa o IPv6 no contêiner.
  • --cap-add=net_admin: ativa o recurso NET_ADMIN, que permite executar operações relacionadas à rede, como adicionar rotas de IP.

Uma vez no contêiner, você verá um prompt semelhante a este:

root@c0f3912a74ff:/#

No exemplo acima, o c0f3912a74ff é o ID do contêiner. O ID do contêiner da sua instância do contêiner do Docker será diferente daquele mostrado nas solicitações deste codelab.

Como usar o Docker

Este codelab pressupõe que você sabe o básico sobre o uso do Docker. Você deve permanecer no contêiner do Docker durante todo o codelab.

3. Simular uma rede Thread

O aplicativo de exemplo que você usará para este codelab demonstra um aplicativo mínimo do OpenThread que expõe as interfaces de configuração e gerenciamento do OpenThread por meio de uma interface de linha de comando (CLI) básica.

Este exercício mostra as etapas mínimas necessárias para dar um ping em um dispositivo Thread emulado usando outro dispositivo Thread emulado.

A figura abaixo descreve uma topologia de rede Thread básica. Neste exercício, vamos emular os dois nós dentro do círculo verde: um Thread Leader e Thread Router com uma única conexão entre eles.

6e3aa07675f902dc.png

Criar a rede

1. Nó inicial 1

Em uma janela de terminal, inicie o contêiner do Docker e conecte-se ao shell bash, se ainda não tiver feito isso:

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

No contêiner do Docker, gere o processo da CLI para um dispositivo Thread emulado usando o binário ot-cli-ftd.

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

Observação: se você não vir a solicitação > depois de executar este comando, pressione enter.

Esse binário implementa um dispositivo OpenThread. O driver de rádio IEEE 802.15.4 é implementado sobre UDP. Os frames IEEE 802.15.4 são transmitidos para payloads do UDP.

O argumento de 1 é um descritor de arquivo que representa os bits menos significativos do IEEE EUI-64 "atribuído à configuração original" para o dispositivo emulado. Esse valor também é usado na vinculação a uma porta UDP para emulação de rádio IEEE 802.15.4 (porta = 9000 + descritor de arquivo). Cada instância de um dispositivo Thread emulado neste codelab usará um descritor de arquivo diferente.

Observação:use descritores de arquivos de 1 ou mais, conforme observado neste codelab, ao gerar o processo para um dispositivo emulado. Um descritor de arquivo de 0 está reservado para outro uso.

Crie um novo conjunto de dados operacional e confirme-o como o ativo. O conjunto de dados operacional é a configuração para a rede Thread que você está criando.

> 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

Confirme este conjunto de dados como o ativo:

> dataset commit active
Done

Abra a interface IPv6:

> ifconfig up
Done

Inicie a operação do protocolo Thread:

> thread start
Done

Aguarde alguns segundos e verifique se o dispositivo se tornou o líder da linha de execução. O líder é o dispositivo responsável por gerenciar a atribuição do ID do roteador.

> state
leader
Done

Visualize os endereços IPv6 atribuídos à interface de Thread do Nó 1 (seu resultado será diferente):

> 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

Observe os tipos de endereço IPv6 específicos:

  • Começa com fd = malha-local
  • Começa com fe80 = link-local

Os tipos de endereço locais de malha são classificados ainda mais:

  • Contém ff:fe00 = Localizador de roteadores (RLOC)
  • Não contém ff:fe00 = identificador de endpoint (EID)

Identifique o EID na saída do console e anote-o para uso posterior. Na amostra de saída acima, o EID é:

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

2. Nó inicial 2

Abra um novo terminal e execute um shell bash no contêiner do Docker em execução para usar no Node 2.

$ docker exec -it codelab_otsim_ctnr bash

Nesse novo prompt bash, gere o processo da CLI com o argumento 2. Este é seu segundo dispositivo Thread emulado:

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

Observação: se você não vir a solicitação > depois de executar este comando, pressione enter.

Configure a chave de rede e o código PAN do Thread usando os mesmos valores do conjunto de dados operacional do nó 1:

> dataset networkkey e4344ca17d1dca2a33f064992f31f786
Done
> dataset panid 0xc169
Done

Confirme este conjunto de dados como o ativo:

> dataset commit active
Done

Abra a interface IPv6:

> ifconfig up
Done

Inicie a operação do protocolo Thread:

> thread start
Done

O dispositivo será inicializado como um dispositivo secundário. Um filho do Thread é equivalente a um Dispositivo final, que é um dispositivo do Thread que transmite e recebe tráfego unicast apenas com um dispositivo principal.

> state
child
Done

Em dois minutos, você verá o estado mudar de child para router. Um roteador Thread é capaz de rotear o tráfego entre dispositivos Thread. Ela também é chamada de "Pai".

> state
router
Done

Verificar a rede

Uma maneira fácil de verificar a rede mesh é analisar a tabela do roteador.

1. Verificar a conectividade:

No Node 2, acesse o RLOC16. O RLOC16 são os últimos 16 bits do endereço IPv6 do RLOC do dispositivo.

> rloc16
5800
Done

No nó 1, verifique o RLOC16 do nó 2 na tabela do roteador. Verifique se o nó 2 mudou para o estado do roteador primeiro.

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

O RLOC do nó 2 de 0x5800 é encontrado na tabela, confirmando que ele está conectado à malha.

2. Dar um ping no nó 1 do nó 2

Verifique a conectividade entre os dois dispositivos Thread emulados. No nó 2, ping o EID atribuído ao nó 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

Pressione enter para retornar ao prompt da CLI >.

Testar a rede

Agora que você pode dar um ping entre dois dispositivos Thread emulados, teste a rede mesh colocando um nó off-line.

Retorne ao nó 1 e pare a linha de execução:

> thread stop
Done

Alterne para o nó 2 e verifique o estado. Em dois minutos, o nó 2 detecta que o líder (nó 1) está off-line, e você verá a transição do nó 2 como leader da rede:

> state
router
Done
...
> state
leader
Done

Após a confirmação, interrompa o Thread e redefina o nó para a configuração original antes de retornar ao prompt bash do Docker. Uma redefinição para a configuração original foi feita para garantir que as credenciais da rede Thread que usamos neste exercício não sejam transferidas para o próximo exercício.

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

Talvez seja necessário pressionar enter algumas vezes para que o prompt > volte após um comando factoryreset. Não saia do contêiner do Docker.

Também redefinir o nó para a configuração original e sair do nó 1:

> factoryreset
>
> exit
root@c0f3912a74ff:/#

Consulte a Referência da CLI do OpenThread para explorar todos os comandos da CLI disponíveis.

4. Autenticar nós com Commissioning

No exercício anterior, você configurou uma rede Thread com dois dispositivos simulados e conectividade verificada. No entanto, isso permite que apenas o tráfego de link local IPv6 não autenticado passe entre os dispositivos. Para rotear o tráfego IPv6 global entre eles (e a Internet por meio de um roteador de borda Thread), os nós precisam ser autenticados.

Para autenticar um dispositivo, ele precisa atuar como comissário. O Commissioner é o servidor de autenticação atualmente escolhido para novos dispositivos Thread e o autorizador de fornecer as credenciais de rede necessárias para que os dispositivos participem da rede.

Neste exercício, usaremos a mesma topologia de dois nós de antes. Para a autenticação, o Thread Leader atuará como o Commissioner, o Thread Router como um Joiner.

d6a67e8a0d0b5dcb.png

Docker

Para cada nó (janela do terminal) nos exercícios restantes, verifique se você está executando o contêiner do Docker com o build do OpenThread. Se você continuar usando o exercício anterior, ainda terá duas solicitações bash no mesmo contêiner do Docker já abertas. Caso contrário, consulte a etapa Solução de problemas do Docker ou simplesmente refaça o exercício Simular uma rede Thread.

1. Criar uma rede

No nó 1, gere o processo da CLI:

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

Observação: se você não vir a solicitação > depois de executar este comando, pressione enter.

Crie um novo conjunto de dados operacional, confirme-o como o ativo e inicie a linha de execução:

> 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

Confirme este conjunto de dados como o ativo:

> dataset commit active
Done

Abra a interface IPv6:

> ifconfig up
Done

Inicie a operação do protocolo Thread:

> thread start
Done

Aguarde alguns segundos e verifique se o dispositivo se tornou um líder da linha de execução:

> state
leader
Done

2. Começar o papel de Comissário

Ainda no nó 1, inicie o papel de comissário:

> commissioner start
Done

Permita que qualquer Joiner (usando o caractere curinga *) com a credencial de Joiner J01NME para comissionar na rede. Um Joiner é um dispositivo adicionado por um administrador humano a uma rede Thread encomendada.

> commissioner joiner add * J01NME
Done

3. Iniciar o papel de Combinador

Em uma segunda janela de terminal, no contêiner do Docker, gere um novo processo de CLI. Este é o nó 2.

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

No nó 2, ative o papel de Combinador usando a credencial de J01NME.

> ifconfig up
Done
> joiner start J01NME
Done

... aguarde alguns segundos para confirmar ...

Join success

Como combinador, o dispositivo (Node 2) foi autenticado com o Commissioner (Node 1) e recebeu as credenciais da rede Thread.

Agora que o nó 2 está autenticado, inicie a linha de execução:

> thread start
Done

4. Validar autenticação de rede

Verifique o state no nó 2 para validar se ele agora está associado à rede. Em dois minutos, o nó 2 faz a transição de child para router:

> state
child
Done
...
> state
router
Done

5. Redefinir configuração

Para se preparar para o próximo exercício, redefina a configuração. Em cada nó, interrompa o Thread, redefina para a configuração original e saia do dispositivo emulado:

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

Talvez seja necessário pressionar enter algumas vezes para que o prompt > volte após um comando factoryreset.

5. Gerenciar a rede com o Daemon do OpenThread

Neste exercício, vamos simular uma instância de CLI (um único dispositivo SoC Thread incorporado) e uma instância de coprocessador de rádio (RCP, na sigla em inglês).

ot-daemon é um modo do app Posix do OpenThread que usa um soquete UNIX como entrada e saída, de modo que o núcleo do OpenThread possa ser executado como um serviço. Um cliente pode se comunicar com esse serviço conectando-se ao soquete usando a CLI do OpenThread como protocolo.

ot-ctl é uma CLI fornecida por ot-daemon para gerenciar e configurar o RCP. Usando isso, conectaremos o RCP à rede criada pelo dispositivo Thread.

Docker

Para cada nó (janela do terminal) neste exercício, verifique se você está executando o contêiner do Docker com o build do OpenThread. Se continuar do exercício anterior, você terá duas solicitações bash no mesmo contêiner do Docker já abertas. Caso contrário, consulte a etapa Solução de problemas do Docker.

Usar o ot-daemon

Este exercício usará três janelas de terminal, que correspondem ao seguinte:

  1. Instância da CLI do dispositivo Thread simulado (Node 1)
  2. Processo ot-daemon
  3. ot-ctl instância da CLI

1. Nó inicial 1

Na primeira janela do terminal, gere o processo da CLI para seu dispositivo Thread emulado:

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

Observação: se você não vir a solicitação > depois de executar este comando, pressione enter.

Crie um novo conjunto de dados operacional, confirme-o como o ativo e inicie a linha de execução:

> 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

Confirme este conjunto de dados como o ativo:

> dataset commit active
Done

Abra a interface IPv6:

> ifconfig up
Done

Inicie a operação do protocolo Thread:

> thread start
Done

Veja os endereços IPv6 atribuídos à interface da linha de execução do Node 1:

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

Como explicado na etapa Simular uma rede Thread, um endereço é link-local (fe80) e três são mesh-local (fd). O EID é o endereço local da malha que não contém ff:fe00 no endereço. Nesta saída de amostra, o EID é fd55:cf34:dea5:7994:460:872c:e807:c4ab.

Identifique o EID específico da saída do ipaddr, que será usado para se comunicar com o nó.

2. Iniciar otememon

Na segunda janela do terminal, crie um nó de dispositivo tun e defina as permissões de leitura/gravação:

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

Este dispositivo é usado para transmissão e recebimento de pacotes em dispositivos virtuais. Você poderá receber um erro se o dispositivo já tiver sido criado. Isso é normal e pode ser ignorado.

Inicie ot-daemon para um nó do RCP, que chamaremos de Node 2. Use a sinalização detalhada -v para ver a saída do registro e confirmar se ela está em execução:

root@c0f3912a74ff:/# /openthread/build/posix/src/posix/ot-daemon -v \
'spinel+hdlc+forkpty:///openthread/build/examples/apps/ncp/ot-rcp?forkpty-arg=2'

Quando bem-sucedido, ot-daemon no modo detalhado gera uma saída semelhante a esta:

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

Deixe esse terminal aberto e em execução em segundo plano. Você não inserirá mais comandos nele.

3. Usar o ot-ctl para participar da rede

Ainda não comissionamos o Node 2 (o RCP ot-daemon) para nenhuma rede Thread. É aí que entra a ot-ctl. ot-ctl usa a mesma CLI que o app CLI do OpenThread. Portanto, você pode controlar ot-daemon nós da mesma forma que os outros dispositivos Thread simulados.

Abra uma terceira janela do terminal e execute o contêiner atual:

$ docker exec -it codelab_otsim_ctnr bash

Quando estiver no contêiner, inicie ot-ctl:

root@c0f3912a74ff:/# /openthread/build/posix/src/posix/ot-ctl
>

Você usará ot-ctl nessa terceira janela de terminal para gerenciar o nó 2 (o nó RCP) que você iniciou na segunda janela do terminal com ot-daemon. Verifique o state do nó 2:

> state
disabled
Done

Consiga o eui64 do Node 2 para restringir a mesclagem ao Joiner específico:

> eui64
18b4300000000001
Done

No nó 1 (primeira janela de terminal), inicie o Commissioner e restrinja a participação apenas a esse eui64:

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

Na terceira janela do terminal, abra a interface de rede do Node 2 e entre na rede:

> ifconfig up
Done
> joiner start J01NME
Done

... aguarde alguns segundos para confirmar ...

Join success

Como combinador, o RCP (Node 2) se autenticou com sucesso ao Commissioner (Node 1) e recebeu as credenciais da rede Thread.

Agora, junte o nó 2 à rede Thread (novamente, na terceira janela de terminal):

> thread start
Done

4. Validar autenticação de rede

No terceiro terminal, verifique o state no nó 2 para validar se ele agora se conectou à rede. Em dois minutos, o nó 2 faz a transição de child para router:

> state
child
Done
...
> state
router
Done

5. Validar conectividade

Na terceira janela do terminal, feche ot-ctl usando o comando Ctrl+D ou exit e retorne ao console bash do contêiner. Neste console, dê um ping no nó 1 usando o EID com o comando ping6. Se a instância RCP ot-daemon estiver conectada e se comunicando com a rede Thread, o ping será bem-sucedido:

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. Solução de problemas do Docker

Se você tiver encerrado o contêiner do Docker

bash prompts, talvez seja necessário verificar se ele está em execução e reiniciar / inserir novamente, conforme necessário. Os contêineres do Docker que você criou sem usar a opção --rm ainda existem.

Para mostrar quais contêineres do Docker estão em execução:

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
505fc57ffc72        environment       "bash"              10 minutes ago      Up 10 minutes                           codelab_otsim_ctnr

Para mostrar todos os contêineres do Docker (em execução e interrompidos):

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
505fc57ffc72        environment       "bash"              10 minutes ago      Up 10 minutes                           codelab_otsim_ctnr

Se você não vir o contêiner codelab_otsim_ctnr na saída de um dos comandos docker ps, execute-o novamente:

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

Use a opção --rm apenas se quiser que o contêiner seja excluído ao sair dele.

Se o contêiner estiver parado (listado em docker ps -a, mas não em docker ps), reinicie-o:

$ docker start -i codelab_otsim_ctnr

Se o contêiner do Docker já estiver em execução (listado em docker ps), reconecte-o ao contêiner em cada terminal:

$ docker exec -it codelab_otsim_ctnr bash

Erros "Operação não permitida"

Se você encontrar erros Operation not permitted ao criar novos nós do OpenThread (usando o comando mknod), verifique se está executando o Docker como o usuário raiz de acordo com os comandos fornecidos neste codelab. Este codelab não é compatível com a execução do Docker no modo sem raiz.

7. Parabéns!

Você simulou sua primeira rede Thread usando o OpenThread. Incrível!

Neste codelab, você aprendeu a:

  • Iniciar e gerenciar o contêiner do Docker de simulação do OpenThread
  • Simular uma rede Thread
  • Autenticar nós de thread
  • Gerenciar uma rede Thread com o OpenThread Daemon

Para saber mais sobre o Thread e o OpenThread, veja estas referências:

Ou tente usar o Roteador de borda do OpenThread em um contêiner do Docker.