IPv6 node support

This will make bitcoin relay valid routable IPv6 addresses, and when
USE_IPV6 is enabled, listen on IPv6 interfaces and attempt connections
to IPv6 addresses.
This commit is contained in:
Pieter Wuille 2012-03-31 17:58:25 +02:00
parent 508471bbc0
commit 23aa78c405
11 changed files with 91 additions and 40 deletions

View file

@ -2,7 +2,7 @@ TEMPLATE = app
TARGET = TARGET =
VERSION = 0.6.99 VERSION = 0.6.99
INCLUDEPATH += src src/json src/qt INCLUDEPATH += src src/json src/qt
DEFINES += QT_GUI BOOST_THREAD_USE_LIB DEFINES += QT_GUI BOOST_THREAD_USE_LIB USE_IPV6
CONFIG += no_include_pwd CONFIG += no_include_pwd
# for boost 1.37, add -mt to the boost libraries # for boost 1.37, add -mt to the boost libraries

View file

@ -44,7 +44,7 @@ sudo port install qrencode
4. Now you should be able to build bitcoind: 4. Now you should be able to build bitcoind:
cd bitcoin/src cd bitcoin/src
make -f makefile.osx make -f makefile.osx USE_IPV6=1
Run: Run:
./bitcoind --help # for a list of command-line options. ./bitcoind --help # for a list of command-line options.

View file

@ -43,6 +43,9 @@ your package manager. Set USE_QRCODE to control this:
USE_QRCODE=0 (the default) No QRCode support - libarcode not required USE_QRCODE=0 (the default) No QRCode support - libarcode not required
USE_QRCODE=1 QRCode support enabled USE_QRCODE=1 QRCode support enabled
IPv6 support may be enabled by setting
USE_IPV6=1 Enable IPv6 support
Licenses of statically linked libraries: Licenses of statically linked libraries:
Berkeley DB New BSD license with additional requirement that linked Berkeley DB New BSD license with additional requirement that linked
software must be free open source software must be free open source
@ -80,7 +83,7 @@ emerge -av1 --noreplace boost glib openssl sys-libs/db:4.8
Take the following steps to build (no UPnP support): Take the following steps to build (no UPnP support):
cd ${BITCOIN_DIR}/src cd ${BITCOIN_DIR}/src
make -f makefile.unix USE_UPNP= BDB_INCLUDE_PATH='/usr/include/db4.8' make -f makefile.unix USE_UPNP= USE_IPV6=1 BDB_INCLUDE_PATH='/usr/include/db4.8'
strip bitcoind strip bitcoind

View file

@ -2424,9 +2424,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{ {
if (fShutdown) if (fShutdown)
return true; return true;
// ignore IPv6 for now, since it isn't implemented anyway
if (!addr.IsIPv4())
continue;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60; addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr); pfrom->AddAddressKnown(addr);

View file

@ -27,7 +27,7 @@ LIBS= \
-l ssl \ -l ssl \
-l crypto -l crypto
DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DUSE_IPV6
DEBUGFLAGS=-g DEBUGFLAGS=-g
CFLAGS=-O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) CFLAGS=-O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS)

View file

@ -23,7 +23,7 @@ LIBS= \
-l ssl \ -l ssl \
-l crypto -l crypto
DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DUSE_IPV6
DEBUGFLAGS=-g DEBUGFLAGS=-g
CFLAGS=-mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) CFLAGS=-mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS)

View file

@ -53,7 +53,7 @@ LIBS += \
TESTDEFS += -DBOOST_TEST_DYN_LINK TESTDEFS += -DBOOST_TEST_DYN_LINK
endif endif
DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DUSE_IPV6
ifdef RELEASE ifdef RELEASE
# Compile for maximum compatibility and smallest size. # Compile for maximum compatibility and smallest size.

View file

@ -4,7 +4,7 @@
USE_UPNP:=0 USE_UPNP:=0
DEFS= DEFS=-DUSE_IPV6
DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH))
LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH))

View file

@ -733,7 +733,11 @@ void ThreadSocketHandler2(void* parg)
// //
if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
{ {
#ifdef USE_IPV6
struct sockaddr_in6 sockaddr;
#else
struct sockaddr_in sockaddr; struct sockaddr_in sockaddr;
#endif
socklen_t len = sizeof(sockaddr); socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
CAddress addr; CAddress addr;
@ -1390,7 +1394,7 @@ void ThreadOpenConnections2(void* parg)
CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);
// if we selected an invalid address, restart // if we selected an invalid address, restart
if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
break; break;
nTries++; nTries++;
@ -1620,6 +1624,7 @@ void ThreadMessageHandler2(void* parg)
bool BindListenPort(string& strError) bool BindListenPort(string& strError)
{ {
unsigned short nPort = GetListenPort();
strError = ""; strError = "";
int nOne = 1; int nOne = 1;
@ -1636,7 +1641,12 @@ bool BindListenPort(string& strError)
#endif #endif
// Create socket for listening for incoming connections // Create socket for listening for incoming connections
hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #ifdef USE_IPV6
int nFamily = AF_INET6;
#else
int nFamily = AF_INET;
#endif
hListenSocket = socket(nFamily, SOCK_STREAM, IPPROTO_TCP);
if (hListenSocket == INVALID_SOCKET) if (hListenSocket == INVALID_SOCKET)
{ {
strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError());
@ -1669,22 +1679,36 @@ bool BindListenPort(string& strError)
// The sockaddr_in structure specifies the address family, // The sockaddr_in structure specifies the address family,
// IP address, and port for the socket that is being bound // IP address, and port for the socket that is being bound
struct sockaddr_in sockaddr; #ifdef USE_IPV6
struct sockaddr_in6 sockaddr = sockaddr_in6();
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin6_family = AF_INET6;
sockaddr.sin6_addr = in6addr_any; // bind to all IPs on this computer
sockaddr.sin6_port = htons(nPort);
# ifdef WIN32
int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */;
int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */;
// this call is allowed to fail
setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int));
# endif
#else
struct sockaddr_in sockaddr = sockaddr_in();
memset(&sockaddr, 0, sizeof(sockaddr)); memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET; sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer
sockaddr.sin_port = htons(GetListenPort()); sockaddr.sin_port = htons(nPort);
#endif
if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR)
{ {
int nErr = WSAGetLastError(); int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE) if (nErr == WSAEADDRINUSE)
strError = strprintf(_("Unable to bind to port %d on this computer. Bitcoin is probably already running."), ntohs(sockaddr.sin_port)); strError = strprintf(_("Unable to bind to port %d on this computer. Bitcoin is probably already running."), nPort);
else else
strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d, %s)", nPort, nErr, strerror(nErr));
printf("%s\n", strError.c_str()); printf("%s\n", strError.c_str());
return false; return false;
} }
printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); printf("Bound to port %d\n", (int)nPort);
// Listen for incoming connections // Listen for incoming connections
if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
@ -1727,28 +1751,22 @@ void static Discover()
if ((ifa->ifa_flags & IFF_UP) == 0) continue; if ((ifa->ifa_flags & IFF_UP) == 0) continue;
if (strcmp(ifa->ifa_name, "lo") == 0) continue; if (strcmp(ifa->ifa_name, "lo") == 0) continue;
if (strcmp(ifa->ifa_name, "lo0") == 0) continue; if (strcmp(ifa->ifa_name, "lo0") == 0) continue;
char pszIP[100];
if (ifa->ifa_addr->sa_family == AF_INET) if (ifa->ifa_addr->sa_family == AF_INET)
{ {
struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), pszIP, sizeof(pszIP)) != NULL)
printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP);
// Take the first IP that isn't loopback 127.x.x.x
CNetAddr addr(s4->sin_addr); CNetAddr addr(s4->sin_addr);
AddLocal(addr, LOCAL_IF); if (AddLocal(addr, LOCAL_IF))
printf("ipv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
} }
#ifdef USE_IPV6
else if (ifa->ifa_addr->sa_family == AF_INET6) else if (ifa->ifa_addr->sa_family == AF_INET6)
{ {
struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), pszIP, sizeof(pszIP)) != NULL)
printf("ipv6 %s: %s\n", ifa->ifa_name, pszIP);
#ifdef USE_IPV6
CNetAddr addr(s6->sin6_addr); CNetAddr addr(s6->sin6_addr);
AddLocal(addr, LOCAL_IF); if (AddLocal(addr, LOCAL_IF))
#endif printf("ipv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
} }
#endif
} }
freeifaddrs(myaddrs); freeifaddrs(myaddrs);
} }

