/* * Send a ping packet to one of the endpoints. */ static void sendPing(struct InterfaceController_Peer* ep) { struct InterfaceController_pvt* ic = ifcontrollerForPeer(ep); ep->pingCount++; struct SwitchPinger_Ping* ping = SwitchPinger_newPing(ep->switchLabel, String_CONST(""), ic->timeoutMilliseconds, onPingResponse, ic->allocator, ic->switchPinger); #ifdef Log_DEBUG uint8_t key[56]; Base32_encode(key, 56, CryptoAuth_getHerPublicKey(ep->cryptoAuthIf), 32); #endif if (!ping) { Log_debug(ic->logger, "Failed to ping [%s.k], out of ping slots", key); return; } else { Log_debug(ic->logger, "SwitchPing [%s.k]", key); } ping->onResponseContext = ep; }
/* * Send a ping packet to one of the endpoints. */ static void sendPing(struct Peer* ep) { struct InterfaceController_pvt* ic = Identity_check(ep->ici->ic); ep->pingCount++; struct SwitchPinger_Ping* ping = SwitchPinger_newPing(ep->addr.path, String_CONST(""), ic->timeoutMilliseconds, onPingResponse, ep->alloc, ic->switchPinger); if (Defined(Log_DEBUG)) { uint8_t key[56]; Base32_encode(key, 56, ep->caSession->herPublicKey, 32); if (!ping) { Log_debug(ic->logger, "Failed to ping [%s.k], out of ping slots", key); } else { Log_debug(ic->logger, "SwitchPing [%s.k]", key); } } if (ping) { ping->onResponseContext = ep; } }
String* Key_stringify(uint8_t key[32], struct Allocator* alloc) { String* out = String_newBinary(NULL, 55, alloc); Base32_encode((uint8_t*)out->bytes, 53, key, 32); out->bytes[53] = '.'; out->bytes[54] = 'k'; return out; }
static void onPingResponse(struct SwitchPinger_Response* resp, void* onResponseContext) { if (SwitchPinger_Result_OK != resp->res) { return; } struct InterfaceController_Peer* ep = Identity_check((struct InterfaceController_Peer*) onResponseContext); struct InterfaceController_pvt* ic = ifcontrollerForPeer(ep); struct Address addr; Bits_memset(&addr, 0, sizeof(struct Address)); Bits_memcpyConst(addr.key, CryptoAuth_getHerPublicKey(ep->cryptoAuthIf), 32); addr.path = ep->switchLabel; addr.protocolVersion = resp->version; #ifdef Log_DEBUG uint8_t addrStr[60]; Address_print(addrStr, &addr); uint8_t key[56]; Base32_encode(key, 56, CryptoAuth_getHerPublicKey(ep->cryptoAuthIf), 32); #endif if (!Version_isCompatible(Version_CURRENT_PROTOCOL, resp->version)) { Log_debug(ic->logger, "got switch pong from node [%s] with incompatible version [%d]", key, resp->version); } else { Log_debug(ic->logger, "got switch pong from node [%s] with version [%d]", key, resp->version); } if (!ep->timeOfLastPing) { // We've never heard from this machine before (or we've since forgotten about it) // This is here because we want the tests to function without the janitor present. // Other than that, it just makes a slightly more synchronous/guaranteed setup. Router_sendGetPeers(ic->router, &addr, 0, 0, ic->allocator); } struct Node_Link* link = Router_linkForPath(ic->router, resp->label); if (!link || !Node_getBestParent(link->child)) { RumorMill_addNode(ic->rumorMill, &addr); } else { Log_debug(ic->logger, "link exists"); } ep->timeOfLastPing = Time_currentTimeMilliseconds(ic->eventBase); #ifdef Log_DEBUG // This will be false if it times out. //Assert_true(label == ep->switchLabel); uint8_t path[20]; AddrTools_printPath(path, resp->label); uint8_t sl[20]; AddrTools_printPath(sl, ep->switchLabel); Log_debug(ic->logger, "Received [%s] from lazy endpoint [%s] [%s]", SwitchPinger_resultString(resp->res)->bytes, path, sl); #endif }
// Called from the pingInteral timeout. static void pingCallback(void* vic) { struct Context* ic = Identity_cast((struct Context*) vic); uint64_t now = Time_currentTimeMilliseconds(ic->eventBase); ic->pingCount++; // scan for endpoints have not sent anything recently. for (uint32_t i = 0; i < ic->peerMap.count; i++) { struct IFCPeer* ep = ic->peerMap.values[i]; if (now > ep->timeOfLastMessage + ic->pingAfterMilliseconds) { #ifdef Log_DEBUG uint8_t key[56]; Base32_encode(key, 56, CryptoAuth_getHerPublicKey(ep->cryptoAuthIf), 32); #endif if (ep->transient && now > ep->timeOfLastMessage + ic->forgetAfterMilliseconds) { Log_debug(ic->logger, "Unresponsive peer [%s.k] has not responded in [%u] " "seconds, dropping connection", key, ic->forgetAfterMilliseconds / 1024); Allocator_free(ep->external->allocator); } else if (now > ep->timeOfLastMessage + ic->unresponsiveAfterMilliseconds) { // Lets skip 87% of pings when they're really down. if (ic->pingCount % 8) { continue; } ep->state = InterfaceController_PeerState_UNRESPONSIVE; uint32_t lag = ((now - ep->timeOfLastMessage) / 1024); Log_debug(ic->logger, "Pinging unresponsive peer [%s.k] lag [%u]", key, lag); } else { uint32_t lag = ((now - ep->timeOfLastMessage) / 1024); Log_debug(ic->logger, "Pinging lazy peer [%s] lag [%u]", key, lag); } struct SwitchPinger_Ping* ping = SwitchPinger_newPing(ep->switchLabel, String_CONST(""), ic->timeoutMilliseconds, onPingResponse, ic->switchPinger); ping->onResponseContext = ep; SwitchPinger_sendPing(ping); } } }
static int genAddress(uint8_t addressOut[40], uint8_t privateKeyHexOut[65], uint8_t publicKeyBase32Out[53]) { struct Address address; uint8_t privateKey[32]; for (;;) { randombytes(privateKey, 32); crypto_scalarmult_curve25519_base(address.key, privateKey); AddressCalc_addressForPublicKey(address.ip6.bytes, address.key); // Brute force for keys until one matches FC00:/8 if (address.ip6.bytes[0] == 0xFC) { Hex_encode(privateKeyHexOut, 65, privateKey, 32); Base32_encode(publicKeyBase32Out, 53, address.key, 32); Address_printIp(addressOut, &address); return 0; } } }
int main() { struct Allocator* alloc; BufferAllocator_STACK(alloc, 512); struct Random* rand = Random_new(alloc, NULL); uint8_t bytes[32]; Random_bytes(rand, bytes, 32); uint8_t base32[64]; Bits_memset(base32, 0, 64); Assert_always(Base32_encode(base32, 64, bytes, 32) == 52); //printf("base32 encoded: %s\n", base32); uint8_t bytes2[32]; Assert_always(Base32_decode(bytes2, 32, base32, 52) == 32); Assert_always(Bits_memcmp(bytes, bytes2, 32) == 0); }
int main() { /* verify public key */ struct Address address; crypto_scalarmult_curve25519_base(address.key, privateKey); AddressCalc_addressForPublicKey(address.ip6.bytes, address.key); uint8_t privateKeyHexOut[65]; uint8_t publicKeyHexOut[65]; uint8_t publicKeyBase32Out[53]; Hex_encode(privateKeyHexOut, 65, privateKey, 32); Hex_encode(publicKeyHexOut, 65, publicKey, 32); printf("Private key %s (hex)\n\nExpect:\nPublic Key: %s (hex)\n" "Public Key: %s (base32)\nAddress: %s\n", privateKeyHexOut, publicKeyHexOut, publicKeyBase32, ipv6); uint8_t addressOut[40]; Hex_encode(publicKeyHexOut, 65, address.key, 32); Base32_encode(publicKeyBase32Out, 53, address.key, 32); Address_printIp(addressOut, &address); printf("\nGot:\nPublic Key: %s (hex)\n" "Public Key: %s (base32)\nAddress: %s\n", publicKeyHexOut, publicKeyBase32Out, addressOut); Assert_always(0 == memcmp(address.key, publicKey, 32)); Assert_always(0 == strcmp(publicKeyBase32, (char*) publicKeyBase32Out)); Assert_always(0 == strcmp(ipv6, (char*) addressOut)); }
static int genAddress(uint8_t addressOut[40], uint8_t privateKeyHexOut[65], uint8_t publicKeyBase32Out[53], uint8_t privateKey[32]) { struct Address address; crypto_scalarmult_curve25519_base(address.key, privateKey); AddressCalc_addressForPublicKey(address.ip6.bytes, address.key); // Brute force for keys until one matches FC00:/8 if( address.ip6.bytes[0] == 0xFC// && //(address.ip6.bytes[15] & 0xF) == (address.ip6.bytes[15] & 0x0F << 4) && //address.ip6.bytes[14] == address.ip6.bytes[15] ) { Hex_encode(privateKeyHexOut, 65, privateKey, 32); Base32_encode(publicKeyBase32Out, 53, address.key, 32); Address_printIp(addressOut, &address); return 1; } return 0; }
int main() { struct Allocator* alloc = MallocAllocator_new(20000); struct Random* rand = Random_new(alloc, NULL, NULL); uint8_t bytes[32]; Random_bytes(rand, bytes, 32); uint8_t base32[64]; Bits_memset(base32, 0, 64); Assert_always(Base32_encode(base32, 64, bytes, 32) == 52); //printf("base32 encoded: %s\n", base32); uint8_t bytes2[32]; Assert_always(Base32_decode(bytes2, 32, base32, 52) == 32); Assert_always(Bits_memcmp(bytes, bytes2, 32) == 0); Allocator_free(alloc); return 0; }
int main(int argc, char** argv) { #ifdef Log_KEYS fprintf(stderr, "Log_LEVEL = KEYS, EXPECT TO SEE PRIVATE KEYS IN YOUR LOGS!\n"); #endif Crypto_init(); assert(argc > 0); if (argc == 1) { // no arguments if (isatty(STDIN_FILENO)) { // We were started from a terminal // The chances an user wants to type in a configuration // bij hand are pretty slim so we show him the usage return usage(argv[0]); } else { // We assume stdin is a configuration file and that we should // start routing } } if (argc == 2) { // one argument if (strcmp(argv[1], "--help") == 0) { return usage(argv[0]); } else if (strcmp(argv[1], "--genconf") == 0) { return genconf(); } else if (strcmp(argv[1], "--getcmds") == 0) { // Performed after reading the configuration } else if (strcmp(argv[1], "--pidfile") == 0) { // Performed after reading the configuration } else { fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[1]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); return -1; } } if (argc > 2) { // more than one argument? fprintf(stderr, "%s: too many arguments\n", argv[0]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); return -1; } struct Context context; memset(&context, 0, sizeof(struct Context)); context.base = event_base_new(); // Allow it to allocate 4MB context.allocator = MallocAllocator_new(1<<22); struct Reader* reader = FileReader_new(stdin, context.allocator); Dict config; if (JsonBencSerializer_get()->parseDictionary(reader, context.allocator, &config)) { fprintf(stderr, "Failed to parse configuration.\n"); return -1; } if (argc == 2 && strcmp(argv[1], "--getcmds") == 0) { return getcmds(&config); } if (argc == 2 && strcmp(argv[1], "--pidfile") == 0) { pidfile(&config); return 0; } char* user = setUser(Dict_getList(&config, BSTR("security"))); // Admin Dict* adminConf = Dict_getDict(&config, BSTR("admin")); if (adminConf) { admin(adminConf, user, &context); } // Logging struct Writer* logwriter = FileWriter_new(stdout, context.allocator); struct Log logger = { .writer = logwriter }; context.logger = &logger; struct Address myAddr; uint8_t privateKey[32]; parsePrivateKey(&config, &myAddr, privateKey); context.eHandler = AbortHandler_INSTANCE; context.switchCore = SwitchCore_new(context.logger, context.allocator); context.ca = CryptoAuth_new(&config, context.allocator, privateKey, context.base, context.logger); context.registry = DHTModules_new(context.allocator); ReplyModule_register(context.registry, context.allocator); // Router Dict* routerConf = Dict_getDict(&config, BSTR("router")); registerRouter(routerConf, myAddr.key, &context); SerializationModule_register(context.registry, context.allocator); // Authed passwords. List* authedPasswords = Dict_getList(&config, BSTR("authorizedPasswords")); if (authedPasswords) { authorizedPasswords(authedPasswords, &context); } // Interfaces. Dict* interfaces = Dict_getDict(&config, BSTR("interfaces")); Dict* udpConf = Dict_getDict(interfaces, BSTR("UDPInterface")); if (udpConf) { configureUDP(udpConf, &context); } if (udpConf == NULL) { fprintf(stderr, "No interfaces configured to connect to.\n"); return -1; } // pid file String* pidFile = Dict_getString(&config, BSTR("pidFile")); if (pidFile) { Log_info1(context.logger, "Writing pid of process to [%s].\n", pidFile->bytes); FILE* pf = fopen(pidFile->bytes, "w"); if (!pf) { Log_critical2(context.logger, "Failed to open pid file [%s] for writing, errno=%d\n", pidFile->bytes, errno); return -1; } fprintf(pf, "%d", getpid()); fclose(pf); } Ducttape_register(&config, privateKey, context.registry, context.routerModule, context.routerIf, context.switchCore, context.base, context.allocator, context.logger); uint8_t address[53]; Base32_encode(address, 53, myAddr.key, 32); Log_info1(context.logger, "Your address is: %s.k\n", address); uint8_t myIp[40]; Address_printIp(myIp, &myAddr); Log_info1(context.logger, "Your IPv6 address is: %s\n", myIp); // Security. security(Dict_getList(&config, BSTR("security")), context.logger, context.eHandler); event_base_loop(context.base, 0); // Never reached. return 0; }
static void randomBase32(uint8_t output[32]) { uint8_t bin[16]; randombytes(bin, 16); Base32_encode(output, 32, bin, 16); }
/** * Check the table for nodes which might need to be pinged, ping a node if necessary. * If a node has not responded in unresponsiveAfterMilliseconds then mark them as unresponsive * and if the connection is incoming and the node has not responded in forgetAfterMilliseconds * then drop them entirely. * This is called every PING_INTERVAL_MILLISECONDS but pingCallback is a misleading name. */ static void pingCallback(void* vic) { struct InterfaceController_pvt* ic = Identity_check((struct InterfaceController_pvt*) vic); if (!ic->peerMap.count) { return; } uint64_t now = Time_currentTimeMilliseconds(ic->eventBase); // scan for endpoints have not sent anything recently. uint32_t startAt = Random_uint32(ic->rand) % ic->peerMap.count; for (uint32_t i = startAt, count = 0; (!count || i != startAt) && count <= ic->peerMap.count;) { i = (i + 1) % ic->peerMap.count; count++; struct InterfaceController_Peer* ep = ic->peerMap.values[i]; if (now < ep->timeOfLastMessage + ic->pingAfterMilliseconds) { if (now < ep->timeOfLastPing + ic->pingAfterMilliseconds) { // Possibly an out-of-date node which is mangling packets, don't ping too often // because it causes the RumorMill to be filled with this node over and over. continue; } struct Node_Link* link = Router_linkForPath(ic->router, ep->switchLabel); // It exists, it's parent is the self-node, and it's label is equal to the switchLabel. if (link && Node_getBestParent(link->child) && Node_getBestParent(link->child)->parent->address.path == 1 && Node_getBestParent(link->child)->cannonicalLabel == ep->switchLabel) { continue; } } #ifdef Log_DEBUG uint8_t key[56]; Base32_encode(key, 56, CryptoAuth_getHerPublicKey(ep->cryptoAuthIf), 32); #endif if (ep->isIncomingConnection && now > ep->timeOfLastMessage + ic->forgetAfterMilliseconds) { Log_debug(ic->logger, "Unresponsive peer [%s.k] has not responded in [%u] " "seconds, dropping connection", key, ic->forgetAfterMilliseconds / 1024); Allocator_free(ep->external->allocator); continue; } bool unresponsive = (now > ep->timeOfLastMessage + ic->unresponsiveAfterMilliseconds); if (unresponsive) { // our link to the peer is broken... Router_disconnectedPeer(ic->router, ep->switchLabel); // Lets skip 87% of pings when they're really down. if (ep->pingCount % 8) { ep->pingCount++; continue; } ep->state = InterfaceController_PeerState_UNRESPONSIVE; } #ifdef Log_DEBUG uint32_t lag = (now - ep->timeOfLastMessage) / 1024; Log_debug(ic->logger, "Pinging %s peer [%s.k] lag [%u]", (unresponsive ? "unresponsive" : "lazy"), key, lag); #endif sendPing(ep); // we only ping one node return; } }
static void iciPing(struct InterfaceController_Iface_pvt* ici, struct InterfaceController_pvt* ic) { if (!ici->peerMap.count) { return; } uint64_t now = Time_currentTimeMilliseconds(ic->eventBase); // scan for endpoints have not sent anything recently. uint32_t startAt = Random_uint32(ic->rand) % ici->peerMap.count; for (uint32_t i = startAt, count = 0; count < ici->peerMap.count;) { i = (i + 1) % ici->peerMap.count; count++; struct Peer* ep = ici->peerMap.values[i]; if (now < ep->timeOfLastMessage + ic->pingAfterMilliseconds) { if (now < ep->timeOfLastPing + ic->pingAfterMilliseconds) { // Possibly an out-of-date node which is mangling packets, don't ping too often // because it causes the RumorMill to be filled with this node over and over. continue; } } uint8_t keyIfDebug[56]; if (Defined(Log_DEBUG)) { Base32_encode(keyIfDebug, 56, ep->caSession->herPublicKey, 32); } if (ep->isIncomingConnection && now > ep->timeOfLastMessage + ic->forgetAfterMilliseconds) { Log_debug(ic->logger, "Unresponsive peer [%s.k] has not responded in [%u] " "seconds, dropping connection", keyIfDebug, ic->forgetAfterMilliseconds / 1024); sendPeer(0xffffffff, PFChan_Core_PEER_GONE, ep); Allocator_free(ep->alloc); continue; } bool unresponsive = (now > ep->timeOfLastMessage + ic->unresponsiveAfterMilliseconds); if (unresponsive) { // our link to the peer is broken... // Lets skip 87% of pings when they're really down. if (ep->pingCount % 8) { ep->pingCount++; continue; } sendPeer(0xffffffff, PFChan_Core_PEER_GONE, ep); ep->state = InterfaceController_PeerState_UNRESPONSIVE; SwitchCore_setInterfaceState(&ep->switchIf, SwitchCore_setInterfaceState_ifaceState_DOWN); } Log_debug(ic->logger, "Pinging %s peer [%s.k] lag [%u]", (unresponsive ? "unresponsive" : "lazy"), keyIfDebug, (uint32_t)((now - ep->timeOfLastMessage) / 1024)); sendPing(ep); // we only ping one node return; } }
static int genconf() { uint8_t passwdBin[16]; randombytes(passwdBin, 16); uint8_t password[32]; Base32_encode(password, 32, passwdBin, 16); uint16_t port; randombytes((uint8_t*) &port, 2); uint8_t publicKeyBase32[53]; uint8_t address[40]; uint8_t privateKeyHex[65]; genAddress(address, privateKeyHex, publicKeyBase32); printf("{\n" " // Private key:\n" " // This key corrisponds to the public key: %s.k\n", publicKeyBase32); printf(" // And the ipv6 address: %s\n", address); printf(" // Your confidentiality and data integrity depend on this key, keep it secret!\n" " //\n" " \"privateKey\": \"%s\",\n", privateKeyHex); printf("\n" " // Anyone connecting and offering these passwords on connection will be allowed.\n" " //\n" " // WARNING: Currently there is no key derivation done on the password field,\n" " // DO NOT USE A PASSWORD HERE use something which is truly random and\n" " // cannot be guessed.\n" " // Including a username in the beginning of the password string is encouraged\n" " // to aid in remembering which users are who.\n" " //\n" " \"authorizedPasswords\":\n" " [\n" " {\n" " // A unique string which is known to the client and server.\n" " \"password\": \"%s\",\n", password); printf("\n" " // the authentication type, currently only 1 is supported.\n" " \"authType\": 1,\n" "\n" " // How much anti-flood trust to give a client\n" " // who connects with this password.\n" " \"trust\": 5000\n" " },\n" "\n" " /* These are your connection credentials\n" " for people connecting to you with your default password.\n" " adding more passwords for different users is advisable\n" " so that leaks can be isolated.\n" "\n" " \"your.external.ip.goes.here:%u\":\n", port); printf(" {\n" " \"password\": \"%s\",\n", password); printf(" \"authType\": 1,\n" " \"publicKey\": \"%s.k\",\n", publicKeyBase32); printf(" \"trust\": 10000\n" " }\n" " */\n" " ],\n" "\n" " // Settings for administering and extracting information from your router.\n" " // This interface provides API functions which can be called through a TCP socket.\n" " \"admin\":\n" " {\n" " // Port to bind the admin TCP server to.\n" " \"bind\": \"127.0.0.1:11234\"\n" " }\n" "\n" " // Interfaces to connect to the switch core.\n" " \"interfaces\":\n" " {\n" " // The interface which connects over UDP/IP based VPN tunnel.\n" " \"UDPInterface\":\n" " {\n" " // Bind to this port.\n" " \"bind\": \"0.0.0.0:%u\",\n", port); printf("\n" " // Nodes to connect to.\n" " \"connectTo\":\n" " {\n" " // Add connection credentials here to join the network\n" " // Ask somebody who is already connected.\n" " }\n" " }\n" " },\n" "\n" " // Configuration for the router.\n" " \"router\":\n" " {\n" " // The interface which is used for connecting to the cjdns network.\n" " \"interface\":\n" " {\n" " // The type of interface (only TUNInterface is supported for now)\n" " \"type\": \"TUNInterface\",\n" "\n" " // The name of the TUN device to use.\n" " // This allows you to create a persistent TUN device with the cjdns user\n" " // authorized to use it so that cjdns does not need to run as root.\n" " // If this is commented out, cjdns will try to allocate a TUN on startup.\n" " // If it can't do that (because it's not root?) then it will run as a\n" " // pure router, unable to send or receive traffic.\n" " \"tunDevice\": \"tun0\"\n" " }\n" " },\n" "\n" " // Tear down inactive CryptoAuth sessions after this number of seconds\n" " // to make them more forgiving in the event that they become desynchronized.\n" " \"resetAfterInactivitySeconds\": 30,\n" "\n" " // Dropping permissions.\n" " \"security\":\n" " [\n" " // Set number of open files to zero, in Linux, this will succeed even if\n" " // files are already open and will not allow any files to be opened for the\n" " // duration of the program's operation.\n" " // Most security exploits require the use of files.\n" " \"nofiles\",\n" "\n" " // Change the user id to this user after starting up and getting resources.\n" " {\"setuser\": \"nobody\"}\n" " ],\n" "\n" " // Version of the config file, used internally for migration.\n" " \"version\": 1\n" "}\n"); return 0; }
// Called from the pingInteral timeout. static void pingCallback(void* vic) { struct Context* ic = Identity_cast((struct Context*) vic); uint64_t now = Time_currentTimeMilliseconds(ic->eventBase); ic->pingCount++; // scan for endpoints have not sent anything recently. for (uint32_t i = 0; i < ic->peerMap.count; i++) { struct IFCPeer* ep = ic->peerMap.values[i]; // This is here because of a pathological state where the connection is in ESTABLISHED // state but the *direct peer* has somehow been dropped from the routing table. // TODO: understand the cause of this issue rather than checking for it once per second. struct Node* peerNode = RouterModule_getNode(ep->switchLabel, ic->routerModule); if (now > ep->timeOfLastMessage + ic->pingAfterMilliseconds || !peerNode) { #ifdef Log_DEBUG uint8_t key[56]; Base32_encode(key, 56, CryptoAuth_getHerPublicKey(ep->cryptoAuthIf), 32); #endif if (ep->isIncomingConnection && now > ep->timeOfLastMessage + ic->forgetAfterMilliseconds) { Log_debug(ic->logger, "Unresponsive peer [%s.k] has not responded in [%u] " "seconds, dropping connection", key, ic->forgetAfterMilliseconds / 1024); Allocator_free(ep->external->allocator); return; } bool unresponsive = (now > ep->timeOfLastMessage + ic->unresponsiveAfterMilliseconds); uint32_t lag = ~0u; if (unresponsive) { // flush the peer from the table... RouterModule_brokenPath(ep->switchLabel, ic->routerModule); // Lets skip 87% of pings when they're really down. if (ic->pingCount % 8) { continue; } ep->state = InterfaceController_PeerState_UNRESPONSIVE; lag = ((now - ep->timeOfLastMessage) / 1024); } else { lag = ((now - ep->timeOfLastMessage) / 1024); } struct SwitchPinger_Ping* ping = SwitchPinger_newPing(ep->switchLabel, String_CONST(""), ic->timeoutMilliseconds, onPingResponse, ic->allocator, ic->switchPinger); if (!ping) { Log_debug(ic->logger, "Failed to ping %s peer [%s.k] lag [%u], out of ping slots.", (unresponsive ? "unresponsive" : "lazy"), key, lag); return; } ping->onResponseContext = ep; SwitchPinger_sendPing(ping); Log_debug(ic->logger, "Pinging %s peer [%s.k] lag [%u]", (unresponsive ? "unresponsive" : "lazy"), key, lag); } } }