DTLS 握手在 ipv6 上失败

DTLS handshake fail on ipv6

我有一个dtls的演示代码,它在ipv4上运行良好。但是在我为 ipv6 修改它之后,它在握手阶段失败了。 服务器代码如下:

SSL_load_error_strings();
SSL_library_init();
SSL_CTX *ctx = SSL_CTX_new(DTLSv1_server_method());
if(SSL_CTX_use_certificate_chain_file(ctx, "path/to/crt") !=1)
    ERR_print_errors_fp(stderr);

if(SSL_CTX_use_PrivateKey_file(ctx, "path/to/key", SSL_FILETYPE_PEM) != 1)
    ERR_print_errors_fp(stderr);

SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cert);
SSL_CTX_set_read_ahead(ctx, 1);
SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie);
SSL_CTX_set_cipher_list(ctx, "ALL:NULL:eNULL:aNULL");

int fd = socket(AF_INET6, SOCK_DGRAM, 0);
struct sockaddr_in6 server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin6_family = AF_INET6;
server_addr.sin6_port = htons(MYPORT);
server_addr.sin6_addr = in6addr_any;
int flag = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0)
    perror("server reuse addr");

#ifdef SO_REUSEPORT
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)) < 0)
    perror("server reuse port");
#endif
if(bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
    perror("bind server fd");

BIO *bio = BIO_new_dgram(fd, BIO_NOCLOSE);
SSL *ssl = SSL_new(ctx);
SSL_set_bio(ssl, bio, bio);

/* Enable cookie exchange */
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);


fprintf(stderr, "Wait for incoming connections\n");
struct sockaddr_in6 client_addr;
while(1){
    int ret = DTLSv1_listen(ssl, &client_addr);
    if(ret < 0){
        ERR_print_errors_fp(stderr);
    }
    if(ret > 0)
        break;
}
fprintf(stderr, "Handle client connection\n");
int client_fd = socket(client_addr.sin6_family, SOCK_DGRAM, 0);
if (setsockopt(client_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0)
    perror("reuse addr");

#ifdef SO_REUSEPORT
if (setsockopt(client_fd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)) < 0) 
    perror("reuse port");
#endif
if(bind(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
    perror("bind client fd");
if(connect(client_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)))
    perror("connect client");
BIO *cbio = SSL_get_rbio(ssl);
BIO_set_fd(cbio, client_fd, BIO_NOCLOSE);
BIO_ctrl(cbio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &client_addr);
fprintf(stderr, "waiting SSL_accept\n");
if(SSL_accept(ssl)!=1){
    ERR_print_errors_fp(stderr);
}
fprintf(stderr, "SSL_accept completed\n");

客户端代码为:

SSL_load_error_strings();
SSL_library_init();
SSL_CTX *ctx = SSL_CTX_new(DTLSv1_client_method());
SSL_CTX_set_read_ahead(ctx, 1);

union {
    struct sockaddr_storage ss;
    struct sockaddr_in6 s6;
    struct sockaddr_in s4;
} server_addr;
int fd;
memset(&server_addr, 0, sizeof(server_addr));
if(inet_pton(AF_INET, argv[1], &server_addr.s4.sin_addr) == 1){
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    server_addr.s4.sin_family = AF_INET;
    server_addr.s4.sin_port = htons(MYPORT);
}else if(inet_pton(AF_INET6, argv[1], &server_addr.s6.sin6_addr) ==1){
    fd = socket(AF_INET6, SOCK_DGRAM, 0);
    server_addr.s6.sin6_family = AF_INET6;
    server_addr.s6.sin6_port = htons(MYPORT);
}else{
    fprintf(stderr, "Wrong ip format\n");
    return 1;
}
if(connect(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)))
    perror("connect");
BIO *bio = BIO_new_dgram(fd, BIO_NOCLOSE);
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &server_addr);
SSL *ssl = SSL_new(ctx);
SSL_set_bio(ssl, bio, bio);
fprintf(stderr, "waiting SSL_connect\n");
if(SSL_connect(ssl)!=1)
    ERR_print_errors_fp(stderr);
fprintf(stderr, "SSL connected\n");

SSL_accept和SSL_connect好像没有return。 我通过仅将 sockaddr_in 更改为 sockaddr_in6 来修改代码。 openssl 版本为:linux

上的 1.0.2h

我还用 wireshark 捕获了包裹:

有人可以告诉我代码有什么问题吗?

我加

BIO_ctrl_set_connected(bio, 0, &client_addr);

在服务器端 DTLSv1_listen 之后解决了这个问题。