Chappe 05 · Firmware

Un répéteur qui filtre

Réguler le trafic d'un réseau LoRa communautaire sans tout interdire : fermé par défaut, ouvert sur demande, décidé par région. Le guide des commandes.

01Le problème

Un répéteur relaie en aveugle. Il ne peut pas déchiffrer le contenu d'un canal, donc il ne sait pas distinguer une sonde légitime d'un flood abusif. Or les données applicatives (GRP_DATA) voyagent en flood : chaque répéteur les réémet, et elles peuvent occuper l'antenne de tout le réseau, loin de tout destinataire utile.

La tentation est de tout bloquer. Mais sur un réseau communautaire, celui qui pose une sonde a le droit d'émettre. Interdire par principe, c'est décider à la place des autres.

Bloquer partout, c'est simple, mais c'est brutal. Le bon réglage n'est pas « tout ou rien » : c'est une régulation graduée, par consensus régional.

02Le pipeline

À l'arrivée d'un paquet en flood, cinq portes se présentent, de la plus large à la plus fine. Tout se décide sans déchiffrer : le type, la portée et l'empreinte de canal sont en clair dans l'en-tête.

entréePaquet flood 1 · nœudblocklist 2 · portéerégion 3 · défautGRP_DATA
bloqués
4 · exceptioncanal
autorisé
5 · distancesauts max
par type
sortieRelayé

À chaque porte, une seule question. Si la condition n'est pas remplie, le paquet est rejeté et n'est jamais relayé. Les commandes ci-dessous règlent chaque porte.

03Les commandes

Chaque porte du pipeline correspond à une famille de commandes de la console MeshCore. Syntaxe, valeurs acceptées, défauts.

Ces commandes ne sont pas toutes dans le firmware officiel. Seul le groupe region existe en standard. Les commandes set grp.data.*, set flood.max.type, set prio.type, set airtime.budget.type, block.* et set grp.relay.* proviennent du firmware communautaire Chappe 05 : il faut le compiler et le flasher pour en disposer. Sur un build officiel, elles renvoient une erreur, c'est normal.

Porte 2 · portée régionale

MeshCore, v1.12+

Ne relaie que le trafic des régions déclarées, et jette les floods sans portée. La base du contrôle par zone : un répéteur des Hautes-Alpes n'a aucune raison de propager un flood national.

region put <région>
Déclare une région connue du répéteur. La hiérarchie va du général au précis : eufrfr-pacfr-05.
argumentrôle
<région>identifiant de région, ex. fr-05
region allowf <région> region denyf <région | *>
allowf autorise le flood pour une région ; denyf le refuse. denyf * rejette tout flood non rattaché à une région autorisée (le plus courant).
valeureffet
<région>cible une région précise
*joker : tout le trafic non scopé
region save
Écrit la configuration région en mémoire persistante. Sans save, les règles sont perdues au redémarrage.
# n'accepter que le flood des Hautes-Alpes region put fr-05 region allowf fr-05 region denyf * region save

Porte 3 · refus des GRP_DATA

Chappe 05

Par défaut, le répéteur ne relaie pas les données applicatives non sollicitées. C'est la position communautaire de base : on n'inonde pas le réseau des mesures d'un tiers sans accord. Le chat de canal (GRP_TXT) n'est pas concerné.