View file

@ -305,7 +305,37 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
{ {
hSocketRet = INVALID_SOCKET; hSocketRet = INVALID_SOCKET;
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_storage sockaddr;
int nFamily = 0;
size_t nSockAddrLen = 0;
if (addrConnect.IsIPv4())
{
// Use IPv4 stack to connect to IPv4 addresses
struct sockaddr_in sockaddr4;
if (!addrConnect.GetSockAddr(&sockaddr4))
return false;
memcpy(&sockaddr, &sockaddr4, sizeof(sockaddr4));
nSockAddrLen = sizeof(sockaddr4);
nFamily = AF_INET;
}
#ifdef USE_IPV6
else if (addrConnect.IsIPv6())
{
struct sockaddr_in6 sockaddr6;
if (!addrConnect.GetSockAddr6(&sockaddr6))
return false;
memcpy(&sockaddr, &sockaddr6, sizeof(sockaddr6));
nSockAddrLen = sizeof(sockaddr6);
nFamily = AF_INET6;
}
#endif
else {
printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str());
return false;
}
SOCKET hSocket = socket(nFamily, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET) if (hSocket == INVALID_SOCKET)
return false; return false;
#ifdef SO_NOSIGPIPE #ifdef SO_NOSIGPIPE
@ -313,13 +343,6 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
#endif #endif
struct sockaddr_in sockaddr;
if (!addrConnect.GetSockAddr(&sockaddr))
{
closesocket(hSocket);
return false;
}
#ifdef WIN32 #ifdef WIN32
u_long fNonblock = 1; u_long fNonblock = 1;
if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
@ -332,7 +355,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
return false; return false;
} }
if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) if (connect(hSocket, (struct sockaddr*)&sockaddr, nSockAddrLen) == SOCKET_ERROR)
{ {
// WSAEINVAL is here because some legacy version of winsock uses it // WSAEINVAL is here because some legacy version of winsock uses it
if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL)
@ -531,6 +554,11 @@ bool CNetAddr::IsIPv4() const
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
} }
bool CNetAddr::IsIPv6() const
{
return (!IsIPv4());
}
bool CNetAddr::IsRFC1918() const bool CNetAddr::IsRFC1918() const
{ {
return IsIPv4() && ( return IsIPv4() && (
@ -919,12 +947,16 @@ std::vector<unsigned char> CService::GetKey() const
std::string CService::ToStringPort() const std::string CService::ToStringPort() const
{ {
return strprintf(":%i", port); return strprintf("%i", port);
} }
std::string CService::ToStringIPPort() const std::string CService::ToStringIPPort() const
{ {
return ToStringIP() + ToStringPort(); if (IsIPv4()) {
return ToStringIP() + ":" + ToStringPort();
} else {
return "[" + ToStringIP() + "]:" + ToStringPort();
}
} }
std::string CService::ToString() const std::string CService::ToString() const

View file

@ -31,6 +31,7 @@ class CNetAddr
void Init(); void Init();
void SetIP(const CNetAddr& ip); void SetIP(const CNetAddr& ip);
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not IPv4)
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)