From 23aa78c405f82257e8578afb3d5d244aa27dcd74 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 31 Mar 2012 17:58:25 +0200 Subject: [PATCH] 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. --- bitcoin-qt.pro | 2 +- doc/build-osx.txt | 2 +- doc/build-unix.txt | 5 +++- src/main.cpp | 3 --- src/makefile.linux-mingw | 2 +- src/makefile.mingw | 2 +- src/makefile.osx | 2 +- src/makefile.unix | 2 +- src/net.cpp | 56 ++++++++++++++++++++++++++-------------- src/netbase.cpp | 54 ++++++++++++++++++++++++++++++-------- src/netbase.h | 1 + 11 files changed, 91 insertions(+), 40 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 112e8e93..c9bbc873 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -2,7 +2,7 @@ TEMPLATE = app TARGET = VERSION = 0.6.99 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 # for boost 1.37, add -mt to the boost libraries diff --git a/doc/build-osx.txt b/doc/build-osx.txt index 2ae77067..256614f7 100644 --- a/doc/build-osx.txt +++ b/doc/build-osx.txt @@ -44,7 +44,7 @@ sudo port install qrencode 4. Now you should be able to build bitcoind: cd bitcoin/src -make -f makefile.osx +make -f makefile.osx USE_IPV6=1 Run: ./bitcoind --help # for a list of command-line options. diff --git a/doc/build-unix.txt b/doc/build-unix.txt index c5b42050..eb597368 100644 --- a/doc/build-unix.txt +++ b/doc/build-unix.txt @@ -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=1 QRCode support enabled +IPv6 support may be enabled by setting + USE_IPV6=1 Enable IPv6 support + Licenses of statically linked libraries: Berkeley DB New BSD license with additional requirement that linked 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): 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 diff --git a/src/main.cpp b/src/main.cpp index b0ed28aa..f9691a5d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2424,9 +2424,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { if (fShutdown) 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) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 645f0a16..373b49ab 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -27,7 +27,7 @@ LIBS= \ -l ssl \ -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 CFLAGS=-O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) diff --git a/src/makefile.mingw b/src/makefile.mingw index bb646695..156eb17c 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -23,7 +23,7 @@ LIBS= \ -l ssl \ -l crypto -DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB +DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DUSE_IPV6 DEBUGFLAGS=-g CFLAGS=-mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) diff --git a/src/makefile.osx b/src/makefile.osx index eb9ae4ba..9c05d500 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -53,7 +53,7 @@ LIBS += \ TESTDEFS += -DBOOST_TEST_DYN_LINK endif -DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 +DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DUSE_IPV6 ifdef RELEASE # Compile for maximum compatibility and smallest size. diff --git a/src/makefile.unix b/src/makefile.unix index 53fb1f0b..ce8b55cd 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -4,7 +4,7 @@ USE_UPNP:=0 -DEFS= +DEFS=-DUSE_IPV6 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)) diff --git a/src/net.cpp b/src/net.cpp index 8603514f..75c8bbab 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -733,7 +733,11 @@ void ThreadSocketHandler2(void* parg) // if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { +#ifdef USE_IPV6 + struct sockaddr_in6 sockaddr; +#else struct sockaddr_in sockaddr; +#endif socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); CAddress addr; @@ -1390,7 +1394,7 @@ void ThreadOpenConnections2(void* parg) CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); // 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; nTries++; @@ -1620,6 +1624,7 @@ void ThreadMessageHandler2(void* parg) bool BindListenPort(string& strError) { + unsigned short nPort = GetListenPort(); strError = ""; int nOne = 1; @@ -1636,7 +1641,12 @@ bool BindListenPort(string& strError) #endif // 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) { 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, // 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)); sockaddr.sin_family = AF_INET; 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) { int nErr = WSAGetLastError(); 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 - 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()); return false; } - printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); + printf("Bound to port %d\n", (int)nPort); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) @@ -1727,28 +1751,22 @@ void static Discover() if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (strcmp(ifa->ifa_name, "lo") == 0) continue; if (strcmp(ifa->ifa_name, "lo0") == 0) continue; - char pszIP[100]; if (ifa->ifa_addr->sa_family == AF_INET) { 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); - 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) { 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); - AddLocal(addr, LOCAL_IF); -#endif + if (AddLocal(addr, LOCAL_IF)) + printf("ipv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); } +#endif } freeifaddrs(myaddrs); } diff --git a/src/netbase.cpp b/src/netbase.cpp index 48709dc5..a22d42a9 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -305,7 +305,37 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe { 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) return false; #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)); #endif - struct sockaddr_in sockaddr; - if (!addrConnect.GetSockAddr(&sockaddr)) - { - closesocket(hSocket); - return false; - } - #ifdef WIN32 u_long fNonblock = 1; if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) @@ -332,7 +355,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe 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 if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) @@ -531,6 +554,11 @@ bool CNetAddr::IsIPv4() const return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); } +bool CNetAddr::IsIPv6() const +{ + return (!IsIPv4()); +} + bool CNetAddr::IsRFC1918() const { return IsIPv4() && ( @@ -919,12 +947,16 @@ std::vector CService::GetKey() const std::string CService::ToStringPort() const { - return strprintf(":%i", port); + return strprintf("%i", port); } std::string CService::ToStringIPPort() const { - return ToStringIP() + ToStringPort(); + if (IsIPv4()) { + return ToStringIP() + ":" + ToStringPort(); + } else { + return "[" + ToStringIP() + "]:" + ToStringPort(); + } } std::string CService::ToString() const diff --git a/src/netbase.h b/src/netbase.h index e5c466e4..acbcc36d 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -31,6 +31,7 @@ class CNetAddr void Init(); void SetIP(const CNetAddr& ip); 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 IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)