Quelle est la sécurité intrinsèque \ n \ r en tant qu'octets d'arrêt?

8

Dans ma communication UART, j'ai besoin de connaître l'octet de début et l'octet d'arrêt du message envoyé. L'octet de début est facile mais l'octet d'arrêt, pas tellement. J'ai implémenté deux octets d'arrêt à la fin de mon message, c'est-à-dire \ n et \ r (10 et 13 décimales). UART ne fonctionne que sur les valeurs d'octets 0-255, alors à quel point est-ce sûr? Je peux imaginer, bien que peu probable, que mon message puisse contenir les valeurs "10 et 13" les unes après les autres alors qu'il ne s'agit pas des octets d'arrêt.

Existe-t-il une meilleure façon de mettre cela en œuvre?

CK
la source
7
Pour envoyer des données arbitraires, vous devez soit utiliser des paquets, soit le bourrage d'octets. Dans votre cas, la probabilité que le motif apparaisse à un certain endroit est de 1/65536. Ce qui passe à 1 si vous disposez d'un flux de données aléatoire suffisamment long.
Oldfart
4
Pouvez-vous fournir le contexte s'il vous plaît. Les bits d'arrêt font partie de la communication UART mais les octets d'arrêt? Cela ressemble à un problème logiciel pur et dépend de ce qui a été convenu par l'expéditeur et le destinataire.
Warren Hill
2
@MariusGulbrandsen si vos données sont vraiment arbitraires et pas strictement textuelles (pensez ASCII) alors la terminaison nulle ne fonctionnera pas; vous devrez implémenter un paquet.
RamblinRose
4
BTW: Cette pratique courante est de mettre le retour chariot avant l'alimentation en ligne: "\x0D\x0A".
Adrian McCarthy
3
@AdrianMcCarthy Je pense que le point de l'inversion est de minimiser les chances que ce soit une séquence valide. Cela dit, deux fins de ligne Windows consécutives vous donneraient ce \r\n\r\nqui contient la \n\rséquence au milieu ...
Mike Caron

Réponses:

14

Il existe différentes façons d'empêcher cela:

  • Assurez-vous de ne jamais envoyer de combinaison 10/13 dans vos messages réguliers (donc uniquement en octets d'arrêt). Par exemple pour envoyer 20 21 22 23 24 25:

20 21 22 23 24 25 10 13

  • Escape 10 et 13 (ou tous les caractères non ASCII avec un caractère d'échappement par exemple. Donc pour envoyer 20 21 10 13 25 26 envoyer: (voir commentaire / crédits pour: DanW)

20 21 1b 10 1b 13 25 26

  • Définissez un paquet lors de l'envoi de messages. Par exemple, si vous souhaitez envoyer un message 20 21 22 23 24 25, ajoutez plutôt le nombre d'octets à envoyer, le package est donc:

<nr_of_data_bytes> <données>

Si vos messages font au maximum 256 octets, envoyez:

06 20 21 22 23 24 25

Donc, vous savez, après avoir reçu 6 octets de données, c'est la fin; vous n'avez pas besoin d'envoyer un 10 13 par la suite. Et vous pouvez envoyer 10 13 dans un message. Si vos messages peuvent être plus longs, vous pouvez utiliser 2 octets pour la taille des données.

Mise à jour 1: une autre façon de définir les paquets

Une autre alternative consiste à envoyer des commandes qui ont une longueur spécifique et peuvent avoir de nombreux écarts, par exemple

10 20 30 (commande 10 qui a toujours 2 octets de données)

11 30 40 50 (commande 11 qui a toujours 3 octets de données)

12 06 10 11 12 13 14 15 (commande 12 + 1 octet pour le nombre d'octets de données qui suivent)

13 01 02 01 02 03 ... (Commande 13 + 2 octets (01 02 pour 256 + 2 = 258 octets de données qui suivent)

14 80 90 10 13 (commande 14 suivie d'une chaîne ASCII se terminant par 10 13)

Mise à jour 2: Mauvaise connexion / pertes d'octets

Tout ce qui précède ne fonctionne que lorsque la ligne UART envoie correctement les octets. Si vous souhaitez utiliser des moyens d'envoi plus fiables, il existe également de nombreuses possibilités. En voici quelques-uns:

  1. Envoi d'une somme de contrôle dans le package (consultez google pour CRC: Cyclic Redundancy Check). Si le CRC est correct, le récepteur sait que le message a été envoyé correctement (avec une probabilité élevée).
  2. Si vous avez besoin d'un message pour être renvoyé, alors un mécanisme d'accusé de réception (ACK / réponse) doit être utilisé (par exemple, l'expéditeur envoie quelque chose, le récepteur reçoit des données corrompues, envoie un NACK (non acquitté), l'expéditeur peut alors renvoyer.
  3. Délai d'expiration: dans le cas où le récepteur ne reçoit pas un ACK ou un NACK à temps, un message doit être renvoyé.

Notez que tous les mécanismes ci-dessus peuvent être simples ou aussi compliqués que vous le souhaitez (ou devez) être. En cas de renvoi de message, un mécanisme d'identification des messages est également nécessaire (par exemple, l'ajout d'un numéro de séquence dans le paquet).

Michel Keijzers
la source
1
"Assurez-vous de ne jamais envoyer une combinaison 10/13 dans vos messages réguliers (donc uniquement en tant qu'octets d'arrêt)." - vous ne l' avez pas dit comment envoyer des données qui ne comprennent une combinaison 10/13 - vous devez échapper. Donc "20 10 13 23 10 13" pourrait être envoyé comme "20 1b 10 1b 13 23" avec 1b comme caractère d'échappement.
Dan W
1
Notez que l'utilisation d'un champ de longueur comme proposé, vous aurez des problèmes lorsque votre liaison série est mauvaise et perd un seul octet. Tout se désynchronisera.
Jonas Schäfer
@DanW Si vous utilisez le premier ou les 2 octets comme nombre d'octets de données, peu importe si 10 ou 13 font partie de ces données ... Donc, 20 10 13 23 10 13 peut être envoyé au 06 20 10 13 23 10 13 où 06 est le nombre d'octets de données qui suivent.
Michel Keijzers
@MichelKeijzers - oui, mais c'est la deuxième solution que vous mentionnez. Votre première solution manque une explication des séquences d'échappement pour empêcher la transmission des octets d'arrêt.
Dan W
Les deux approches fonctionnent et sont couramment utilisées, mais elles présentent des avantages et des inconvénients différents, que vous pouvez ajouter si vous le souhaitez, bien que ce soit au-delà de ce que le PO a demandé.
Dan W
13

Quelle est la sécurité intrinsèque \ n \ r en tant qu'octets d'arrêt?

Si vous envoyez envoyer des données arbitraires -> probablement pas assez sûr.

Une solution courante consiste à utiliser l'échappement:

Définissons que les caractères 0x02 (STX - début de trame) et 0x03 (ETX - fin de trame) doivent être uniques dans le flux de données transmis. De cette façon, le début et la fin d'un message peuvent être détectés en toute sécurité.

Si l'un de ces caractères doit être envoyé dans le cadre du message, il est remplacé par le préfixe d'un caractère d'échappement (ESC = 0x1b) et l'ajout de 0x20 au caractère d'origine.

Caractère original remplacé par

0x02 -> 0x1b 0x22  
0x03 -> 0x1b 0x23  
0x1b -> 0x1b 0x3b  

Le récepteur inverse ce processus: chaque fois qu'il reçoit un caractère d'échappement, ce caractère est supprimé et le caractère suivant est soustrait par 0x20.

Cela ne fait qu'ajouter des frais généraux de traitement mais est fiable à 100% (en supposant qu'aucune erreur de transmission ne se produise, que vous pourriez / devriez vérifier en implémentant en outre un mécanisme de somme de contrôle).

Rev1.0
la source
1
Bonne réponse. Le caractère d'échappement commun utilisé pour les protocoles ASCII était '\x10'DLE (Data Link Escape). Certaines pages de Wikipédia suggèrent que le DLE était souvent utilisé dans le sens inverse: pour dire que l'octet suivant était un caractère de contrôle plutôt qu'un octet de données. D'après mon expérience, c'est généralement le sens opposé d'une évasion.
Adrian McCarthy
2
Une chose à surveiller ici est que la taille de la mémoire tampon de votre pire cas double. Si la mémoire est vraiment étroite, ce n'est peut-être pas la meilleure solution.
TechnoSam
1
@Rev Quelle est la justification de l'ajout de 0x20 au caractère d'origine? Le schéma d'évasion ne fonctionnerait-il pas aussi bien sans cela?
Nick Alexeev
1
@NickAlexeev: Il est plus facile / plus rapide d'identifier les limites de trame réelles si vous supprimez toute autre occurrence des caractères réservés du flux. De cette façon, vous pouvez séparer la réception des trames et l'analyse des trames (y compris le non-échappement). Cela peut être particulièrement pertinent si vous avez un contrôleur très lent sans FIFO et / ou des débits de données élevés. Ainsi, vous pouvez simplement copier les octets entrants (entre STX / ETX) dans le tampon de trame à leur arrivée, marquer la trame comme terminée et effectuer le traitement avec une priorité inférieure.
Rev1.0
@TechnoSam: Bon point.
Rev1.0
5

Vous savez, ASCII a déjà des octets pour ces fonctions.

  • 0x01: début d'en-tête - octet de début
  • 0x02: début du texte - fin des en-têtes, début de la charge utile
  • 0x03: fin du texte - fin de la charge utile
  • 0x04: fin de transmission - octet d'arrêt
  • 0x17: fin du bloc de transmission - le message continue dans le bloc suivant

Il a également des codes pour diverses utilisations à l'intérieur de la charge utile.

  • 0x1b: escape (échapper le caractère suivant - utiliser dans la charge utile pour indiquer que le caractère suivant ne fait pas partie de la structure décrivant les codes utilisés dans votre protocole)
  • 0x1c, 0x1d, 0x1e, 0x1f: fichier, groupe, enregistrement et séparateur d'unité, respectivement - utilisés comme octet d'arrêt et de démarrage simultanés pour des parties de données hiérarchiques

Votre protocole doit spécifier la granularité la plus fine de ACK (0x06) et NAK (0x15), afin que les données acquittées négatives puissent être retransmises. Jusqu'à cette granularité la plus fine, il est sage d'avoir un champ de longueur immédiatement après tout indicateur de démarrage (non échappé) et (comme expliqué dans d'autres réponses), il est sage de suivre tout indicateur d'arrêt (non échappé) avec un CRC.

Eric Towers
la source
J'enverrai des données arbitraires, je suppose que cela pourrait être déroutant d'utiliser "\ n \ r" dans ma question lorsque je n'envoie pas de données ASCII. Même si, j'aime cette réponse, elle est très informative sur l'envoi d'ASCII sur UART
CK
@MariusGulbrandsen: Tant que votre protocole établit où se trouve la charge utile et quels codes doivent être échappés dans chaque section de charge utile, vous pouvez envoyer n'importe quoi, pas seulement des données textuelles.
Eric Towers
4

L'UART n'est pas à sécurité intrinsèque par sa nature même - nous parlons ici de la technologie des années 60.

La racine du problème est que l'UART ne se synchronise qu'une fois par 10 bits, ce qui permet à beaucoup de charabia de passer entre ces périodes de synchronisation. Contrairement à CAN par exemple, qui échantillonne chaque bit individuel plusieurs fois.

Toute erreur double bit se produisant à l'intérieur des données corrompra une trame UART et passera sans être détectée. Les erreurs de bits dans les bits de démarrage / d'arrêt peuvent ou non être détectées sous la forme d'erreurs de dépassement.

Par conséquent, peu importe si vous utilisez des données brutes ou des paquets, il y a toujours une probabilité que les retournements de bits causés par EMI entraînent des données inattendues.

Il existe de nombreuses façons de "charlatanisme UART traditionnel" pour améliorer légèrement la situation. Vous pouvez ajouter des octets de synchronisation, des bits de synchronisation, la parité, des bits d'arrêt double. Vous pouvez ajouter des sommes de contrôle qui comptent la somme de tous les octets (puis l'inverser - parce que pourquoi pas) ou vous pouvez compter le nombre de binaires comme une somme de contrôle. Tout cela est largement utilisé, très peu scientifique et avec une forte probabilité d'erreurs manquantes. Mais c'est ce que les gens ont fait des années 1960 aux années 1990 et beaucoup de choses étranges comme celles-ci continuent aujourd'hui.

La manière la plus professionnelle de gérer une transmission sûre sur UART est d'avoir une somme de contrôle CRC 16 bits à la fin du paquet. Tout le reste n'est pas très sûr et a une forte probabilité d'erreurs manquantes.

Ensuite, au niveau matériel, vous pouvez utiliser le différentiel RS-422 / RS-485 pour améliorer considérablement la robustesse de la transmission. C'est un must pour une transmission sûre sur de plus longues distances. L'UART de niveau TTL ne doit être utilisé que pour la communication à bord. RS-232 ne doit pas être utilisé à d'autres fins que la rétrocompatibilité avec des éléments anciens.

Dans l'ensemble, plus le mécanisme de détection d'erreur est proche du matériel, plus il est efficace. En termes d'efficacité, les signaux différentiels ajoutent le plus, suivi de la vérification des erreurs de cadrage / dépassement, etc. CRC16 en ajoute, puis le "charlatanisme UART traditionnel" en ajoute un peu.

Lundin
la source
7
Ce conseil est assez tangentiel - vous n'avez pas réellement répondu à la question posée. En particulier, les solutions que vous proposez peuvent résoudre d'autres problèmes, mais elles ne résolvent pas le problème de base de la question sur cette page , qui est la confusion entre les byes de cadrage et les byes de charge utile. Tout au plus, votre proposition rejetterait des données valides incorporant un octet de trame en raison de CRC ou d'une défaillance similaire, sans aucun moyen de les communiquer.
Chris Stratton
3
En fait, cette réponse aggrave la situation. L'original ne contenait que des octets de données et des octets d'arrêt. Cela ajoute une troisième catégorie, les octets CRC. Et comme présenté ici, ceux-ci peuvent prendre n'importe quelle valeur, y compris {10,13}.
MSalters
1
@MSalters: Le CRC peut être au format hexadécimal ASCII pour éviter ce problème. Une autre astuce que j'ai vue sur RS485 est de définir le bit 7 sur l'octet de début / adresse.
Transistor
Re "CAN qui échantillonne chaque bit individuel plusieurs fois." : L'échantillonnage réel de la valeur de bit est seulement une fois par bit. De quoi parlez-vous ici? Une sorte de vérification d'erreur, comme par l'expéditeur? Synchronisation d'horloge?
Peter Mortensen
L'inversion de la somme de contrôle a été effectuée de sorte que la somme de l'ensemble du bloc de données se traduise par un zéro, ce qui est un peu plus facile à coder et un peu plus rapide à exécuter. En outre, le CRC est bien meilleur que vous ne le pensez, recherchez-le dans Wikipedia.
toolforger
0

... Je peux imaginer, bien que peu probable, que mon message puisse contenir les valeurs "10 et 13" les unes après les autres alors qu'il ne s'agit pas des octets d'arrêt.

Une situation où une partie des données est égale à la séquence de terminaison doit être prise en compte lors de la conception du format d'un paquet de données série. Une autre chose à considérer est que n'importe quel personnage peut être corrompu ou perdu pendant la transmission. Un caractère de début, un caractère d'arrêt, un octet de charge utile de données, une somme de contrôle ou un octet CRC, un octet de correction d'erreur directe ne sont pas à l'abri de la corruption. Le mécanisme de tramage doit pouvoir détecter lorsqu'un paquet contient des données corrompues.

Il y a plusieurs façons d'aborder tout cela.

Je fais l'hypothèse de travail que les paquets sont encadrés uniquement avec les octets série. Les lignes de poignée de main ne sont pas utilisées pour le cadrage. Les délais ne sont pas utilisés pour le cadrage.

Envoyer la longueur du paquet

Envoyez la longueur du paquet au début, au lieu de [ou en plus de] le caractère de fin à la fin.

avantages: la charge utile est envoyée dans un format binaire efficace.

inconvénients: Besoin de connaître la longueur du paquet au début de la transmission.

Échapper aux personnages spéciaux

Échappez aux caractères spéciaux lors de l'envoi des données utiles. Ceci est déjà expliqué dans une réponse antérieure .

avantages: l' expéditeur n'a pas besoin de connaître la longueur du paquet au début de la transmission.

inconvénients: légèrement moins efficace, selon le nombre d'octets de charge utile à échapper.

Données utiles codées de manière à ne pas pouvoir contenir de caractères de début et de fin

La charge utile du paquet est codée de telle sorte qu'elle ne peut pas contenir les caractères de début ou d'arrêt. Habituellement, cela se fait en envoyant des nombres en tant que leur représentation ASCII ou Hex-ASCII.

Avantages: lisibles par l'homme avec des programmes terminaux courants. Pas besoin de code pour gérer l'échappement. Pas besoin de connaître la longueur du paquet au début de la transmission

inconvénients: efficacité moindre. Pour un octet de données utiles, plusieurs octets sont envoyés.

Nick Alexeev
la source