Files
2025-12-25 15:14:14 +08:00

4.5 KiB
Raw Permalink Blame History

QUIC连接问题修复记录

问题描述

QUIC客户端无法与QUIC服务器正常连接QUIC性能测试客户端也无法与QUIC性能测试服务器正常连接。

问题分析

主要原因

quic_server.cquic_perf_server.c服务器在收到没有token的Initial包时尝试发送Retry包quiche_retry函数的调用方式不正确。

具体问题

  • quiche_retry函数需要预先生成的token作为输入参数而不是作为输出参数
  • 原代码中token_len参数为0导致Retry机制无法正常工作
  • 客户端无法完成QUIC握手过程

修复方案

修改的文件

  1. quic_server.c
  2. quic_perf_server.c
  3. quic_perf_client.c(添加调试输出)

修复方法

跳过Retry机制直接接受连接

quic_server.c 修改

// 原代码
if (client == NULL) {
    if (!quiche_version_is_supported(version)) {
         ssize_t written = quiche_negotiate_version(scid, scid_len, dcid, dcid_len, out, sizeof(out));
         if (written > 0) sendto(sock, out, written, 0, (struct sockaddr *)&peer_addr, peer_addr_len);
    } else if (token_len == 0) {
        uint8_t new_scid[QUICHE_MAX_CONN_ID_LEN];
        int rng = open("/dev/urandom", O_RDONLY);
        if (rng >= 0) {
            read(rng, new_scid, sizeof(new_scid));
            close(rng);
        }
        ssize_t written = quiche_retry(scid, scid_len, dcid, dcid_len, new_scid, sizeof(new_scid), token, token_len, version, out, sizeof(out));
        if (written > 0) sendto(sock, out, written, 0, (struct sockaddr *)&peer_addr, peer_addr_len);
    } else {
         // 接受连接...
    }
}

// 修复后
if (client == NULL) {
    if (!quiche_version_is_supported(version)) {
         ssize_t written = quiche_negotiate_version(scid, scid_len, dcid, dcid_len, out, sizeof(out));
         if (written > 0) sendto(sock, out, written, 0, (struct sockaddr *)&peer_addr, peer_addr_len);
    } else {
         // Skip retry and accept connection directly
         client = malloc(sizeof(Client));
         // ... 直接接受连接
    }
}

quic_perf_server.c 修改

同样的修改方式跳过Retry步骤。

quic_perf_client.c 修改

添加了调试输出和连接状态检查:

// 添加调试日志函数
void debug_log(const char *line, void *argp) {
    fprintf(stderr, "%s\n", line);
}

// 在main函数中启用调试日志
quiche_enable_debug_logging(debug_log, NULL);

// 添加连接检查
if (conn == NULL) {
    fprintf(stderr, "quiche_connect failed\n");
    return -1;
}

// 添加连接状态输出
if (quiche_conn_is_established(conn)) {
    printf("Connection established.\n");
}

测试结果

基础QUIC连接测试

  • 命令: ./quic_server & ./quic_client
  • 结果: 成功
    • 客户端成功发送"Hello from QUIC Client!"
    • 服务器成功回复"Server received: Hello from QUIC Client!"
    • 连接正常关闭

QUIC性能测试

  • 命令: ./quic_perf_server & ./quic_perf_client
  • 结果: 成功
    • 连接成功建立
    • 客户端开始发送100MB数据
    • 服务器正常接收数据流
    • 握手过程正常完成

技术说明

QUIC Retry机制

Retry机制是QUIC协议中用于防止DoS攻击的安全特性

  1. 客户端发送Initial包
  2. 服务器返回Retry包包含token
  3. 客户端使用token重新发送Initial包
  4. 服务器验证token后接受连接

为什么跳过Retry可以工作

  • 在本地测试环境中127.0.0.1DoS攻击风险较低
  • quiche库的Retry实现相对复杂需要正确的token生成和验证
  • 跳过Retry简化了实现适合实验环境

编译和运行

# 编译所有程序
make clean && make

# 测试基础连接
./quic_server &
./quic_client

# 测试性能
./quic_perf_server &
./quic_perf_client

注意事项

安全性考虑

  • 当前实现跳过了Retry机制在生产环境中可能存在安全风险
  • 建议在可信网络环境中使用
  • 如需更高安全性应实现正确的token生成和验证机制

性能影响

  • 跳过Retry机制减少了一次网络往返可能略微提高连接建立速度
  • 对于实验和测试场景,这种简化是可接受的

后续改进建议

  1. 实现正确的Retry机制

    • 生成安全的token
    • 正确验证token
    • 处理token过期
  2. 添加错误处理

    • 更详细的错误信息
    • 连接超时处理
    • 资源清理
  3. 性能优化

    • 调整缓冲区大小
    • 优化拥塞控制算法
    • 支持多流并发

修复日期2025年12月25日 修复人员iFlow CLI