changeset 2476:a9b012461972 draft

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.
author Pieter Wuille <pieter.wuille@gmail.com>
date Sat, 31 Mar 2012 17:58:25 +0200
parents c5898fd4a4f5
children f8e75c9db98a
files bitcoin-qt.pro doc/build-osx.txt doc/build-unix.txt src/main.cpp src/makefile.linux-mingw src/makefile.mingw src/makefile.osx src/makefile.unix src/net.cpp src/netbase.cpp src/netbase.h
diffstat 11 files changed, 91 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -2,7 +2,7 @@
 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 
--- a/doc/build-osx.txt
+++ b/doc/build-osx.txt
@@ -44,7 +44,7 @@
 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.
--- a/doc/build-unix.txt
+++ b/doc/build-unix.txt
@@ -43,6 +43,9 @@
  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 @@
 
 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
 
 
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2424,9 +2424,6 @@
         {
             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);
--- a/src/makefile.linux-mingw
+++ b/src/makefile.linux-mingw
@@ -27,7 +27,7 @@
  -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)
 
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -23,7 +23,7 @@
  -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)
 
--- a/src/makefile.osx
+++ b/src/makefile.osx
@@ -53,7 +53,7 @@
 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.
--- 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))
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -733,7 +733,11 @@
         //
         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 @@
             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 @@
 
 bool BindListenPort(string& strError)
 {
+    unsigned short nPort = GetListenPort();
     strError = "";
     int nOne = 1;
 
@@ -1636,7 +1641,12 @@
 #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 @@
 
     // 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 @@
             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);
+                if (AddLocal(addr, LOCAL_IF))
+                    printf("ipv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
+            }
 #endif
-            }
         }
         freeifaddrs(myaddrs);
     }
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -305,7 +305,37 @@
 {
     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 @@
     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 @@
         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 @@
     return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
 }
 
+bool CNetAddr::IsIPv6() const
+{
+    return (!IsIPv4());
+}
+
 bool CNetAddr::IsRFC1918() const
 {
     return IsIPv4() && (
@@ -919,12 +947,16 @@
 
 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
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -31,6 +31,7 @@
         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)