s2n-quicとpcap2qlogを使ってqlogを生成してみた

ツイッターで、pcap2qlogというpcapをqlogに変換するツールを教えていただきました。

今回は、s2n-quic と pcap2qlog を組み合わせて、qlogを生成してみたので紹介します。

pcap2qlog をインストールする

pcap2qlogのREADME.md の手順に従いインストールします。

まずリポジトリをクローンします。

$ git clone https://github.com/quiclog/pcap2qlog
$ cd pcap2qlog

次に、レポジトリの中で以下のコマンドを実行します。

$ npm install
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN qlog-pcap@1.0.0 No repository field.

added 5 packages from 38 contributors and audited 5 packages in 3.749s
found 0 vulnerabilities

$ npx tsc

こうすると、以下のコマンドでpcapをqlogに変換するツールが実行できるようになります。

$ node out/main.js 
No input file or list of files specified, use --input or --list

s2n-quicでkeylogファイルを出力する

pcap ファイルは暗号化されています。それを復号するためにkeylogファイルを書きだす必要があります。

「key_log」という名前のAPIが無いかを検索すると、クライアントとサーバーそれぞれにkeylogを出力するためのAPIがあることが分かります。

s2n_quic::provider::tls::default::client::Builder::with_key_logging
s2n_quic::provider::tls::default::server::Builder::with_key_logging

githubのソースを検索すると、テストコード で使っているのを見つけました。

        let tls = s2n_quic::provider::tls::default::Server::builder()
            .with_certificate(certificate, private_key)?
            .with_application_protocols(
                self.opts.application_protocols.iter().map(String::as_bytes),
            )?
            .with_key_logging()?
            .build()?;

このテストコードは、echoのexampleとは、サーバーのインスタンスの生成方法が異なっています。

    let mut server = Server::builder()
        .with_tls((CERT_PEM, KEY_PEM))?
        .with_io("127.0.0.1:4433")?
        .start()?;

with_tlsのAPI を見ると、s2n_quic::provider::tls::default::Server::builder を使って生成したtlsインスタンスを渡せばよさそうなことが分かります。

なので、echoのexampleの宣言を以下のように変更しました。

    let tls = s2n_quic::provider::tls::default::Server::builder()
        .with_key_logging()?
        .with_certificate(CERT_PEM, KEY_PEM)?
        .build()?;
        
    let mut server = Server::builder()
        .with_tls(tls)?
        .with_io("127.0.0.1:4433")?
        .start()?;

echoを実行し、pcapとkeylogファイルを取得する

ターミナルを分けて、パケットをキャプチャするためのtshark、echo サーバー、echo クライアントをそれぞれ実行します

まずtshark を実行して、test.pcapというファイルにpcapを保存します。tsharkは、echo クライアント、echo サーバーの実行を終えたら終了します。

tshark -i lo -w test.pcap

次にecho serverを実行します。 keylog.rs を見ると、環境変数 SSLKEYLOGFILE を設定していると、keylogファイルを出力しそうなことが分かります。

なので、実行時に環境変数を設定して実行するようにします。

$ SSLKEYLOGFILE=./keylog.txt ./target/release/quic_echo_server

最後にecho clientを実行します。今回は、「abcdefg」、「hijklmn」と2つのメッセージを送っています。

$ ./target/release/quic_echo_client 
abcdefg
abcdefg
hijklmn
hijklmn
^C

echo server を終了させて、keylogファイルが生成されていることを確認します。

$ cat keylog.txt 
SERVER_HANDSHAKE_TRAFFIC_SECRET XXXX...
CLIENT_HANDSHAKE_TRAFFIC_SECRET XXXX....
SERVER_TRAFFIC_SECRET_0 XXXX....
CLIENT_TRAFFIC_SECRET_0 XXXX....

pcapファイル、keylogファイル、tsharkのパスを指定して、pcap2qlogを実行します。

$ node out/main.js  --input=./test.pcap --secrets=../s2n-quic/examples/echo/keylog.txt --tshark=/usr/bin/tshark --output=.
/path_to_dir/pcap2qlog/cache/4168580c0e24469893c5a5b5f21553b58e4d09ef.qlog

生成されたqlogファイルを見ると、正しく生成されていることが分かります。

{
    "qlog_version": "draft-02-wip",
    "description": "",
    "traces": [
        {
            "title": "Connection 1",
            "vantage_point": {
                "name": "pcap",
                "type": "network",
                "flow": "client"
            },
            "configuration": {
                "time_offset": "0",
                "time_units": "ms",
                "original_uris": [
                    "./test.pcap"
                ]
            },
            "common_fields": {
                "group_id": "7175fb807a7e1d4e",
                "protocol_type": "QUIC",
                "reference_time": "1647345682.7767463"
            },
            "event_fields": [
                "relative_time",
                "category",
                "event",
                "data"
            ],
(以下略)

長いので、全文は、gist に置いておきました。

qvisで可視化すると、ちゃんと可視化できていることが分かります。

f:id:neko--suki:20220315212227p:plain
qvisで可視化した結果

まとめ

s2n-quicでpcap2qlogを使って、qlogファイルを生成してみました。

pcapから生成できるのはすごいですね。。。

ツイッターで、s2n_quic::provider::event::Subscriber を使うという方法を紹介いただいたので、それを使ってqlogを生成する方法も試してみたいと思います。