diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/btfrv.cpp | 51 | ||||
| -rw-r--r-- | src/btfrv.h | 7 | ||||
| -rw-r--r-- | src/net.cpp | 13 | ||||
| -rw-r--r-- | src/net.h | 8 |
4 files changed, 72 insertions, 7 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp index 63e092e..7efd38b 100644 --- a/src/btfrv.cpp +++ b/src/btfrv.cpp | |||
| @@ -258,10 +258,57 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port, | |||
| 258 | return (RvSocket)s; | 258 | return (RvSocket)s; |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | bool RvServiceWaitPaired(RvSocket s) | 261 | bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs) |
| 262 | { | 262 | { |
| 263 | // A registration socket is idle on purpose -- we announce ourselves and | ||
| 264 | // wait, possibly for a long time, for someone to dial. ConnectTo() clears | ||
| 265 | // the socket timeout for exactly that reason, and with no timeout at all | ||
| 266 | // this recv() has no way to end when the path dies without either side | ||
| 267 | // saying so: a NAT drops the idle mapping, a relay restarts, a route | ||
| 268 | // changes. No FIN arrives, nothing is readable, and the call never | ||
| 269 | // returns. The thread stays alive and asleep, so ThreadBtfAccept never | ||
| 270 | // reaches the `continue` that would register again, and the node goes deaf | ||
| 271 | // while every outward sign says it is healthy. | ||
| 272 | // | ||
| 273 | // Seen in production on the bootstrap seed: 27 minutes without a block, | ||
| 274 | // eight behind the network, relay reachable throughout, fixed instantly by | ||
| 275 | // a restart. | ||
| 276 | // | ||
| 277 | // TCP keepalive is the obvious answer and it does not work here -- I | ||
| 278 | // measured it. With probes every 5s and a 2-probe limit, a socket whose | ||
| 279 | // replies were being dropped was still ESTAB after four minutes, the probe | ||
| 280 | // counter stuck at zero. So the timeout is ours to enforce, at the one | ||
| 281 | // place that knows what waiting means. | ||
| 282 | // | ||
| 283 | // Expiring is not a failure. It costs one reconnect per interval and | ||
| 284 | // returns the loop to a known state. | ||
| 285 | if (nTimeoutSecs > 0) | ||
| 286 | { | ||
| 287 | #ifdef _WIN32 | ||
| 288 | DWORD tv = (DWORD)nTimeoutSecs * 1000; | ||
| 289 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); | ||
| 290 | #else | ||
| 291 | struct timeval tv; tv.tv_sec = nTimeoutSecs; tv.tv_usec = 0; | ||
| 292 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); | ||
| 293 | #endif | ||
| 294 | } | ||
| 295 | |||
| 263 | unsigned char paired = 0; | 296 | unsigned char paired = 0; |
| 264 | return ReadN((SOCKET)s, &paired, 1) && paired == 0x01; | 297 | bool fOk = ReadN((SOCKET)s, &paired, 1) && paired == 0x01; |
| 298 | |||
| 299 | // Hand back a socket that blocks again: from here it is a data tunnel, and | ||
| 300 | // a tunnel legitimately sits quiet between messages. | ||
| 301 | if (fOk && nTimeoutSecs > 0) | ||
| 302 | { | ||
| 303 | #ifdef _WIN32 | ||
| 304 | DWORD tvOff = 0; | ||
| 305 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tvOff, sizeof(tvOff)); | ||
| 306 | #else | ||
| 307 | struct timeval tvOff; tvOff.tv_sec = 0; tvOff.tv_usec = 0; | ||
| 308 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, &tvOff, sizeof(tvOff)); | ||
| 309 | #endif | ||
| 310 | } | ||
| 311 | return fOk; | ||
| 265 | } | 312 | } |
| 266 | 313 | ||
| 267 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, | 314 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, |
diff --git a/src/btfrv.h b/src/btfrv.h index 9d6756e..9cd7476 100644 --- a/src/btfrv.h +++ b/src/btfrv.h | |||
| @@ -49,7 +49,12 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port, | |||
| 49 | // Block on a registered service socket until a client is paired to us. Returns | 49 | // Block on a registered service socket until a client is paired to us. Returns |
| 50 | // false if the relay drops us or errors, in which case the caller should close | 50 | // false if the relay drops us or errors, in which case the caller should close |
| 51 | // the socket and register again. | 51 | // the socket and register again. |
| 52 | bool RvServiceWaitPaired(RvSocket s); | 52 | // Blocks until a client is paired with us. Give it a timeout, in seconds, and |
| 53 | // it returns false once that passes with nobody arriving -- which the caller | ||
| 54 | // should treat as "register again", not as an error. Zero means wait forever, | ||
| 55 | // which is what this used to do unconditionally and must not be used for a | ||
| 56 | // registration socket: see the note on the implementation. | ||
| 57 | bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs = 0); | ||
| 53 | 58 | ||
| 54 | // ---- client side (the node dialing a .btf address) ---- | 59 | // ---- client side (the node dialing a .btf address) ---- |
| 55 | // Connect through the relay to the service registered under `target_pubkey`. | 60 | // Connect through the relay to the service registered under `target_pubkey`. |
diff --git a/src/net.cpp b/src/net.cpp index 395092d..2581578 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -783,12 +783,17 @@ void ThreadBtfAccept(void* parg) | |||
| 783 | LogPrint("net", "rendezvous: registered at %s, waiting for a dial\n", | 783 | LogPrint("net", "rendezvous: registered at %s, waiting for a dial\n", |
| 784 | strMeeting.c_str()); | 784 | strMeeting.c_str()); |
| 785 | 785 | ||
| 786 | // Now block for someone to arrive. | 786 | // Now wait for someone to arrive -- but not forever. This used to have |
| 787 | if (!btf::RvServiceWaitPaired(rv)) | 787 | // no bound, and a registration whose path died quietly left the thread |
| 788 | // parked in recv() with nothing to wake it: the loop never came back | ||
| 789 | // here, the node stopped being reachable, and nothing in the log said | ||
| 790 | // so. Re-registering every few minutes when nobody has dialled costs | ||
| 791 | // one reconnect and removes the whole failure mode. | ||
| 792 | if (!btf::RvServiceWaitPaired(rv, BTF_RENDEZVOUS_WAIT_SECS)) | ||
| 788 | { | 793 | { |
| 789 | btf::RvClose(rv); | 794 | btf::RvClose(rv); |
| 790 | LogPrint("net", "rendezvous: %s dropped us before any dial, re-registering\n", | 795 | LogPrint("net", "rendezvous: no dial at %s within %ds (or it dropped us), " |
| 791 | strMeeting.c_str()); | 796 | "re-registering\n", strMeeting.c_str(), BTF_RENDEZVOUS_WAIT_SECS); |
| 792 | continue; | 797 | continue; |
| 793 | } | 798 | } |
| 794 | 799 | ||
| @@ -43,6 +43,14 @@ static const unsigned int MAX_PEX_DESCRIPTOR_BYTES = 1024; | |||
| 43 | // connection is the intended traffic; anything faster is someone else's idea. | 43 | // connection is the intended traffic; anything faster is someone else's idea. |
| 44 | static const int64 PEX_MIN_INTERVAL = 60; | 44 | static const int64 PEX_MIN_INTERVAL = 60; |
| 45 | 45 | ||
| 46 | // How long to sit registered at a rendezvous relay before giving up on this | ||
| 47 | // attempt and registering again. Bounded because a registration socket that | ||
| 48 | // goes quiet is indistinguishable, from inside recv(), from one whose path has | ||
| 49 | // died -- and the second kind never wakes up. Five minutes is short enough | ||
| 50 | // that a node is unreachable only briefly and long enough that the reconnect | ||
| 51 | // traffic is nothing: one per relay per five minutes. | ||
| 52 | static const int BTF_RENDEZVOUS_WAIT_SECS = 300; | ||
| 53 | |||
| 46 | // Descriptors to hand a peer: ours first, then peers that answered us. | 54 | // Descriptors to hand a peer: ours first, then peers that answered us. |
| 47 | void BtfPexCollect(std::vector<std::string>& vDescOut); | 55 | void BtfPexCollect(std::vector<std::string>& vDescOut); |
| 48 | // Verify descriptors a peer sent and remember the good ones. Returns how many. | 56 | // Verify descriptors a peer sent and remember the good ones. Returns how many. |