简体   繁体   English

具有NAT64网络的IPv6套接字

[英]IPv6 Sockets with NAT64 network

I'm trying to implement IPv6 socket connections for UE3 using BSD Sockets. 我正在尝试使用BSD套接字为UE3实现IPv6套接字连接。 I've successfully ported the code from UE4 that uses all sockaddr_in6 and other relative to IPv6 structs and variables. 我已经从UE4成功移植了代码,该代码使用了所有sockaddr_in6以及其他相对于IPv6的结构和变量。

The connection works fine when connected to a regular network or 4G, but when connected to a NAT64 network (Required from Apple Approvals) through a Mac sharing feature, Connect() always returns ENETUNREACH . 当连接到常规网络或4G时,连接工作正常,但是通过Mac共享功能连接到NAT64网络(Apple Approvals要求)时,Connect()始终返回ENETUNREACH I've tested it with an iPad4 and iPhone6S with many code modifications (removing setIPv6Only function, looking with AF_UNSPEC , etc) not having luck with any of them. 我已经在iPad4和iPhone6S上进行了测试,该代码进行了许多代码修改(删除setIPv6Only函数,使用AF_UNSPEC查找等),但其中的任何一个都不算运气。

Debugging some data found that getaddrinfo() is returning an IPv4 from the DNS, even thought I've set up the AAAA config in my DNS table, pointing to a Amazon AWS server that allows IPv6 (I've added this last part recently as I previously thought that an IPv6 should be also necessary from Server Side) 调试一些数据后发现getaddrinfo()正在从DNS返回IPv4,甚至认为我已经在DNS表中设置了AAAA配置,指向了允许IPv6的Amazon AWS服务器(我最近在最后一部分添加为我以前认为从服务器端也应该有一个IPv6)

Here is the DNS look up: 这是DNS查找:

FScopeLock ScopeLock(&HostByNameSynch);
addrinfo* AddrInfo = NULL;

// We are only interested in IPv6 addresses.
addrinfo HintAddrInfo;
appMemzero(&HintAddrInfo, sizeof(HintAddrInfo));

/* We only care about IPV6 results */
HintAddrInfo.ai_family = AF_INET6;//AF_UNSPEC;
HintAddrInfo.ai_socktype = SOCK_STREAM;
HintAddrInfo.ai_flags = AI_DEFAULT;

INT ErrorCode = SE_HOST_NOT_FOUND;
ErrorCode = getaddrinfo(HostName, NULL, &HintAddrInfo, &AddrInfo);
//ESocketErrors SocketError = TranslateGAIErrorCode(ErrorCode);
if (ErrorCode == SE_NO_ERROR)
{
    for (; AddrInfo != nullptr; AddrInfo = AddrInfo->ai_next)
    {
        if (AddrInfo->ai_family == AF_INET6)
        {
            /*sockaddr_in6* IPv6SockAddr = reinterpret_cast<sockaddr_in6*>(AddrInfo->ai_addr);
            if (IPv6SockAddr != nullptr)
            {
                static_cast<FInternetAddrBSDIPv6&>(OutAddr).SetIp(IPv6SockAddr->sin6_addr);
                return SE_NO_ERROR;
            }*/

            struct sockaddr_in6 input_socket6;
            memset (&input_socket6, 0, sizeof(struct sockaddr_in6));
            memcpy (&input_socket6, AddrInfo->ai_addr, AddrInfo->ai_addrlen);

            Addr.SetIp(input_socket6.sin6_addr);
            ErrorCode = 0;  // good to go.
            break;
        }
    }
}

freeaddrinfo(AddrInfo);

return ErrorCode;

Here is the Connect part: 这是连接部分:

UBOOL FSocketBSD::Connect(const FInternetAddrBSDIPv6& Addr)
{
INT Err = 0;

Err = connect(Socket, (sockaddr*)(FInternetAddrBSDIPv6&)Addr, sizeof(sockaddr_in6));//connect(Socket,Addr,sizeof(struct sockaddr_in));

debugf(TEXT("TCP Connect to with V6 %s"), *Addr.ToString(TRUE) );

if (Err == 0)
{
    return TRUE;
}
else 
{
    debugf(TEXT("TCP ERROR Connect with result: %s"), GSocketSubsystem->GetSocketError(Err) );
}

Err = GSocketSubsystem->GetLastErrorCode();
INT Return = FALSE;
switch (Err)
{
    case 0:
    case EAGAIN:
    case EINPROGRESS:
    case EINTR:
        Return = TRUE;
        break;
}
return Return;
}

Here is the socket creation: 这是套接字的创建:

SOCKET Socket = INVALID_SOCKET;
FSocketBSD* NewSocket = NULL;

// Creates a stream (TCP) socket
Socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
NewSocket = (Socket != INVALID_SOCKET) ? new FSocketBSD(Socket,SOCKTYPE_Streaming,SocketDescription) : NULL;

if (!NewSocket)
{
    debugf(NAME_Init, TEXT("Failed to create IPv6 socket %s [%s]"), *SocketDescription);
}
else 
{
    NewSocket->SetIPv6Only(false);

    // disable the SIGPIPE exception 
    int bAllow = 1;
    setsockopt(Socket, SOL_SOCKET, SO_NOSIGPIPE, &bAllow, sizeof(bAllow)); 
}

