具有 NAT64 网络的 IPv6 套接字
IPv6 Sockets with NAT64 network
我正在尝试使用 BSD 套接字为 UE3 实现 IPv6 套接字连接。我已经成功地从 UE4 移植了使用所有 sockaddr_in6 和其他与 IPv6 结构和变量相关的代码。
当连接到常规网络或 4G 时,连接工作正常,但当通过 Mac 共享功能连接到 NAT64 网络(需要 Apple 批准)时,Connect() 总是 returns ENETUNREACH
。我已经用 iPad4 和 iPhone6S 对它进行了很多代码修改(删除 setIPv6Only 函数,使用 AF_UNSPEC
等)进行了测试,但都没有成功。
调试一些数据发现 getaddrinfo()
正在从 DNS 返回 IPv4,甚至认为我已经在我的 DNS table 中设置了 AAAA 配置,指向亚马逊 AWS 服务器允许IPv6(我最近添加了最后一部分,因为我之前认为服务器端也需要 IPv6)
这是 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;
这是连接部分:
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;
}
这里是套接字创建:
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;
}
这是一些调试信息:
[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
我从未使用过 IPv6 协议或低级套接字连接,所以我在这里迷路了。这是由客户端问题引起的吗? DNS 查找问题?服务器不接受 IPv6 客户端?
提前致谢!
在设备上多次测试后,我成功了。
问题出在 DNS 的缓存上,它返回了过时的 IP 以及对 getaddrinfo() 的调用。添加端口参数时,它触发了 DNS 中的正确注册:IPv4 为 A,IPv6 为 AAAA。
这是适用于 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;
我正在尝试使用 BSD 套接字为 UE3 实现 IPv6 套接字连接。我已经成功地从 UE4 移植了使用所有 sockaddr_in6 和其他与 IPv6 结构和变量相关的代码。
当连接到常规网络或 4G 时,连接工作正常,但当通过 Mac 共享功能连接到 NAT64 网络(需要 Apple 批准)时,Connect() 总是 returns ENETUNREACH
。我已经用 iPad4 和 iPhone6S 对它进行了很多代码修改(删除 setIPv6Only 函数,使用 AF_UNSPEC
等)进行了测试,但都没有成功。
调试一些数据发现 getaddrinfo()
正在从 DNS 返回 IPv4,甚至认为我已经在我的 DNS table 中设置了 AAAA 配置,指向亚马逊 AWS 服务器允许IPv6(我最近添加了最后一部分,因为我之前认为服务器端也需要 IPv6)
这是 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;
这是连接部分:
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;
}
这里是套接字创建:
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;
}
这是一些调试信息:
[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
我从未使用过 IPv6 协议或低级套接字连接,所以我在这里迷路了。这是由客户端问题引起的吗? DNS 查找问题?服务器不接受 IPv6 客户端?
提前致谢!
在设备上多次测试后,我成功了。
问题出在 DNS 的缓存上,它返回了过时的 IP 以及对 getaddrinfo() 的调用。添加端口参数时,它触发了 DNS 中的正确注册:IPv4 为 A,IPv6 为 AAAA。
这是适用于 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;