set grp.data.block <0|1>
Active ou non le refus des GRP_DATA reçus en flood.
valeureffet
1bloque tous les GRP_DATA en flooddéfaut
0laisse passer (comportement d'origine)

Porte 4 · autorisation par canal

Chappe 05

« Tu veux poser une sonde ? Demande. » Quand un membre le demande, l'opérateur inscrit l'empreinte de son canal dans la liste d'exceptions : ses données passent, tout le reste reste bloqué. C'est ce qui transforme le refus en gouvernance plutôt qu'en interdiction.

set grp.data.allow <empreinte> set grp.data.deny <empreinte> set grp.data.allow.clear
allow ajoute une empreinte de canal à la liste d'exceptions, deny la retire, allow.clear vide la liste. L'empreinte est le premier octet du canal, en hexadécimal (00 à FF).
argumentrôle
<empreinte>1 octet hexadécimal, ex. 8C
get grp.data.allow
Liste les empreintes actuellement autorisées.
# ouvrir le canal d'une sonde, vérifier, puis le refermer set set grp.data.allow 8C get grp.data.allow -> 8C set grp.data.deny 8C

Porte 5 · plafond de sauts par type

Chappe 05

C'est le levier qui s'applique à tous les types de paquets : les 16 types que le relais peut relayer ont chacun leur propre plafond de sauts. Même autorisé, un paquet n'a pas besoin de traverser tout le réseau, un GRP_DATA peut être livré localement sans partir à l'autre bout du pays. Un plafond à 0 signifie « pas de limite dédiée », c'est le réglage global qui s'applique alors.

set flood.max.type <type> <sauts> get flood.max.type
Fixe (ou lit) le nombre de sauts maximum pour un type de paquet donné. Un paquet ayant déjà franchi ce nombre de sauts n'est plus relayé.
argumentvaleurs
<type>numéro du type de charge utile, 0 à 15 (table ci-dessous)
<sauts>plafond, 0 à 64. 0 = pas de plafond dédié, seul flood.max global s'applique
typepaquetplafond défaut
0REQ, requête0
1RESPONSE, réponse0
2TXT_MSG, message direct0
3ACK, accusé de réception0
4ADVERT, annonce de nœud0 · voir flood.max.advert
5GRP_TXT, chat de canal12
6GRP_DATA, données applicatives6
7ANON_REQ, requête anonyme0
8PATH, chemin retour0
9TRACE, traçage de chemin0
10MULTIPART, gros message8
11CONTROL, découverte0
12 à 14réservés0
15RAW_CUSTOM, brut applicatif6
# limiter les données appli. à 4 sauts, garder le chat large set flood.max.type 6 4 set flood.max.type 5 12 get flood.max.type

File d'envoi · qualité de service

Chappe 05

La QoS, c'est la régulation plutôt que l'interdiction : au lieu de bloquer, on fait céder le pas. MeshCore a déjà une file d'envoi à priorités (le trafic direct passe avant le flood) et un budget d'airtime lié au duty cycle ; on ajoute juste de quoi les régler par type de paquet. Un réseau non saturé laisse tout circuler, c'est seulement sous charge que le broadcast applicatif s'efface devant les échanges.

set prio.type <type> <priorité> get prio.type
Décale la priorité d'un type dans la file d'envoi. Le paquet est toujours relayé, mais plus la valeur est haute, plus il attend derrière les autres quand la file est chargée, et plus il saute en premier si la file est pleine.
argumentvaleurs
<type>numéro du type, 0 à 15 (même table que flood.max.type)
<priorité>décalage, 0 à 64. 0 = inchangé (défaut). Plus haut = cède davantage
# le GRP_DATA cède le pas aux conversations sous charge set prio.type 6 6
set airtime.budget.type <type> <part> get airtime.budget.type
Plafonne la part d'airtime qu'un type peut consommer, en pourcentage du budget de duty cycle du relais. Au-delà de sa part, les paquets suivants de ce type sont écrêtés (le seau se recharge en continu). C'est le partage de bande passante par classe.
argumentvaleurs
<type>numéro du type, 0 à 15
<part>0 à 100 (%). 0 = pas de plafond (défaut)
# le GRP_DATA ne prend jamais plus de 15% de mon airtime set airtime.budget.type 6 15

Porte 1 · blocage d'un nœud

Chappe 05

Un nœud abuse malgré tout ? On le bloque nommément, par le préfixe de sa clé publique, sans punir les autres. C'est la sanction ciblée, en dernier recours.

block.add <préfixe> block.remove <préfixe> block.list block.clear
Gère la liste des nœuds bloqués. Le préfixe identifie le nœud par les premiers octets de sa clé publique.
argumentvaleurs
<préfixe>1 à 4 octets hexadécimaux. Plus il est long, plus il est spécifique
set block.lasthop <0|1> set block.alltypes <0|1>
Étend la portée du blocage.
optioneffet
block.lasthopbloque aussi si le nœud est le dernier relais du chemin, pas seulement l'émetteur
block.alltypesbloque tous les types de paquets du nœud, pas seulement le flood

En complément · confirmation de relais

Chappe 05

Un canal n'a pas d'acquittement. Le répéteur peut écouter passivement si un autre nœud a bien re-diffusé le paquet (reconnu par son empreinte), et ne réémettre que sinon. Cela réduit les doublons sans sacrifier la couverture.

set grp.relay.enable <0|1> set grp.relay.retries <n> set grp.relay.timeout <ms> set grp.relay.firsthop <0|1>
Règle la confirmation passive de relais.
optionvaleurs
enable1 active, 0 désactive
retriesnombre de réémissions si aucune confirmation n'est entendue
timeoutdélai d'écoute avant réémission, en millisecondes
firsthopapplique la logique dès le premier saut

04La configuration type

« Fermé par défaut, ouvert sur demande. » Le réglage d'un répéteur communautaire des Hautes-Alpes, en une session console.

# --- portée : je ne relaie que ma région, je jette le non-scopé ---
region put fr-05 ; region allowf fr-05 ; region denyf * ; region save

# --- GRP_DATA : refus par défaut... ---
set grp.data.block 1
# ...sauf le canal d'une sonde qui en a fait la demande
grp.data.allow 8C

# --- même autorisé, on borne la portée par type ---
set flood.max.type 6 4       # GRP_DATA : 4 sauts
set flood.max.type 5 12      # GRP_TXT  : 12 sauts

# --- QoS : le broadcast applicatif cède le pas sous charge, sans être interdit ---
set prio.type 6 6            # GRP_DATA : priorité basse si congestion
set airtime.budget.type 6 15 # GRP_DATA : au plus 15% de l'airtime

# --- confirmation passive de relais ---
set grp.relay.enable 1

# --- sanction d'un nœud fautif, si besoin ---
block.add <préfixe>

05Vérifier que ça marche

Filtrer sans mesurer, c'est piloter à l'aveugle. Le firmware compte ce qu'il rejette, par motif, et l'affiche en console. On peut ainsi confirmer qu'une règle agit, et l'ajuster.

stats-filter
Affiche les compteurs de rejets depuis la dernière remise à zéro.
compteurce qu'il mesure
grp_dataGRP_DATA rejetés (canal absent de grp.data.allow)
blocklistrejets par blocklist de nœud (advert ou saut du chemin)
hopcaprejets par plafond de sauts (flood.max.type)
airtimerejets QoS par budget d'airtime dépassé (total, puis air[type] par type)
qfullpaquets jetés car la file d'envoi était pleine : le signal que la priorité (prio.type) travaille sous charge
clear stats
Remet tous les compteurs à zéro (répond (OK - stats reset)). À lancer avant une phase d'observation.

Pour valider le budget d'airtime, par exemple : clear stats, puis set airtime.budget.type 6 5 (GRP_DATA plafonné à 5 %), laisse tourner, et regarde air[6] grimper dans stats-filter. Compare avec stats-packets (reçu contre relayé) pour mesurer la proportion écrêtée.

> clear stats
(OK - stats reset)
> stats-filter
drops grp_data=128 blocklist=3 hopcap=41 airtime=57 qfull=0 air[6]=57

La limite, assumée

Dans un paquet, un canal n'est identifié que par une empreinte d'un seul octet, soit 256 valeurs possibles. Autoriser une empreinte autorise donc les quelques canaux qui la partagent : c'est grossier, et c'est inévitable côté répéteur, qui ne voit rien d'autre en clair. On le documente plutôt que de le cacher. Pour tout ce qui doit rester privé, la vraie protection reste une clé de canal aléatoire, jamais dérivée d'un nom.