From ef7a4801800ad5701ecfcc2a8d49b699426875d0 Mon Sep 17 00:00:00 2001 From: raph666 Date: Sat, 18 Jul 2026 16:52:06 +0200 Subject: [PATCH] Document observed REG3 frame structure --- docs/frame_protocol_analysis.md | 167 ++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 docs/frame_protocol_analysis.md diff --git a/docs/frame_protocol_analysis.md b/docs/frame_protocol_analysis.md new file mode 100644 index 0000000..3c665a7 --- /dev/null +++ b/docs/frame_protocol_analysis.md @@ -0,0 +1,167 @@ +# Analyse observable du flux Arkteos REG3 + +## Périmètre et méthode + +Cette analyse porte exclusivement sur : + +- `captures/stream_normal.bin` ; +- `references/arkteos_nodered_flow.json` ; +- `AGENTS.md`. + +Les offsets sont relatifs au début d'une trame présumée, indexés à zéro, et +exprimés en décimal puis en hexadécimal. Les conclusions portant sur le +découpage sont établies à partir des octets de la capture, pas à partir de +limites de lectures TCP. + +## Résumé + +| Élément | Observation | +|---|---:| +| Taille totale de la capture | 43 165 octets (`0xA89D`) | +| Occurrences de `55 00` | 267 | +| Occurrences de chaque taille | 95 : 89 ; 163 : 89 ; 227 : 89 | +| Cycles complets observés | 89 | +| Ordre | `227 → 163 → 95`, sans exception dans cette capture | + +La capture commence par `55 00` à l'offset global 0 (`0x0000`). Toutes les +occurrences suivantes sont séparées de la précédente par 227, 163 ou 95 +octets. La dernière occurrence commence à l'offset global 43 070 (`0xA83E`) ; +les 95 octets restants terminent exactement la capture. Ces positions +partitionnent donc entièrement cette capture en 267 segments. + +## Types et tailles observés + +| Taille totale | Taille hex. | Occurrences | Nom dans le flow | Valeur observée à 8 (`0x08`) | +|---:|---:|---:|---|---:| +| 95 | `0x005F` | 89 | aucune branche de parsing | `0x0C` | +| 163 | `0x00A3` | 89 | `frigo` | `0x0A` | +| 227 | `0x00E3` | 89 | `regulation` | `0x0B` | + +Le flow identifie `frigo` et `regulation` uniquement à partir de la taille du +buffer reçu (163 ou 227). Il ne contient pas de parsing pour 95 octets. Le nom +fonctionnel du type de 95 octets n'est donc pas déterminé par les trois fichiers +analysés. + +## Signatures et en-têtes + +Les 16 premiers octets sont invariants pour les 89 occurrences de chaque taille +dans cette capture : + +| Taille | Octets 0–15 (`0x00–0x0F`) | +|---:|---| +| 95 | `55 00 58 FF A7 40 02 02 0C 00 50 00 00 00 00 00` | +| 163 | `55 00 9C FF 63 40 02 04 0A 00 94 00 00 00 00 00` | +| 227 | `55 00 DC FF 23 40 02 04 0B 00 D4 00 01 00 00 00` | + +### Différences observables dans l'en-tête + +| Offset déc. | Offset hex. | 95 octets | 163 octets | 227 octets | Observation | +|---:|---:|---:|---:|---:|---| +| 0 | `0x00` | `55` | `55` | `55` | commun | +| 1 | `0x01` | `00` | `00` | `00` | commun | +| 2 | `0x02` | `58` | `9C` | `DC` | distingue les trois tailles dans cette capture | +| 3 | `0x03` | `FF` | `FF` | `FF` | commun | +| 4 | `0x04` | `A7` | `63` | `23` | distingue les trois tailles dans cette capture | +| 5 | `0x05` | `40` | `40` | `40` | commun | +| 6 | `0x06` | `02` | `02` | `02` | commun | +| 7 | `0x07` | `02` | `04` | `04` | 95 distinct de 163 et 227 | +| 8 | `0x08` | `0C` | `0A` | `0B` | valeur distincte par taille dans cette capture | +| 9 | `0x09` | `00` | `00` | `00` | commun | +| 10–11 | `0x0A–0x0B` | `50 00` | `94 00` | `D4 00` | entier little-endian corrélé à la taille | +| 12 | `0x0C` | `00` | `00` | `01` | 227 distinct dans cette capture | +| 13–15 | `0x0D–0x0F` | `00 00 00` | `00 00 00` | `00 00 00` | commun | + +Les octets 2 (`0x02`) et 4 (`0x04`) ont aussi la relation observée +`octet[2] + octet[4] = 0xFF` pour les trois signatures. Cette relation ne +démontre ni checksum ni signification fonctionnelle. + +## Hypothèse de détection de longueur + +L'entier non signé little-endian aux offsets 10–11 (`0x0A–0x0B`) a les valeurs +suivantes pour toutes les occurrences de chaque type : + +| Taille totale observée | Valeur de `octets[10:12]` LE | Écart | +|---:|---:|---:| +| 95 | 80 (`0x0050`) | 15 (`0x000F`) | +| 163 | 148 (`0x0094`) | 15 (`0x000F`) | +| 227 | 212 (`0x00D4`) | 15 (`0x000F`) | + +**Hypothèse probable :** dans cette capture, après lecture de 12 octets à +partir de `55 00`, la taille totale peut être calculée comme +`little_endian_u16(offset 10) + 15`. + +Cette corrélation est vérifiée sur les 267 segments observés. Elle n'établit +pas le nom ou la sémantique de ce champ, ni sa validité pour d'autres versions, +types ou états de la PAC. Elle ne constitue pas une preuve d'un champ longueur +défini par le protocole. + +La valeur à l'offset 8 (`0x08`) est une seconde signature probable de type : +`0x0A → 163`, `0x0B → 227`, `0x0C → 95` dans cette capture. Elle n'est pas une +preuve qu'il s'agit d'un identifiant de trame documenté. + +## Séquence observée + +Les 267 segments suivent exactement le motif répété : + +```text +227, 163, 95, 227, 163, 95, … (89 répétitions) +``` + +Il n'y a ni répétition, ni inversion, ni omission dans la capture. Cela rend la +séquence régulière **pour cet échantillon**. Il n'est pas déterminé si elle est +obligatoire dans le protocole, si elle peut varier avec l'état de la PAC, ou si +des trames supplémentaires peuvent être intercalées. + +## Ce qui est observé, probable et non déterminé + +### Observé + +- `55 00` apparaît 267 fois et permet une partition complète de cette capture. +- Les trois distances entre signatures sont exclusivement 95, 163 et 227. +- Chaque taille apparaît 89 fois, dans l'ordre cyclique indiqué. +- Les 16 octets d'en-tête listés sont constants pour chaque taille dans cet + échantillon. +- Le flow décode des champs aux offsets connus pour les segments de 163 et 227 + octets ; il écarte les autres tailles. + +### Probable, mais non spécifié + +- `55 00` est une signature de début de segment dans ce flux. +- L'entier little-endian aux offsets 10–11 permet de calculer la taille totale + observée par addition de 15. +- L'octet à l'offset 8 distingue les trois catégories observées. + +### Non déterminé + +- La signification des octets d'en-tête, y compris 2, 4, 7, 8, 10–12. +- L'existence, la position ou l'algorithme d'un checksum. +- L'existence d'un champ longueur officiellement défini. +- La sémantique du segment de 95 octets. +- La stabilité des signatures, tailles et de l'ordre avec d'autres PAC, + versions logicielles ou états de fonctionnement. +- La relation entre les limites de cette capture et les lectures effectuées par + une socket TCP. + +## Conséquences pour un extracteur TCP incrémental + +TCP fournit un flux d'octets : une lecture peut contenir une fraction de +segment, un segment complet ou plusieurs segments. Le nœud Node-RED est réglé +en mode `stream`, mais son parser teste directement la taille de chaque message +reçu ; cette capture ne prouve pas que ce comportement soit sûr pour toutes les +segmentations TCP. + +Un extracteur doit donc : + +1. conserver les octets reçus dans un tampon persistant ; +2. rechercher `55 00` sans présumer que le début du tampon est une frontière ; +3. attendre au moins 12 octets avant d'évaluer l'hypothèse de taille ; +4. n'extraire un segment que lorsque la taille attendue est entièrement présente + dans le tampon ; +5. traiter les signatures ou tailles non observées comme non déterminées, sans + les faire passer au parser connu ; +6. prévoir une resynchronisation prudente si l'en-tête observé ne correspond + pas aux signatures documentées ici. + +L'extracteur ne doit pas dépendre du cycle `227 → 163 → 95` pour délimiter les +segments : cette régularité est observée, mais non démontrée comme une règle de +protocole.