iOS Socket cannot connect ipv6 address when use PacketTunnelProvider

When I try to use socket to connect to an ipv6 address created by PacketTunnelProvider in my iOS device, an error occurs. Here is the code to create socket server and client:

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int dx_create_ipv6_server(const char *ipv6_address, int port) {
    int server_fd;
    struct sockaddr_in6 server_addr;

    server_fd = socket(AF_INET6, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket() failed");
        return -1;
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin6_family = AF_INET6;
    server_addr.sin6_port = htons(port);

    if (inet_pton(AF_INET6, ipv6_address, &server_addr.sin6_addr) <= 0) {
        perror("inet_pton() failed");
        close(server_fd);
        return -1;
    }

    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind() failed");
        close(server_fd);
        return -1;
    }

    if (listen(server_fd, 5) == -1) {
        perror("listen() failed");
        close(server_fd);
        return -1;
    }

    printf("Server is listening on [%s]:%d\n", ipv6_address, port);
    return server_fd;
}

int dx_accept_client_connection(int server_fd) {
    int client_fd;
    struct sockaddr_in6 client_addr;
    socklen_t client_addr_len = sizeof(client_addr);

    client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_fd == -1) {
        perror("accept() failed");
        return -1;
    }

    char client_ip[INET6_ADDRSTRLEN];
    inet_ntop(AF_INET6, &client_addr.sin6_addr, client_ip, sizeof(client_ip));
    printf("Client connected: [%s]\n", client_ip);

    return client_fd;
}

int dx_connect_to_ipv6_server(const char *ipv6_address, int port) {
    int client_fd;
    struct sockaddr_in6 server_addr;

    client_fd = socket(AF_INET6, SOCK_STREAM, 0);
    if (client_fd == -1) {
        perror("socket() failed");
        return -1;
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin6_family = AF_INET6;
    server_addr.sin6_port = htons(port);

    if (inet_pton(AF_INET6, ipv6_address, &server_addr.sin6_addr) <= 0) {
        perror("inet_pton() failed");
        close(client_fd);
        return -1;
    }

    if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect() failed");
        close(client_fd);
        return -1;
    }

    printf("Connected to server [%s]:%d\n", ipv6_address, port);

    close(client_fd);
    return 0;
}

@implementation SocketTest

+ (void)startSever:(NSString *)addr port:(int)port {
    [[NSOperationQueue new] addOperationWithBlock:^{
        
        int server_fd = dx_create_ipv6_server(addr.UTF8String, port);
        if (server_fd == -1) {
            return;
        }

        int client_fd = dx_accept_client_connection(server_fd);
        if (client_fd == -1) {
            close(server_fd);
            return;
        }

        close(client_fd);
        close(server_fd);
    }];
}


+ (void)clientConnect:(NSString *)addr port:(int)port{
    [[NSOperationQueue new] addOperationWithBlock:^{
        dx_connect_to_ipv6_server(addr.UTF8String, port);
    }];
}


@end

PacketTunnelProvider code:

override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
        
        let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "fd84:306d:fc4e::1")
        
        let ipv6 = NEIPv6Settings(addresses: ["fd84:306d:fc4e::1"], networkPrefixLengths: 64)
        settings.ipv6Settings = ipv6
        
        
        setTunnelNetworkSettings(settings) { error in
            if error == nil {
                self.readPackets()
            }
            completionHandler(error)
        }
    }

private func readPackets() {
        // do nothing
        packetFlow.readPackets { [self] packets, protocols in
            self.packetFlow.writePackets(packets, withProtocols: protocols)
            self.readPackets()
        }
    }

At main target, in viewcontroller's viewDidAppear, after starting the VPN, executed following code:

[SocketTest startSever:@"fd84:306d:fc4e::1" port:12345];
sleep(3);
[SocketTest clientConnect:@"fd84:306d:fc4e::1" port:12345];

The startSever is executed correctly, but when executing:

connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))

in clientConnect, the code is blocked until it times out and returns -1.

Even if I use GCDAsyncSocket or BlueSocket, I get the same error.

The strange thing is that if I use the ipv4 address in PacketTunnelProvider, and change the above code to the ipv4 version and connect to ipv4 address, or use GCDAsyncSocket to perform the corresponding operation, it can be executed correctly.

I tried to search Google for problems with ios-related ipv6 addresses, but I still couldn't find a solution. Is this a bug in the ios system or is there something wrong with my code? I hope to get your help!

Answered by DTS Engineer in 813233022
iOS Socket cannot connect ipv6 address when use PacketTunnelProvider
 
 
Q