
Rete mesh IoT/Radioamatori come prova di concetto con routing globale su Internet

Sistema di routing mesh che supporta l'uso di un proxy OpenDHT come backend, in modo che i nodi possano comunicare direttamente via internet. Molto presto pre-alpha, proof of concept, potrebbe non funzionare effettivamente, ecc.
È pensato sia per casi d'uso hobbistici/HAM, sia per il più tipico lavoro IoT consumer/commerciale, ma specificamente non cerca di sostituire internet o di coprire grandi aree ad alto traffico con antenne omnidirezionali.
Non ha il routing hop-by-hop stile Meshtastic, da qui il nome LazyMesh. Se vuoi usarlo per applicazioni offgrid in un'area densa, probabilmente avrai bisogno di antenne direzionali e ID di route coordinati manualmente.
Al momento questa è l'unica applicazione reale. Apri lo sketch Arduino di esempio, modificalo con il tuo nome utente e una chiave segreta del canale che deve essere una password forte.
Digita il tuo messaggio nel monitor seriale di Arduino e dovresti essere in grado di chattare con tutti gli altri dispositivi.
I messaggi dovrebbero passare fintanto che i nodi sono sulla stessa rete o entrambi hanno accesso a internet.
Visita il Web Client per chattare con il nodo via MQTT da internet. Imposta un nome utente, aggiungi un canale e inserisci la password del canale. Il nome del canale può essere qualsiasi cosa e influisce solo sull'etichettatura nell'interfaccia.
Puoi anche provare lo sketch di esempio Read/Write data. Questo espone l'ID dati 195 come leggibile e l'ID dati 196 come leggibile e scrivibile. Vai sul sito, inserisci i dettagli del tuo canale e usa il dialogo di richiesta dati per richiedere l'ID 196 da tutti i dispositivi.
Poi clicca "set" e impostalo su qualcos'altro, e prova a leggerlo di nuovo.
Il Web Client è attualmente hardcoded per usare test.mosquitto.org, questo cambierà in futuro.
I nodi necessitano di un modo per sincronizzare il tempo per comunicare. Attualmente, se non hanno mai ricevuto il tempo da una fonte attendibile, impostano il loro tempo da qualsiasi pacchetto casuale vedano.
Questo potrebbe essere un rischio per la sicurezza consentendo attacchi replay, quindi i dispositivi che necessitano di sicurezza dovrebbero avere una fonte temporale attendibile.
Una volta che il tempo è stato impostato inizialmente, il codice regolerà il tempo di sistema fino a 1 secondo al giorno per rimanere sincronizzato con gli altri nodi, quindi il disallineamento temporale non dovrebbe essere un problema importante.
In Lazymesh, tutto è un canale, non ci sono messaggi diretti. Se li vuoi, crea un canale privato dedicato.
I canali sono definiti da una password; conoscere la password consente l'accesso in lettura e scrittura.
Ci sono 256 numeri di route. Ogni pacchetto ne ha uno, e i ripetitori ripetono solo se hanno abilitato un numero di route corrispondente. Per impostazione predefinita, tutto viene inviato con il numero di route 0, che è abilitato di default.
Questo riguarda solo i ripetitori; i nodi ascolteranno qualsiasi numero di route mesh se è direttamente per loro.
I payload dei pacchetti sono array MessagePack. Alternano ID dati interi e elementi dati. 192-256 sono riservati per messaggi specifici dell'applicazione.
ID 32 è per messaggi di testo, che possono essere preceduti da un nome utente e due punti.
ID 2 è usato per un ID univoco, che deve essere un intero. Molte applicazioni potrebbero non averne affatto bisogno.
Aggiungi un byte di lunghezza dei metadati e N byte di metadati.
Per creare l'IV, prendi 12 byte casuali per l'IV. Quindi crittografa il tutto. Prependi l'IV e aggiungi 4 byte di tag di autenticazione.
Quindi prendi i primi 8 byte dell'hash dell'ID di route e converti in esadecimale.
L'argomento MQTT sarà lazymesh_route_HEX
Nota che usiamo un argomento di primo livello. Questo in modo che tu non possa usare wildcard per sottoscrivere tutti i canali lazymesh contemporaneamente su broker pubblici, il che ti permetterebbe di fare DoS a tutti abbastanza facilmente.
Semplici pacchetti grezzi trasmessi su 224.0.0.251:2221
Niente di questo è definitivo!!!
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
Alcune implementazioni potrebbero scegliere di ignorare completamente questo.
Gli ACK sono puramente per hop a meno che un livello di protocollo superiore non voglia fare ACK end-to-end.
I pacchetti ACK non vengono ripetuti, instradati o altro, né sono autenticati. Ogni passo della ripetizione mesh ha il proprio riconoscimento; dalla prospettiva del mittente originale, è fuoco e dimentica.
Come Meshtastic e molti altri, il protocollo è "semi-affidabile", ci sono, come tutte le reti, casi limite che causano guasti.
L'affidabilità reale deve essere implementata a un livello superiore.
Per ogni pacchetto, ogni ascoltatore interessato a quel canale specifico invia un riconoscimento di canale esattamente una volta, a meno che non rilevi che più di 8 ACK sono già stati inviati da altri nodi.
Questo pacchetto deve essere inviato su tutti i trasporti, non solo su quello da cui proviene il pacchetto, altrimenti altri nodi potrebbero farsi un'idea sbagliata del numero di ascoltatori.
Quando un pacchetto ha sia il bit di primo tentativo di invio che il flag di interesse, è come un ACK implicito. Se stiamo inviando una copia del pacchetto, non abbiamo bisogno di inviare anche un ACK per aggiungerci al conteggio degli interessi.
I ripetitori riconoscono semplicemente inviando una copia del pacchetto; quando è contrassegnato con il flag della prima copia, lo contiamo. Per questo motivo, trasporti come WiFi devono ripetere anche se non ha molto senso.
Questo NON si applica ai trasporti di routing globale; il routing globale è gestito separatamente e non è soggetto ad alcun tipo di ACK, ripetizioni o altro; assumiamo che il server MQTT gestisca tutto.
I nodi possono reinviare un messaggio alcune volte se ricevono meno risposte del previsto.
I nodi non devono mai aspettarsi più di 6 ripetitori e 6 ascoltatori di canale, anche se ricevono più risposte, poiché il semplice schema ACK diventa impreciso oltre quel punto se la perdita di pacchetti è moderata.
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
Ogni ora, pochi minuti prima dell'ora, i nodi dovrebbero inviare un annuncio dei canali a cui sono interessati.
Questo dovrebbe essere inviato con i codici rotanti per l'ora successiva invece dell'ora corrente, in modo che le connessioni possano essere impostate in anticipo e tutto funzioni anche quando i tempi sono disallineati.
I nodi fanno mesh via bluetooth usando un pacchetto pubblicitario esteso con UUID del servizio d1a77e11-420f-9f11-1a00-10a6beef0001, e il payload è semplicemente il formato del pacchetto sopra.
I pacchetti BLE non devono mai avere il flag "first copy" impostato e non contiamo i ripetitori su BLE. La perdita di pacchetti è semplicemente troppo alta per qualsiasi schema semplice e scalabile a cui possa pensare.
Pertanto, lo trattiamo come un canale intrinsecamente soggetto a perdite, che mitigiamo in qualche modo ripetendo i pacchetti fino a 4 volte, o fino a quando non dobbiamo smettere per poter inviare qualche altro pacchetto.
Fintanto che il nodo non cerca di inviare più di un pacchetto o due al secondo, lo schema di pura ripetizione fornirà una certa affidabilità, e se si supera quel limite, rallenterà per non intasare tutto.
Smettiamo anche di inviare se vediamo troppi altri nodi nella stessa area che inviano troppe copie del pacchetto.