
Prova de conceito de rede mesh de IoT/Rádio Amador com roteamento global pela internet

Sistema de roteamento mesh que suporta o uso de um proxy OpenDHT como backend, para que os nós possam se comunicar diretamente pela internet. Pré-alfa muito inicial, prova de conceito, pode não funcionar de fato, etc.
Isso é destinado tanto para casos de uso de hobby/HAM quanto para trabalho de IoT mais típico ao consumidor/comercial, mas especificamente não tenta substituir a internet, nem cobrir grandes áreas de alto tráfego com antenas omnidirecionais.
Não possui roteamento de próximo salto no estilo Meshtastic, daí o nome LazyMesh. Se você quiser usá-lo para aplicações do tipo offgrid em uma área densa, provavelmente precisará de antenas direcionais e IDs de rota coordenados manualmente.
No momento, este é o único aplicativo real. Abra o sketch de exemplo Arduino, modifique-o com seu nome de usuário e uma chave de canal secreta que deve ser uma senha forte.
Digite sua mensagem no monitor serial do Arduino e você deverá conseguir conversar com todos os outros dispositivos.
As mensagens devem passar, desde que os nós estejam na mesma rede ou ambos tenham acesso à internet.
Visite o Cliente Web para conversar com o nó via MQTT pela internet. Basta definir um nome de usuário, adicionar um canal e inserir a senha do canal. O nome do canal pode ser qualquer coisa e afeta apenas a rotulagem na interface.
Você também pode experimentar o sketch de exemplo de dados de Leitura/Escrita. Ele expõe o ID de dados 195 como legível e o ID de dados 196 como legível e gravável. Vá ao site, insira os detalhes do seu canal e use a caixa de diálogo de solicitação de dados para solicitar o ID 196 de todos os dispositivos.
Em seguida, clique em "set" (definir) e altere para outro valor, e tente ler novamente.
O Cliente Web atualmente está codificado para usar test.mosquitto.org; isso mudará no futuro.
Os nós precisam de alguma forma de sincronizar o tempo para se comunicar. Atualmente, se nunca receberam tempo de uma fonte confiável, eles definirão seu tempo a partir de qualquer pacote aleatório que virem.
Isso pode ser um risco de segurança que permite ataques de repetição; portanto, dispositivos que precisam de segurança devem ter uma fonte de tempo confiável.
Uma vez que o tempo tenha sido definido inicialmente, o código ajustará o tempo do sistema em até 1 segundo por dia para permanecer sincronizado com outros nós; portanto, a dessincronização não deve ser um grande problema.
No Lazymesh, tudo é um canal; não há mensagens diretas. Se você quiser isso, basta criar um canal privado dedicado.
Os canais são definidos por uma senha; conhecer a senha permite acesso de leitura e escrita.
Existem 256 números de rota. Todo pacote possui um, e os repetidores só repetem se tiverem habilitado um número de rota correspondente. Por padrão, tudo é enviado com o número de rota 0, que está habilitado por padrão.
Isso afeta apenas os repetidores; os nós ouvirão qualquer número de rota mesh se for diretamente para eles.
Os payloads dos pacotes são arrays MessagePack. Eles alternam IDs de dados inteiros e itens de dados. 192-256 são reservados para mensagens específicas do aplicativo.
O ID 32 é para mensagens de texto, que podem ser prefixadas com um nome de usuário e dois-pontos.
O ID 2 é usado para um ID único, que deve ser um inteiro. Muitos aplicativos podem nem precisar disso.
Acrescente um byte de comprimento de metadados e N bytes de metadados.
Para criar o IV, pegue 12 bytes aleatórios para o IV. Em seguida, criptografe tudo. Prefixe o IV e acrescente 4 bytes de tag de autenticação.
Em seguida, pegue os primeiros 8 bytes do hash do ID de rota e converta para hexadecimal.
O tópico MQTT será lazymesh_route_HEX
Observe que usamos um tópico de nível superior. Isso para que você não possa usar curingas para assinar todos os canais lazymesh de uma vez em brokers públicos, o que permitiria um DoS em todos com bastante facilidade.
Apenas os pacotes brutos transmitidos em broadcast em 224.0.0.251:2221
Nada sobre isso está finalizado!!!
All numbers are little-endian.
1 byte header:
2 bit packet type(Either 1 or 2, depending on if we want ACK)
3 bits TTL hops remaining
1 bit allow slow transport(LoRa etc)
1 bit allow global routing
1 bit was already global routed
1 byte header 2:
1 bit first send attempt:
Whenever we create a or recieve a packet, set this bit. After trying to send it,
clear it. This way, as long as we assume packet loss is low-ish, we can count the repeaters in the area
without extra overhead.
1 bit repeater bit:
Marks that this packet should be included when counting repeaters.
Set it if you would repeat the packet or one like it, even if you originated it.
1 bit interest bit:
If this is set, the node who sent it is directly interested in the channel,
not purely just a repeater. If first send is also set, it is treated as an
implicit ack
1 bit location enabled
If this bit is set, repeaters may add location metadata to the packets forwarded to the internet. This metadata must be encrypted with the routing ID as the key,
meaning nearby people could track you for 1 hour after you get out of range.
Not implemented anywhere at the moment.
5 reserved 0 bits
1 byte mesh route number
1 byte path loss accumulated:
5 bits total
3 bits last hop
Every hop is a point of path loss,
plus whatever extra cost heuristic the transport applies.
1 extra point of loss should be roughly the same "badness" as
10dbm extra loss on wifi.
16 bytes routing ID:
Changes every hour, derived from the channel PSK by a hash.
The PSK is just the 16 byte SHA256 of the password.
The routing ID changes hourly, and is the SHA256 of:
The letter 'cr'
The count of hours since 1970 as a 32 bit unsigned int
the PSK
8 Bytes random entropy:
Used as part of the IV for the cipher
4 bytes timestamp:
Also part of the IV, also prevents replay attacks
N bytes ciphertext:
AES-GCM encrypted.
The encryption key changes hourly, and is the SHA256 of:
The letter 'c'
The count of hours since 1970 as a 32 bit unsigned int
the PSK
6 bytes auth tag:
The last 6 bytes are the GCM tag
Algumas implementações podem escolher ignorar isso completamente.
Os ACKs são puramente por salto, a menos que uma camada de protocolo superior queira fazer ACKs de ponta a ponta.
Pacotes ACK não são repetidos, roteados nem nada disso, e também não são autenticados. Cada etapa da repetição mesh tem seu próprio reconhecimento; da perspectiva do remetente original, é "disparar e esquecer".
Como o Meshtastic e a maioria dos outros, o protocolo é "semiconfiável"; há, como em todas as redes, casos extremos que causam falhas.
A confiabilidade real deve ser feita em um nível mais alto.
Para cada pacote, todo ouvinte interessado naquele canal específico envia um reconhecimento de canal exatamente uma vez, a menos que detecte que mais de 8 ACKs já foram enviados por outros nós.
Este pacote deve ser enviado em todos os transportes, não apenas naquele de onde o pacote veio; caso contrário, outros nós podem ter uma ideia errada de quantos ouvintes existem.
Quando um pacote tem tanto o bit de primeira tentativa de envio quanto o flag de interesse, é como um ACK implícito. Se estivermos enviando uma cópia do pacote, não precisamos também enviar um ACK para nos adicionar à contagem de interesse.
Os repetidores reconhecem apenas enviando uma cópia do pacote; quando está marcado com o flag de primeira cópia, nós a contamos. Por esse motivo, transportes como WiFi devem repetir, mesmo que isso não faça muito sentido.
Isso NÃO se aplica a transportes de roteamento global; o roteamento global é tratado separadamente e não está sujeito a nenhum tipo de ACK, repetição ou qualquer outra coisa; presumimos que o servidor MQTT cuide de tudo.
Os nós podem reenviar uma mensagem algumas vezes se receberem menos respostas do que o esperado.
Os nós nunca devem esperar mais de 6 repetidores e 6 ouvintes de canal, mesmo que recebam mais respostas, pois o esquema simples de ACK se torna impreciso além desse ponto se a perda de pacotes for moderada.
1 byte header:
Always 0, packet type is control, and these are not routable or repeatable
1 byte header 2:
Same as on the data packets. Not really used at the moment
1 byte subtype:
CONTROL_TYPE_CHANNEL_ACKNOWLEDGE
You can acknowlege as a channel listener
so the sender knows how many there are.
4 byte message ID:
just the first 4 bytes of the random IV from the packet we are ACKing
A cada hora, alguns minutos antes da hora cheia, os nós devem enviar um anúncio dos canais nos quais estão interessados.
Isso deve ser enviado com os códigos rotativos para a próxima hora, em vez da hora atual, para que as conexões possam ser estabelecidas com antecedência e tudo funcione mesmo quando os horários estiverem dessincronizados.
Os nós formam a mesh via bluetooth usando um pacote de publicidade estendida com o UUID de serviço d1a77e11-420f-9f11-1a00-10a6beef0001, e o payload sendo apenas o formato de pacote acima.
Pacotes BLE nunca devem ter o flag "first copy" (primeira cópia) definido, e não contamos repetidores via BLE. A perda de pacotes é simplesmente alta demais para qualquer esquema simples e escalável que eu possa imaginar.
Portanto, apenas o tratamos como um canal inerentemente sujeito a perdas, o que mitigamos um pouco repetindo pacotes até 4 vezes, ou até precisarmos parar para enviar algum outro pacote.
Contanto que o nó não tente enviar mais do que um ou dois pacotes por segundo, o esquema de repetição pura fornecerá alguma confiabilidade; e se formos além disso, ele reduzirá o ritmo para não congestionar tudo.
Também paramos de enviar se virmos muitos outros nós na mesma área enviando cópias demais do pacote.