return NewSocket;


UBOOL FSocketBSD::SetIPv6Only(UBOOL bIPv6Only)
{
    INT v6only = bIPv6Only ? 1 : 0;
    UBOOL bOk = setsockopt(Socket,IPPROTO_IPV6,IPV6_V6ONLY,(char*) &v6only,sizeof( v6only )) == 0;

    if(bOk == false)
    {
        //check(SocketSubsystem);
        debugf(NAME_Init, TEXT("Failed to set sock opt for socket (%s)"), GSocketSubsystem->GetSocketError());
    }

    return bOk;
}

Here is some debugging info: 这是一些调试信息:

[0009.49] Log: TCP SetIp TCHAR u101si.info
[0009.49] Log: TCP GetHostByNameFromCache with FInternetAddrBSDIPv6
[0009.49] Log: TCP GetHostByName starting async task
[0009.49] Log: TCP Performing DNS lookup for u101si.info
[0009.66] Log: TCP SetIp in6_addr
[0009.66] Log: TCP AddHostNameToCache with FInternetAddrBSDIPv6
[0009.66] Log: TCP Tick DNS lookup
[0009.66] Log: TCP AInternetLink Resolve success
[0009.66] ScriptLog: [TcpLinkClient] resolved to 54.169.208.63:4646
[0009.66] Log: Host addr is WIFI: 169.254.23.45
[0009.66] Log: TCP BindPort with NewSocket at 0.0.0.0
[0009.66] Log: TCP SetIp FInternetIpAddr for 0.0.0.0
[0009.66] Log: TCP SetIp in_addr
[0009.66] Log: TCP Using IPv4 address: 0.0.0.0  on an ipv6 socket
[0009.66] Log: TCP Bind for 0.0.0.0:0 result: 1
[0009.66] ScriptLog: [TcpLinkClient] Bound to port: 60885
[0009.66] Log: TCP Open with 54.169.208.63
[0009.66] Log: TCP SetIp with INT value: 917098559
[0009.66] Log: TCP SetIp in_addr
[0009.66] Log: TCP Using IPv4 address: 54.169.208.63  on an ipv6 socket
[0009.66] Log: TCP Connect to with V6 [::ffff:54.169.208.63]:4646
[0009.66] Log: TCP ERROR Connect with result: SE_ENETUNREACH

I have never worked with IPv6 protocols nor low level socket connections, so I'm at a lost here. 我从未使用过IPv6协议,也从未使用过低级别的套接字连接,所以我在这里迷路了。 Is this caused to client side issues? 这是引起客户端问题的原因吗? DNS lookup problems? DNS查找问题? Server not accepting IPv6 clients? 服务器不接受IPv6客户端?

Thanks in advance! 提前致谢!

After many test in the device, I made it work. 在设备中进行了许多测试之后,我使其正常运行。

The issue came the cache for DNS, that was returning the outdated IP and from the call to getaddrinfo(). 问题出在DNS的缓存中,该缓存返回了过时的IP,并从对getaddrinfo()的调用中返回了缓存。 When addding the port parameter, it triggered the right register in the DNS: A for IPv4 and AAAA for IPv6. 添加端口参数时,它触发了DNS中的正确注册:对于IPv4是A,对于IPv6是AAAA。

This is the call that works for IPv4 and IPv6: 这是适用于IPv4和IPv6的调用:

FInternetAddrBSDIPv6& Addr;
struct addrinfo *res, *res0;

// We are only interested in IPv6 addresses.
addrinfo HintAddrInfo;
appMemzero(&HintAddrInfo, sizeof(HintAddrInfo));

HintAddrInfo.ai_family = AF_UNSPEC;
HintAddrInfo.ai_socktype = SOCK_STREAM;
//HintAddrInfo.ai_protocol = IPPROTO_TCP;
HintAddrInfo.ai_flags = AI_DEFAULT;

INT ErrorCode = SE_HOST_NOT_FOUND;
ErrorCode = getaddrinfo(HostName, "1212", &HintAddrInfo, &res0);

if (ErrorCode == SE_NO_ERROR)
{
    for (res = res0; res; res = res->ai_next) 
    {
        debugf(TEXT("TCP GetHostByName with DNS successful"));

        if (res->ai_addr != 0)
        {
            if( res->ai_family == AF_INET6 )
            {
                struct sockaddr_in6 input_socket6;
                memset (&input_socket6, 0, sizeof(struct sockaddr_in6));
                memcpy (&input_socket6, res->ai_addr, res->ai_addrlen);

                Addr.SetIp(input_socket6.sin6_addr);
                ErrorCode = 0;  // good to go.
                break;
            }
            else if( res->ai_family == AF_INET )
            {
                const in_addr &IP = ((sockaddr_in *) res->ai_addr)->sin_addr;
                Addr.SetIp(IP);
                ErrorCode = 0;  // good to go.
                break;
            }
        }

    }
}

freeaddrinfo(res0);

return ErrorCode;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM