Merge pull request #4378

dc942e6 Introduce whitelisted peers. (Pieter Wuille)
This commit is contained in:
Wladimir J. van der Laan 2014-07-14 08:59:12 +02:00
commit c9bc398ad9
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
6 changed files with 99 additions and 28 deletions

View File

@ -10,7 +10,7 @@ touch "$DATADIR/regtest/debug.log"
tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" & tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
WAITER=$! WAITER=$!
PORT=`expr $BASHPID + 10000` PORT=`expr $BASHPID + 10000`
"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -regtest -rpcport=`expr $PORT + 1` & "@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
BITCOIND=$! BITCOIND=$!
#Install a watchdog. #Install a watchdog.

View File

@ -58,7 +58,8 @@ CWallet* pwalletMain;
enum BindFlags { enum BindFlags {
BF_NONE = 0, BF_NONE = 0,
BF_EXPLICIT = (1U << 0), BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1) BF_REPORT_ERROR = (1U << 1),
BF_WHITELIST = (1U << 2),
}; };
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
@ -192,7 +193,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr)) if (!(flags & BF_EXPLICIT) && IsLimited(addr))
return false; return false;
std::string strError; std::string strError;
if (!BindListenPort(addr, strError)) { if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) {
if (flags & BF_REPORT_ERROR) if (flags & BF_REPORT_ERROR)
return InitError(strError); return InitError(strError);
return false; return false;
@ -252,6 +253,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
#endif #endif
#endif #endif
strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n"; strUsage += "\n" + _("Wallet options:") + "\n";
@ -506,11 +509,11 @@ bool AppInit2(boost::thread_group& threadGroup)
// ********************************************************* Step 2: parameter interactions // ********************************************************* Step 2: parameter interactions
if (mapArgs.count("-bind")) { if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
// when specifying an explicit binding address, you want to listen on it // when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified // even when -connect or -proxy is specified
if (SoftSetBoolArg("-listen", true)) if (SoftSetBoolArg("-listen", true))
LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n"); LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n");
} }
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
@ -554,7 +557,7 @@ bool AppInit2(boost::thread_group& threadGroup)
} }
// Make sure enough file descriptors are available // Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind"), 1); int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
nMaxConnections = GetArg("-maxconnections", 125); nMaxConnections = GetArg("-maxconnections", 125);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
@ -771,6 +774,15 @@ bool AppInit2(boost::thread_group& threadGroup)
} }
} }
if (mapArgs.count("-whitelist")) {
BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) {
CSubNet subnet(net);
if (!subnet.IsValid())
return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
CNode::AddWhitelistedRange(subnet);
}
}
CService addrProxy; CService addrProxy;
bool fProxy = false; bool fProxy = false;
if (mapArgs.count("-proxy")) { if (mapArgs.count("-proxy")) {
@ -807,13 +819,21 @@ bool AppInit2(boost::thread_group& threadGroup)
bool fBound = false; bool fBound = false;
if (fListen) { if (fListen) {
if (mapArgs.count("-bind")) { if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
CService addrBind; CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
} }
BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, 0, false))
return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind));
if (addrBind.GetPort() == 0)
return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
}
} }
else { else {
struct in_addr inaddr_any; struct in_addr inaddr_any;

View File

@ -212,7 +212,7 @@ struct CBlockReject {
struct CNodeState { struct CNodeState {
// Accumulated misbehaviour score for this peer. // Accumulated misbehaviour score for this peer.
int nMisbehavior; int nMisbehavior;
// Whether this peer should be disconnected and banned. // Whether this peer should be disconnected and banned (unless whitelisted).
bool fShouldBan; bool fShouldBan;
// String name of this peer (debugging/logging purposes). // String name of this peer (debugging/logging purposes).
std::string name; std::string name;
@ -1427,7 +1427,8 @@ void Misbehaving(NodeId pnode, int howmuch)
return; return;
state->nMisbehavior += howmuch; state->nMisbehavior += howmuch;
if (state->nMisbehavior >= GetArg("-banscore", 100)) int banscore = GetArg("-banscore", 100);
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{ {
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
state->fShouldBan = true; state->fShouldBan = true;
@ -3952,6 +3953,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
if (nEvicted > 0) if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
} else if (pfrom->fWhitelisted) {
// Always relay transactions received from whitelisted peers, even
// if they are already in the mempool (allowing the node to function
// as a gateway for nodes hidden behind it).
RelayTransaction(tx);
} }
int nDoS = 0; int nDoS = 0;
if (state.IsInvalid(nDoS)) if (state.IsInvalid(nDoS))
@ -4445,11 +4451,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
CNodeState &state = *State(pto->GetId()); CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) { if (state.fShouldBan) {
if (pto->addr.IsLocal()) if (pto->fWhitelisted)
LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString()); LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
else { else {
pto->fDisconnect = true; pto->fDisconnect = true;
CNode::Ban(pto->addr); if (pto->addr.IsLocal())
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
else
CNode::Ban(pto->addr);
} }
state.fShouldBan = false; state.fShouldBan = false;
} }

View File

@ -50,7 +50,16 @@
using namespace std; using namespace std;
using namespace boost; using namespace boost;
static const int MAX_OUTBOUND_CONNECTIONS = 8; namespace {
const int MAX_OUTBOUND_CONNECTIONS = 8;
struct ListenSocket {
SOCKET socket;
bool whitelisted;
ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
};
}
// //
// Global state variables // Global state variables
@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL; static CNode* pnodeLocalHost = NULL;
static CNode* pnodeSync = NULL; static CNode* pnodeSync = NULL;
uint64_t nLocalHostNonce = 0; uint64_t nLocalHostNonce = 0;
static std::vector<SOCKET> vhListenSocket; static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman; CAddrMan addrman;
int nMaxConnections = 125; int nMaxConnections = 125;
@ -593,6 +602,24 @@ bool CNode::Ban(const CNetAddr &addr) {
return true; return true;
} }
std::vector<CSubNet> CNode::vWhitelistedRange;
CCriticalSection CNode::cs_vWhitelistedRange;
bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
LOCK(cs_vWhitelistedRange);
BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
if (subnet.Match(addr))
return true;
}
return false;
}
void CNode::AddWhitelistedRange(const CSubNet &subnet) {
LOCK(cs_vWhitelistedRange);
vWhitelistedRange.push_back(subnet);
}
#undef X #undef X
#define X(name) stats.name = name #define X(name) stats.name = name
void CNode::copyStats(CNodeStats &stats) void CNode::copyStats(CNodeStats &stats)
@ -609,6 +636,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nStartingHeight); X(nStartingHeight);
X(nSendBytes); X(nSendBytes);
X(nRecvBytes); X(nRecvBytes);
X(fWhitelisted);
stats.fSyncNode = (this == pnodeSync); stats.fSyncNode = (this == pnodeSync);
// It is common for nodes with good ping times to suddenly become lagged, // It is common for nodes with good ping times to suddenly become lagged,
@ -848,9 +876,9 @@ void ThreadSocketHandler()
SOCKET hSocketMax = 0; SOCKET hSocketMax = 0;
bool have_fds = false; bool have_fds = false;
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {
FD_SET(hListenSocket, &fdsetRecv); FD_SET(hListenSocket.socket, &fdsetRecv);
hSocketMax = max(hSocketMax, hListenSocket); hSocketMax = max(hSocketMax, hListenSocket.socket);
have_fds = true; have_fds = true;
} }
@ -917,13 +945,13 @@ void ThreadSocketHandler()
// //
// Accept new connections // Accept new connections
// //
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
{ {
if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{ {
struct sockaddr_storage sockaddr; struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr); socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr; CAddress addr;
int nInbound = 0; int nInbound = 0;
@ -931,6 +959,7 @@ void ThreadSocketHandler()
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
LogPrintf("Warning: Unknown socket family\n"); LogPrintf("Warning: Unknown socket family\n");
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
{ {
LOCK(cs_vNodes); LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes) BOOST_FOREACH(CNode* pnode, vNodes)
@ -948,7 +977,7 @@ void ThreadSocketHandler()
{ {
closesocket(hSocket); closesocket(hSocket);
} }
else if (CNode::IsBanned(addr)) else if (CNode::IsBanned(addr) && !whitelisted)
{ {
LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
closesocket(hSocket); closesocket(hSocket);
@ -957,6 +986,7 @@ void ThreadSocketHandler()
{ {
CNode* pnode = new CNode(hSocket, addr, "", true); CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef(); pnode->AddRef();
pnode->fWhitelisted = whitelisted;
{ {
LOCK(cs_vNodes); LOCK(cs_vNodes);
@ -1580,7 +1610,7 @@ void ThreadMessageHandler()
bool BindListenPort(const CService &addrBind, string& strError) bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
{ {
strError = ""; strError = "";
int nOne = 1; int nOne = 1;
@ -1661,9 +1691,9 @@ bool BindListenPort(const CService &addrBind, string& strError)
return false; return false;
} }
vhListenSocket.push_back(hListenSocket); vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
if (addrBind.IsRoutable() && fDiscover) if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
AddLocal(addrBind, LOCAL_BIND); AddLocal(addrBind, LOCAL_BIND);
return true; return true;
@ -1788,9 +1818,9 @@ public:
BOOST_FOREACH(CNode* pnode, vNodes) BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->hSocket != INVALID_SOCKET) if (pnode->hSocket != INVALID_SOCKET)
closesocket(pnode->hSocket); closesocket(pnode->hSocket);
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
if (hListenSocket != INVALID_SOCKET) if (hListenSocket.socket != INVALID_SOCKET)
if (closesocket(hListenSocket) == SOCKET_ERROR) if (closesocket(hListenSocket.socket) == SOCKET_ERROR)
LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
// clean up some globals (to help leak detection) // clean up some globals (to help leak detection)

View File

@ -64,7 +64,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
void MapPort(bool fUseUPnP); void MapPort(bool fUseUPnP);
unsigned short GetListenPort(); unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
void StartNode(boost::thread_group& threadGroup); void StartNode(boost::thread_group& threadGroup);
bool StopNode(); bool StopNode();
void SocketSendData(CNode *pnode); void SocketSendData(CNode *pnode);
@ -154,6 +154,7 @@ public:
uint64_t nSendBytes; uint64_t nSendBytes;
uint64_t nRecvBytes; uint64_t nRecvBytes;
bool fSyncNode; bool fSyncNode;
bool fWhitelisted;
double dPingTime; double dPingTime;
double dPingWait; double dPingWait;
std::string addrLocal; std::string addrLocal;
@ -236,6 +237,7 @@ public:
// store the sanitized version in cleanSubVer. The original should be used when dealing with // store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged. // the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer; std::string strSubVer, cleanSubVer;
bool fWhitelisted; // This peer can bypass DoS banning.
bool fOneShot; bool fOneShot;
bool fClient; bool fClient;
bool fInbound; bool fInbound;
@ -259,6 +261,11 @@ protected:
static std::map<CNetAddr, int64_t> setBanned; static std::map<CNetAddr, int64_t> setBanned;
static CCriticalSection cs_setBanned; static CCriticalSection cs_setBanned;
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
static std::vector<CSubNet> vWhitelistedRange;
static CCriticalSection cs_vWhitelistedRange;
// Basic fuzz-testing // Basic fuzz-testing
void Fuzz(int nChance); // modifies ssSend void Fuzz(int nChance); // modifies ssSend
@ -305,6 +312,7 @@ public:
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0; nVersion = 0;
strSubVer = ""; strSubVer = "";
fWhitelisted = false;
fOneShot = false; fOneShot = false;
fClient = false; // set by version message fClient = false; // set by version message
fInbound = fInboundIn; fInbound = fInboundIn;
@ -720,6 +728,9 @@ public:
static bool Ban(const CNetAddr &ip); static bool Ban(const CNetAddr &ip);
void copyStats(CNodeStats &stats); void copyStats(CNodeStats &stats);
static bool IsWhitelistedRange(const CNetAddr &ip);
static void AddWhitelistedRange(const CSubNet &subnet);
// Network stats // Network stats
static void RecordBytesRecv(uint64_t bytes); static void RecordBytesRecv(uint64_t bytes);
static void RecordBytesSent(uint64_t bytes); static void RecordBytesSent(uint64_t bytes);

View File

@ -137,6 +137,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
obj.push_back(Pair("syncheight", statestats.nSyncHeight)); obj.push_back(Pair("syncheight", statestats.nSyncHeight));
} }
obj.push_back(Pair("syncnode", stats.fSyncNode)); obj.push_back(Pair("syncnode", stats.fSyncNode));
obj.push_back(Pair("whitelisted", stats.fWhitelisted));
ret.push_back(obj); ret.push_back(obj);
} }