I've had a look at the code in bitcoinrpc.cpp, specifically the function StartRPCThreads().
The code attempts to open an IPv6 socket and then set the 'v6_only' option to false if the 'rpcallowip' option is used. This should cause the socket to listen for IPv4 connections, but on my system netstat doesn't show a listening IPv4 socket (see my original post).
If the socket failed to open, or the 'rpcallowip' option is not used, the code opens a separate IPv4 socket. This means that when 'rpcallowip' is not used, separate IPv4 and IPv6 sockets are always used.
When I modify the code (by setting
fListening to false), so that it tries to open the IPv4 socket after opening the IPv4/IPv6 socket, I get the error:
Error: An error occurred while setting up the RPC port 8332 for listening on IPv4: Address already in usewhich indicates that the combined IPv4/IPv6 socket is listening, although netstat doesn't show this.
When I modify the code (by forcing
set_option(v6_only(true)) and removing the
if( !fListening... ) line), so that it always opens separate IPv4 and IPv6 sockets, things work as I expect them to:
user@server:~$ netstat -lp | grep bitcoind
tcp 0 0 *:8332 *:* LISTEN 3063/bitcoind
tcp 0 0 *:8333 *:* LISTEN 3063/bitcoind
tcp6 0 0 [::]:8332 [::]:* LISTEN 3063/bitcoind
tcp6 0 0 [::]:8333 [::]:* LISTEN 3063/bitcoind
I assume that the IPv4/IPv6 socket just works on most systems. Unfortunately I can't see a work-around for systems like mine without recompiling bitcoind.
Here are the changes I made:
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index c99b74f..ba7f462 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -791,8 +791,8 @@ void StartRPCThreads()
acceptor->open(endpoint.protocol());
acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
- // Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
- acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error);
+ // This is an IPv6 only socket
+ acceptor->set_option(boost::asio::ip::v6_only(true), v6_only_error);
acceptor->bind(endpoint);
acceptor->listen(socket_base::max_connections);
@@ -807,8 +807,7 @@ void StartRPCThreads()
}
try {
- // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
- if (!fListening || loopback || v6_only_error)
+ // This is an IPv4 socket
{
bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
endpoint.address(bindAddress);