Maison  >  Article  >  Opération et maintenance  >  Voyons comment apprendre les protocoles réseau via le protocole QUIC

Voyons comment apprendre les protocoles réseau via le protocole QUIC

青灯夜游
青灯夜游avant
2022-03-01 09:57:243295parcourir

Cet article vous amènera à comprendre le protocole QUIC et à utiliser le protocole QUIC comme exemple pour expliquer comment apprendre les protocoles réseau. J'espère qu'il sera utile à tout le monde !

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Dans un article précédent sur s2n-quic, un lecteur m'a demandé comment apprendre un protocole réseau comme QUIC. Pour la plupart des praticiens d'Internet, bien qu'ils travaillent quotidiennement sur Internet, peu de gens se soucient des détails du protocole réseau sous HTTP. La plupart du temps, il suffit d'avoir une compréhension générale et de savoir comment l'utiliser. Si vous n'avez aucune idée de QUIC, alors l'image suivante peut vous aider à bien comprendre la position de QUIC dans l'écosystème HTTP/3 :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Donc, si vous voulez juste en savoir plus sur QUIC en détail, comment obtenir commencé?

En tant qu'ancien développeur de protocoles réseau et d'équipements réseau, ma propre expérience est la suivante : commencer par RFC, complété par la capture de paquets Wireshark, pour maîtriser rapidement le protocole cible.

Pour QUIC, la première chose que nous devons lire est la RFC9000. Lire l'accord est très ennuyeux et demande une certaine patience. Si votre anglais n'est pas très bon, vous pouvez utiliser Google Translate pour le traduire en chinois et le parcourir rapidement (lecture approfondie). La première lecture consiste principalement à comprendre les principaux concepts et principaux processus.

Après cela, nous pouvons écrire un programme en utilisant le protocole QUIC, puis capturer les paquets via Wireshark. En étudiant les messages réels et en les comparant avec le contenu du protocole RFC (lecture intensive), nous pouvons acquérir une compréhension plus approfondie de. l'essence du protocole.

Nous allons construire le client et le serveur echo sur la base du code de l'article précédent. Pour faciliter la lecture par tout le monde, j'ai également publié le code. Les étudiants intéressés peuvent cloner mon dépôt et exécuter le code client/serveur.

Code client (voir github : tyrchen/rust-training/live_coding/quic-test/examples/client.rs) :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Code du serveur (voir github : tyrchen/rust-training/live_coding /quic -test/examples/server.rs) :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Ces deux morceaux de code construisent le serveur d'écho le plus simple. Nous pouvons utiliser Wireshark pour surveiller les paquets UDP sous l'interface de bouclage locale et capturer les paquets. Il convient de noter que pour le trafic utilisant le protocole TLS comme QUIC, même si les paquets sont capturés, seuls les premiers paquets peuvent être lisibles, et les paquets suivants sont cryptés et ne peuvent pas être lus. Par conséquent, lorsque nous construisons un client/serveur, nous devons trouver un moyen de capturer la clé de session négociée entre le serveur et le client pour que Wireshark puisse la déchiffrer. Généralement, les bibliothèques SSL/TLS proposent cette fonction. Par exemple, pour Rustls, nous pouvons utiliser key_log dans la configuration tls. Si vous regardez attentivement le code du serveur ci-dessus, vous verrez cette phrase :

let config = Builder::new()
    .with_certificate(CERT_PEM, KEY_PEM)?
    .with_key_logging()? # 使能 keylogging
    .build()?;

Après avoir utilisé key_log, lors du démarrage du serveur, il nous suffit de spécifier SSLKEYLOGFILE :

SSLKEYLOGFILE=session.log cargo run --example server

Une fois la capture du paquet terminée, ouvrez les préférences Wireshark, sélectionnez le protocole TLS et insérez le chemin du journal :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Ce qui suit est une capture complète du paquet de l'interaction entre le client et le serveur. Nous voyons que toutes les "charges utiles protégées" sont affichées normalement :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

.

Étant donné que notre client echo n'effectue que l'action la plus simple (ouvre uniquement un flux bidirectionnel), grâce à cette capture de paquets, nous pouvons nous concentrer sur l'étude du processus d'établissement d'une connexion à l'aide du protocole QUIC.

Le premier paquet envoyé par le client

Regardons le premier message envoyé par le client :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Ce message contient des informations très riches. Tout d'abord, contrairement à la prise de contact TCP, le premier paquet de QUIC est très volumineux, jusqu'à 1 200 octets (le protocole nécessite une charge utile UDP d'au moins 1 200 octets), y compris l'en-tête QUIC, une trame CRYPTO de 255 octets et 890 octets. trame de PADDING d’octets. Comme vous pouvez le voir sur l'en-tête, le type de ce package QUIC est Initial.

Type de message QUIC

Pour les paquets QUIC, l'en-tête est en texte brut et toutes les charges utiles de trame suivantes sont en texte chiffré (à l'exception des premiers paquets). Nous voyons que ce premier paquet est un message Long Header. Dans la section 17.2 de la RFC9000, le Long Header Packet est défini :

Long Header Packet {
   Header Form (1) = 1,
   Fixed Bit (1) = 1,
   Long Packet Type (2),
   Type-Specific Bits (4),
   Version (32),
   Destination Connection ID Length (8),
   Destination Connection ID (0..160),
   Source Connection ID Length (8),
   Source Connection ID (0..160),
   Type-Specific Payload (..),
 }

Si vous êtes intéressé, vous pouvez lire vous-même le chapitre correspondant de la RFC. Pour les messages à en-tête long, il existe les types suivants :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

