🔌 Diagnostic réseau OT/IT · au shell Linux

Chapitre 06
Anatomie d'une trame Profinet

Objectifs du chapitre

1. Profinet : trois classes de trafic

Avant de plonger dans les octets, il faut voir Profinet comme une superposition de flux qui n'ont ni le même rôle, ni la même urgence, ni même la même façon de circuler sur le fil. On en distingue trois grandes familles.

Le point décisif pour le diagnostic : les trames RT ne sont pas des paquets IP. Ce sont des trames Ethernet brutes, identifiées par l'EtherType 0x8892 (« PROFINET »). Pas d'en-tête IP, pas de port TCP/UDP : rien à router. Elles vivent uniquement en couche 2, à l'intérieur du domaine de diffusion. C'est pour cela qu'un outil qui ne regarde que l'IP est aveugle au temps réel — on y revient au §5.

Le trafic cyclique est vital : c'est lui qui maintient les E/S à jour. On l'observe volontiers (capture passive), mais on ne le perturbe jamais sur une installation en production. Ici on travaille sur banc de test : capture et dissection sans conséquence.

2. Structure d'une trame RT

Une trame RT cyclique est étonnamment simple une fois qu'on connaît ses champs. Après les deux adresses MAC et un éventuel tag VLAN, tout tient dans une charge utile Profinet encadrée par un FrameID en tête et une poignée d'octets d'état en queue.

@MAC dest 6 o. @MAC src 6 o. tag 802.1Q VLAN+PCP · opt. EtherType 0x8892 FrameID 2 o. Données process IO data · 40–1440 o. Cycle Counter Data Status Xfer Stat. en-tête Ethernet (couche 2) charge utile PROFINET RT DataStatus (1 octet) → bits d'état : State (Run/Stop) · DataValid · Primary/Backup · ProviderState · StationProblem
Figure 6.1. Une trame RT cyclique du point de vue du fil. Pas d'IP entre l'EtherType 0x8892 et les données : la charge utile Profinet suit immédiatement. Le tag 802.1Q (pointillés) est optionnel mais quasi toujours présent en RT, car il porte la priorité. Les octets de queue — CycleCounter, DataStatus, TransferStatus — disent si la donnée est fraîche et valide.

Passons les champs en revue :

3. Le FrameID, clé de lecture

Le FrameID est le champ qu'on apprend à lire en premier, car il classe la trame instantanément, avant même d'en décoder le contenu. Chaque plage de valeurs correspond à une nature de trafic. Le tableau ci-dessous donne les grandes plages — les bornes exactes varient selon les versions de la spécification, mais tshark les décode de toute façon pour toi ; l'important est l'ordre de grandeur.

Plage FrameID (hexa)Classe / usageNature
0x0100–0x7FFFIRT / RT_CLASS_3 (isochrone)cyclique planifié
0x8000–0xBBFFRT_CLASS_1/2 (E/S cycliques)cyclique temps réel
0xFC01Alarme basse prioritéacyclique
0xFE01Alarme haute prioritéacyclique
0xFEFC–0xFEFFDCP (découverte / configuration)acyclique
0xFF00–0xFF43PTCP (synchronisation d'horloge)acyclique / temps

Concrètement, dès qu'on voit un FrameID 0x8000, on sait sans rien lire d'autre : c'est une trame d'E/S cyclique, la respiration normale du bus. Un 0xFEFE ? Du DCP — quelqu'un cherche ou nomme une station. Un 0xFE01 ? Une alarme haute priorité vient de partir, il se passe quelque chose.

Retiens juste les préfixes hexa : 0x8xxx = E/S cycliques, 0xFC/0xFE01 = alarmes, 0xFEFx = DCP, 0xFFxx = synchro. Lire le FrameID d'un coup d'œil te dit le type de trame avant même de la disséquer — c'est le réflexe le plus rentable du chapitre.

4. VLAN & priorité : faire passer le RT d'abord

Dans un switch, quand plusieurs trames se présentent en même temps, il faut arbitrer. Profinet ne laisse pas le hasard décider : les trames RT portent un tag 802.1Q avec une priorité PCP élevée (couramment PCP = 6), ce qui les place dans une file de sortie prioritaire, devant le trafic « best-effort » (web, SNMP, ping…). C'est ce qui garantit que la cadence temps réel tient même quand le réseau est chargé.

Sur une capture, le tag se lit dans les champs vlan.id et vlan.priority :

$ tshark -r cell.pcap -Y pn_rt -T fields -e vlan.id -e vlan.priority -e pn_rt.frame_id # tag + FrameID
0	6	0x8000
0	6	0x8001
0	6	0x8000

Ici le VLAN vaut 0 (VLAN par défaut, ou « priority-tagged » : on ne se sert du tag que pour la priorité), mais la priorité 6 confirme qu'on regarde bien du trafic prioritaire temps réel.

5. Capturer avec tshark

Puisque le RT n'a pas d'IP, on filtre au niveau liaison. Le filtre BPF de capture le plus utile est tout simplement l'EtherType Profinet :

# Capturer TOUT le Profinet sur l'interface machine (couche 2)
sudo tshark -i eth0 -f 'ether proto 0x8892'

Le -f 'ether proto 0x8892' est un filtre de capture (BPF) : il travaille sur le fil, avant tout décodage. Une fois les trames capturées (ou relues depuis un .pcap), on affine avec les filtres d'affichage Profinet, la famille pn_* :

Filtres d'affichage tshark à connaître :

Pour du diagnostic scriptable, on extrait des champs précis avec -T fields. Par exemple, le FrameID, l'émetteur et le compteur de cycle de chaque trame RT :

$ tshark -r cell.pcap -Y pn_rt \
    -T fields -e pn_rt.frame_id -e eth.src -e pn_rt.cycle_counter
0x8000	00:1b:1b:2a:10:20	14208
0x8001	00:1b:1b:2a:10:01	14210
0x8000	00:1b:1b:2a:10:20	14224
0x8000	00:1b:1b:2a:10:20	14240
0x8001	00:1b:1b:2a:10:01	14242

On lit tout de suite la conversation cyclique : l'ET 200SP …10:20 émet des trames 0x8000 (ses entrées → contrôleur), le CPU …10:01 émet des 0x8001 (ses sorties → device), et le cycle_counter avance régulièrement de trame en trame. Un saut dans ce compteur signalerait des trames perdues.

On capture obligatoirement en couche 2. Le RT n'a pas d'adresse IP : un filtre host 192.168.0.20 ne l'attrapera jamais, car il n'y a pas d'IP à matcher dans la trame. Et rappelle-toi le piège WSL2 du chapitre 1 : derrière le NAT par défaut, tu ne verras aucune trame 0x8892. Capture depuis l'edge Debian ou passe WSL2 en mode miroir.

6. Un extrait décodé, et le démarrage d'une station

Pour voir tous les champs d'une trame, on demande la vue verbeuse avec -V. Sur une trame RT cyclique, on retrouve la structure du §2 :

$ tshark -r cell.pcap -Y 'pn_rt && pn_rt.frame_id == 0x8000' -V -c 1
...
PROFINET Real-Time
    FrameID: 0x8000 (RT_CLASS_1)
    IOData
        # image des entrées du device (octets process)
    CycleCounter: 14208
    DataStatus: 0x35
        .... ...1 = State: Primary
        .... ..0. = Redundancy: primary AR
        .... .1.. = DataValid: valid
        ...1 .... = ProviderState: Run
        ..1. .... = StationProblem: normal
    TransferStatus: 0

Le DataStatus: 0x35 se lit bit à bit : la station est en Primary, les données sont valides, le fournisseur est en Run, pas de StationProblem. C'est l'état sain qu'on veut voir sur toutes les trames cycliques.

Enfin, le démarrage d'une station illustre l'enchaînement des trois classes de trafic. Filtre large sur une capture prise au moment de la mise sous tension d'un device :

$ tshark -r start.pcap -Y 'pn_dcp || pn_io || pn_rt' -T fields \
    -e frame.number -e pn_rt.frame_id -e eth.src -e _ws.col.info
1  0xfefe  00:1b:1b:2a:10:01  # DCP Identify (le contrôleur cherche la station)
2  0xfeff  00:1b:1b:2a:10:20  # DCP Ident Ok (la station répond)
3  0xfefd  00:1b:1b:2a:10:01  # DCP Set : Name-of-Station / IP
5         192.168.0.1        # Connect Request (RPC UDP 34964) : ouverture de l'AR
6         192.168.0.20       # Connect Response : IOCR négociés
9  0x8000  00:1b:1b:2a:10:20  # première trame cyclique : l'échange RT démarre

La séquence est toujours la même : DCP (on identifie et on nomme la station, on lui donne son IP) → Connect / AR (via RPC sur UDP 34964, on négocie les relations de communication et les IOCR) → échange cyclique (les FrameID 0x8xxx commencent à défiler). Reconnaître ces trois temps dans une capture, c'est savoir un démarrage se bloque.

Côté TIA Portal, ce que tu vois ici a un nom. L'AR (Application Relation) établie par le Connect correspond à la relation « le CPU pilote ce device ». Les IOCR (IO Communication Relations) sont les canaux d'entrées et de sorties cycliques que tu configures dans les propriétés du device (temps de cycle « Watchdog », modules d'E/S). Le FrameID 0x8000 capturé sur le fil est exactement l'IOCR d'entrée que TIA a alloué à cette station.

La famille pn_* de dissecteurs (pn_rt, pn_dcp, pn_io, pn_alarm, pn_ptcp) est intégrée à Wireshark/tshark depuis longtemps : aucune extension à installer. Si un champ ne s'affiche pas, vérifie l'orthographe du filtre avec tshark -G fields | grep pn_rt.

Récapitulatif

Exercices

Exercice 1 — Compter les types de trames

Tu as capturé le trafic Profinet dans cell.pcap. Compte combien de trames de chaque type il contient : DCP, RT cyclique, et alarmes. Quelle(s) commande(s) utiliser ?

Voir la solution

On relit le fichier avec un filtre d'affichage par famille et on compte les lignes :

$ for f in pn_dcp pn_rt pn_alarm; do \
    printf '%-9s ' "$f"; tshark -r cell.pcap -Y "$f" 2>/dev/null | wc -l; done
pn_dcp    4
pn_rt     1876
pn_alarm  0

Sans surprise, le cyclique domine largement (des milliers de trames pn_rt pour quelques pn_dcp de découverte). Zéro alarme = bon signe, la cellule tourne sans défaut. On aurait pu capturer directement du fil avec sudo tshark -i eth0 -f 'ether proto 0x8892' -a duration:10 -w cell.pcap avant de compter.

Exercice 2 — Qui émet du cyclique ?

À partir de cell.pcap, dresse la liste des équipements qui émettent des trames RT cycliques, avec le FrameID qu'ils utilisent. Autrement dit : quels eth.src pour quels frame_id ?

Voir la solution
$ tshark -r cell.pcap -Y pn_rt \
    -T fields -e pn_rt.frame_id -e eth.src | sort | uniq -c
    938 0x8000  00:1b:1b:2a:10:20
    938 0x8001  00:1b:1b:2a:10:01

Deux émetteurs cycliques : l'ET 200SP …10:20 pousse ses entrées en 0x8000, le CPU …10:01 pousse ses sorties en 0x8001. Le nombre quasi identique de trames (938 chacune) confirme un échange régulier et symétrique — un cycle par sens, sans perte visible. Si un device attendu n'apparaît pas dans la liste, c'est qu'il n'échange pas de cyclique : connexion non établie, ou station absente.

Exercice 3 — Classer une trame inconnue

On te donne une trame isolée. En vue verbeuse, tu relèves : vlan.priority = 6, vlan.id = 0, pn_rt.frame_id = 0xFE01. À quelle classe de trafic cette trame appartient-elle, et qu'est-ce que ça t'apprend ?

Voir la solution

On lit les indices dans l'ordre :

  • Priorité 6 + tag 802.1Q → trame Profinet prioritaire, traitée avant le best-effort par les switches.
  • FrameID 0xFE01 → d'après le tableau du §3, c'est une alarme haute priorité — donc du trafic acyclique, pas du cyclique d'E/S.

Conclusion : ce n'est pas une trame d'échange normal, c'est une alarme urgente émise par une station (défaut de module, retrait/insertion, diagnostic). On confirmerait avec :

$ tshark -r cell.pcap -Y 'pn_alarm' -V | grep -iE 'alarm|frame id'

Le réflexe FrameID paie : sans lire la charge utile, on sait déjà qu'il faut aller regarder ce qui cloche sur la station émettrice.