зеркало из https://github.com/microsoft/msquic.git
Document troubleshooting of packets using wireshark (#4391)
This commit is contained in:
Родитель
b9c03fcac3
Коммит
45f9bb0b16
|
@ -302,6 +302,50 @@ On the latest version of Windows, these counters are also exposed via PerfMon.ex
|
|||
|
||||
Counters are also captured at the beginning of MsQuic ETW traces, and unlike PerfMon, includes all MsQuic instances running on the system, both user and kernel mode.
|
||||
|
||||
# Network Troubleshooting
|
||||
|
||||
To see what is being transmited on the wire you might use an open-source tool like [Wireshark](https://www.wireshark.org). The packets captured by such tool will be encrypted due to TLS, therefore we must provide the secrets to enable Wireshark to decrypt the packets.
|
||||
|
||||
To enable this we must generate a [SSLKEYLOGFILE](https://www.ietf.org/archive/id/draft-thomson-tls-keylogfile-00.html#name-introduction-10) with information about the secrets used in the TLS connection. With such file we will be able to decrypt the packets.
|
||||
|
||||
For some browsers all you have to do is to set an environment variable `SSLKEYLOGFILE` with the absolute path of the log file to be generated and then you can load it into Wireshark for troubleshooting. For MsQuic applications we need to generate such file. A good practice is to check if the `SSLKEYLOGFILE` env variable is set and if so you write the file. The steps are:
|
||||
|
||||
1. Set the `QUIC_PARAM_CONN_TLS_SECRETS` connection param with a struct to be populated with the TLS secrets by MsQuic.
|
||||
```c
|
||||
// Define empty struct for the TLS Secrets
|
||||
QUIC_TLS_SECRETS ClientSecrets{};
|
||||
...
|
||||
|
||||
// Get the value of the env variable to log the secrets
|
||||
const char* SslKeyLogFile = getenv("SSLKEYLOGFILE");
|
||||
...
|
||||
|
||||
// If the variable is set then we have a file to write the TLS secrets thus we
|
||||
// pass the struct to be filled
|
||||
if (SslKeyLogFile != NULL) {
|
||||
MsQuic->SetParam(Connection, QUIC_PARAM_CONN_TLS_SECRETS, sizeof(ClientSecrets), &ClientSecrets);
|
||||
// Check for errors...
|
||||
}
|
||||
```
|
||||
2. Write the file when the connection succeeds (event `QUIC_CONNECTION_EVENT_CONNECTED`).
|
||||
```c
|
||||
// On your connection callback function
|
||||
...
|
||||
if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED) {
|
||||
if (SslKeyLogFile != NULL) {
|
||||
WriteSslKeyLogFile(SslKeyLogFile, ClientSecrets);
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Write the `WriteSslKeyLogFile` function. You can just copy the function from [src/inc/msquichelper.h#WriteSslKeyLogFile](https://github.com/microsoft/msquic/blob/main/src/inc/msquichelper.h#L564) if it serves your needs or write your own.
|
||||
4. Set the `SSLKEYLOGFILE` env variable set to the path of the log file and run the program. Then check the file with the secets.
|
||||
|
||||
5. Load the key log into Wireshark and start capturing to decrypt the packets. To learn how to load such file inside Wireshark refer to this documentation: [Using the (Pre)-Master-Secret](https://wiki.wireshark.org/TLS#using-the-pre-master-secret).
|
||||
|
||||
Using a Wireshark version that supports QUIC is not mandatory but could help when troubleshooting. To know which version supports QUIC refer to https://github.com/quicwg/base-drafts/wiki/Tools#wireshark.
|
||||
|
||||
If you need a working example on how to generate the key log file please refer to the Sample at src/tools/sample/sample.c.
|
||||
|
||||
# Detailed Troubleshooting
|
||||
|
||||
For detailed trouble shooting steps please see the MsQuic [Trouble Shooting Guide](TSG.md).
|
||||
|
|
|
@ -33,6 +33,10 @@ Start the client providing the IP address for the server. Here 127.0.0.1 is used
|
|||
quicsample.exe -client -unsecure -target:{127.0.0.1}
|
||||
```
|
||||
|
||||
## Troubleshooting with Wireshark
|
||||
|
||||
When running the client you can set the environment variable `SSLKEYLOGFILE` to the absolute path of the file that will receive the TLS secrets. Learn how to load such file inside Wireshark and see what is being transmitted on the wire: [Using the (Pre)-Master-Secret](https://wiki.wireshark.org/TLS#using-the-pre-master-secret)
|
||||
|
||||
## Console Output
|
||||
|
||||
Here is what the console output looks like on the server and client sides after connection is established and data flows:
|
||||
|
|
|
@ -43,6 +43,7 @@ Abstract:
|
|||
// warning here. This is not an MsQuic bug but a Windows SDK bug.
|
||||
//
|
||||
#pragma warning(disable:5105)
|
||||
#include <share.h>
|
||||
#endif
|
||||
#include "msquic.h"
|
||||
#include <stdio.h>
|
||||
|
@ -99,6 +100,19 @@ HQUIC Registration;
|
|||
//
|
||||
HQUIC Configuration;
|
||||
|
||||
|
||||
//
|
||||
// The struct to be filled with TLS secrets
|
||||
// for debugging packet captured with e.g. Wireshark.
|
||||
//
|
||||
QUIC_TLS_SECRETS ClientSecrets = {0};
|
||||
|
||||
//
|
||||
// The name of the environment variable being
|
||||
// used to get the path to the ssl key log file.
|
||||
//
|
||||
const char* SslKeyLogEnvVar = "SSLKEYLOGFILE";
|
||||
|
||||
void PrintUsage()
|
||||
{
|
||||
printf(
|
||||
|
@ -190,6 +204,116 @@ DecodeHexBuffer(
|
|||
return HexBufferLen;
|
||||
}
|
||||
|
||||
void
|
||||
EncodeHexBuffer(
|
||||
_In_reads_(BufferLen) uint8_t* Buffer,
|
||||
_In_ uint8_t BufferLen,
|
||||
_Out_writes_bytes_(2*BufferLen) char* HexString
|
||||
)
|
||||
{
|
||||
#define HEX_TO_CHAR(x) ((x) > 9 ? ('a' + ((x) - 10)) : '0' + (x))
|
||||
for (uint8_t i = 0; i < BufferLen; i++) {
|
||||
HexString[i*2] = HEX_TO_CHAR(Buffer[i] >> 4);
|
||||
HexString[i*2 + 1] = HEX_TO_CHAR(Buffer[i] & 0xf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WriteSslKeyLogFile(
|
||||
_In_z_ const char* FileName,
|
||||
_In_ QUIC_TLS_SECRETS* TlsSecrets
|
||||
)
|
||||
{
|
||||
printf("Writing SSLKEYLOGFILE at %s\n", FileName);
|
||||
FILE* File = NULL;
|
||||
#ifdef _WIN32
|
||||
File = _fsopen(FileName, "ab", _SH_DENYNO);
|
||||
#else
|
||||
File = fopen(FileName, "ab");
|
||||
#endif
|
||||
|
||||
if (File == NULL) {
|
||||
printf("Failed to open sslkeylogfile %s\n", FileName);
|
||||
return;
|
||||
}
|
||||
if (fseek(File, 0, SEEK_END) == 0 && ftell(File) == 0) {
|
||||
fprintf(File, "# TLS 1.3 secrets log file, generated by msquic\n");
|
||||
}
|
||||
|
||||
char ClientRandomBuffer[(2 * sizeof(((QUIC_TLS_SECRETS*)0)->ClientRandom)) + 1] = {0};
|
||||
|
||||
char TempHexBuffer[(2 * QUIC_TLS_SECRETS_MAX_SECRET_LEN) + 1] = {0};
|
||||
if (TlsSecrets->IsSet.ClientRandom) {
|
||||
EncodeHexBuffer(
|
||||
TlsSecrets->ClientRandom,
|
||||
(uint8_t)sizeof(TlsSecrets->ClientRandom),
|
||||
ClientRandomBuffer);
|
||||
}
|
||||
|
||||
if (TlsSecrets->IsSet.ClientEarlyTrafficSecret) {
|
||||
EncodeHexBuffer(
|
||||
TlsSecrets->ClientEarlyTrafficSecret,
|
||||
TlsSecrets->SecretLength,
|
||||
TempHexBuffer);
|
||||
fprintf(
|
||||
File,
|
||||
"CLIENT_EARLY_TRAFFIC_SECRET %s %s\n",
|
||||
ClientRandomBuffer,
|
||||
TempHexBuffer);
|
||||
}
|
||||
|
||||
if (TlsSecrets->IsSet.ClientHandshakeTrafficSecret) {
|
||||
EncodeHexBuffer(
|
||||
TlsSecrets->ClientHandshakeTrafficSecret,
|
||||
TlsSecrets->SecretLength,
|
||||
TempHexBuffer);
|
||||
fprintf(
|
||||
File,
|
||||
"CLIENT_HANDSHAKE_TRAFFIC_SECRET %s %s\n",
|
||||
ClientRandomBuffer,
|
||||
TempHexBuffer);
|
||||
}
|
||||
|
||||
if (TlsSecrets->IsSet.ServerHandshakeTrafficSecret) {
|
||||
EncodeHexBuffer(
|
||||
TlsSecrets->ServerHandshakeTrafficSecret,
|
||||
TlsSecrets->SecretLength,
|
||||
TempHexBuffer);
|
||||
fprintf(
|
||||
File,
|
||||
"SERVER_HANDSHAKE_TRAFFIC_SECRET %s %s\n",
|
||||
ClientRandomBuffer,
|
||||
TempHexBuffer);
|
||||
}
|
||||
|
||||
if (TlsSecrets->IsSet.ClientTrafficSecret0) {
|
||||
EncodeHexBuffer(
|
||||
TlsSecrets->ClientTrafficSecret0,
|
||||
TlsSecrets->SecretLength,
|
||||
TempHexBuffer);
|
||||
fprintf(
|
||||
File,
|
||||
"CLIENT_TRAFFIC_SECRET_0 %s %s\n",
|
||||
ClientRandomBuffer,
|
||||
TempHexBuffer);
|
||||
}
|
||||
|
||||
if (TlsSecrets->IsSet.ServerTrafficSecret0) {
|
||||
EncodeHexBuffer(
|
||||
TlsSecrets->ServerTrafficSecret0,
|
||||
TlsSecrets->SecretLength,
|
||||
TempHexBuffer);
|
||||
fprintf(
|
||||
File,
|
||||
"SERVER_TRAFFIC_SECRET_0 %s %s\n",
|
||||
ClientRandomBuffer,
|
||||
TempHexBuffer);
|
||||
}
|
||||
|
||||
fflush(File);
|
||||
fclose(File);
|
||||
}
|
||||
|
||||
//
|
||||
// Allocates and sends some data over a QUIC stream.
|
||||
//
|
||||
|
@ -682,6 +806,14 @@ ClientConnectionCallback(
|
|||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Context);
|
||||
|
||||
if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED) {
|
||||
const char* SslKeyLogFile = getenv(SslKeyLogEnvVar);
|
||||
if (SslKeyLogFile != NULL) {
|
||||
WriteSslKeyLogFile(SslKeyLogFile, &ClientSecrets);
|
||||
}
|
||||
}
|
||||
|
||||
switch (Event->Type) {
|
||||
case QUIC_CONNECTION_EVENT_CONNECTED:
|
||||
//
|
||||
|
@ -802,6 +934,7 @@ RunClient(
|
|||
|
||||
QUIC_STATUS Status;
|
||||
const char* ResumptionTicketString = NULL;
|
||||
const char* SslKeyLogFile = getenv(SslKeyLogEnvVar);
|
||||
HQUIC Connection = NULL;
|
||||
|
||||
//
|
||||
|
@ -825,6 +958,13 @@ RunClient(
|
|||
}
|
||||
}
|
||||
|
||||
if (SslKeyLogFile != NULL) {
|
||||
if (QUIC_FAILED(Status = MsQuic->SetParam(Connection, QUIC_PARAM_CONN_TLS_SECRETS, sizeof(ClientSecrets), &ClientSecrets))) {
|
||||
printf("SetParam(QUIC_PARAM_CONN_TLS_SECRETS) failed, 0x%x!\n", Status);
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Get the target / server name or IP from the command line.
|
||||
//
|
||||
|
|
Загрузка…
Ссылка в новой задаче