既然有 Long Header packet,那么就有 Short Header packet,Short Header packet 目前的版本只有一种:

1-RTT Packet {
   Header Form (1) = 0,
   Fixed Bit (1) = 1,
   Spin Bit (1),
   Reserved Bits (2),
   Key Phase (1),
   Packet Number Length (2),
   Destination Connection ID (0..160),
   Packet Number (8..32),
   Packet Payload (8..),
}

为什么需要 connection id?

在我们捕获的这个报文头中,我们看到有 Source Connection ID(SCID)和 Destination Connection ID(DCID)这个新的概念。你也许会好奇:QUIC 不是基于 UDP/IP 的协议么?底层的协议已经有五元组(src ip / src port / dst ip / dst port / protocol)来描述一个连接(connection),为什么还需要 connection id 这样一个新的概念?

这是为了适应越来越多的移动场景。有了 QUIC 层自己的 connection id,底层网络(UDP/IP)的变化,并不会引发 QUIC 连接的中断,也就是说,你从家里开车出门,即便手机的网络从 WIFI(固网运营商分配给你的 IP)切换到蜂窝网络(移动运营商分配给你的 IP),整个 UDP/IP 网络变化了,但你的 QUIC 应用只会感受到细微的延迟,并不需要重新建立 QUIC 连接。

从这个使用场景来看,QUIC 底层使用无连接的 UDP 是非常必要的。

首包中就包含了 TLS hello?

我们接下来看看 CRYPTO frame:

Voyons comment apprendre les protocoles réseau via le protocole QUIC

可以看到,QUIC 在建立连接的首包就把 TLS Client Hello 囊括在 CRYPTO frame 中。并且使用的 TLS版本是 1.3。在 Client Hello 的 extension 中,QUIC 协议使用了一个 quic_transport_parameters 的 extension,用来协商 QUIC 自己的一些初始值,比如支持多少个 stream,这个连接中可以最多使用多少个 active connection id 等等。

QUIC 支持哪些 frame?

现在我们已经见到了两种 Frame:CRYPTO 和 PADDING。下表中罗列了 QUIC 所有支持的 frame:

Voyons comment apprendre les protocoles réseau via le protocole QUIC

服务器的回包

我们来看 server 的回包:

Voyons comment apprendre les protocoles réseau via le protocole QUIC

这里有一些新东西。首先,一个 UDP 包内部可以包含若干个 QUIC payload,我们看到 server 回复了一个 QUIC Initial 报文和一个 QUIC Handshake 报文。在 Initial 报文中,我们看到了一个 ACK frame,可见 QUIC 虽然构建于 UDP,但在 QUIC 协议内部构建了类似 TCP 的确认机制。

我们之前看到,在 Initial 报文的 CRYPTO frame 中,客户端发送了 TLS Client Hello,同样的,服务器在 Initial 报文的 CRYPTO frame 中发送了 TLS Server Hello。这个我们就略过不看。

在 Handshake 报文中:

Voyons comment apprendre les protocoles réseau via le protocole QUIC

服务器发送了自己的证书,并结束了 TLS handshake。

客户端结束 Handshake

我们再看第三个包,客户端发送给服务器结束 TLS 握手:

Voyons comment apprendre les protocoles réseau via le protocole QUIC

这个包依旧包含两个 QUIC 报文,其中第一个就是一个 ACK frame,来确认收到了服务器的 Server Hello 那个 QUIC 报文;第二个包含一个 ACK frame,确认服务器的 Handshake,随后有一个 CRYPTO frame 结束客户端的 TLS handshake。

TLS 握手结束之后,客户端和服务器就开始应用层的数据交换,此刻,所有数据都是加密的。

客户端发送一个 “hello” 文本

在我们的  echo client/server 代码中,客户端连接到服务器后,就可以等待用户在 stdin 的输入,然后将其发送到服务器。服务器收到客户端数据,原封不动发回,客户端再将其显示到 stdout 上。在这个过程的前后,客户端和服务器间有一些用于连接管理的 QUIC 报文,比如 PING。我们就略过,只看发送应用层数据的报文。下图是客户端发送的包含 “hello” 文本的报文:

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Comme vous pouvez le voir, le message QUIC ici est un paquet Short Header En plus de la trame ACK, il possède également une trame STREAM. Les deux chiffres les plus bas de l'ID de flux de ce flux sont 00, ce qui représente un flux bidirectionnel lancé par le client. Puisque deux bits sont utilisés pour exprimer le type, le flux de QUIC a les types suivants :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

Regardons la longueur (6) et les données (68 65 6c 6c 6f 0a) de la trame STREAM. Si le contenu de Data est exprimé en ASCII, il s'agit exactement de "hello" et sa longueur est de 6 octets.

Le serveur répond avec le texte "bonjour"

Finalement, le serveur répond :

Voyons comment apprendre les protocoles réseau via le protocole QUIC

C'est exactement le même que le message ci-dessus, donc je ne l'expliquerai pas.

Sage Moment

Je crois que l'introduction ci-dessus à QUIC basée sur la capture de paquets Wireshark peut vous donner une compréhension préliminaire du protocole QUIC. Dans le dernier article, nous avons dit que QUIC prend en charge le multiplexage et résout le problème du blocage en tête de file d'attente au niveau de la couche transport. A travers l’introduction de cet article, pouvez-vous répondre aux deux questions suivantes ?

  • Quel type de trame QUIC utilise-t-il pour le multiplexage ?

  • QUIC Comment résoudre le blocage en tête de file d'attente au niveau de la couche transport ?

Recommandations associées : Sécurité du serveur Web

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer