|
@@ -19,6 +19,9 @@
|
|
|
#include "util/Identity.h"
|
|
|
#include "util/events/Timeout.h"
|
|
|
#include "util/AddrTools.h"
|
|
|
+#include "util/events/Time.h"
|
|
|
+
|
|
|
+#include <stdbool.h>
|
|
|
|
|
|
#define TIMEOUT_MILLISECONDS 10000
|
|
|
|
|
@@ -28,6 +31,20 @@ struct PeerInfo_pvt
|
|
|
|
|
|
// Next path to check when sending getPeers requests to our peer looking for ourselves.
|
|
|
uint64_t pathToCheck;
|
|
|
+
|
|
|
+ // For this 10 second period
|
|
|
+ uint32_t sumOfLag;
|
|
|
+ uint32_t lagSamples;
|
|
|
+ uint32_t timeOfLastLagUpdate;
|
|
|
+
|
|
|
+ uint32_t sumOfDropsLastSlot;
|
|
|
+ uint32_t sumOfPacketsLastSlot;
|
|
|
+ uint32_t sumOfKbLastSlot;
|
|
|
+
|
|
|
+ uint32_t sumOfDrops;
|
|
|
+ uint32_t sumOfPackets;
|
|
|
+ uint32_t sumOfKb;
|
|
|
+
|
|
|
// This peer is waiting for response
|
|
|
bool waitForResponse;
|
|
|
|
|
@@ -48,12 +65,23 @@ struct ReachabilityCollector_pvt
|
|
|
struct Log* log;
|
|
|
struct BoilerplateResponder* br;
|
|
|
struct Address* myAddr;
|
|
|
+ struct EventBase* base;
|
|
|
+ uint32_t resampleCycle;
|
|
|
|
|
|
struct ArrayList_OfPeerInfo_pvt* piList;
|
|
|
|
|
|
Identity
|
|
|
};
|
|
|
|
|
|
+static struct PeerInfo_pvt* piForLabel(struct ReachabilityCollector_pvt* rcp, uint64_t label)
|
|
|
+{
|
|
|
+ for (int j = 0; j < rcp->piList->length; j++) {
|
|
|
+ struct PeerInfo_pvt* pi0 = ArrayList_OfPeerInfo_pvt_get(rcp->piList, j);
|
|
|
+ if (pi0->pub.addr.path == label) { return pi0; }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
static void mkNextRequest(struct ReachabilityCollector_pvt* rcp);
|
|
|
|
|
|
static void change0(struct ReachabilityCollector_pvt* rcp,
|
|
@@ -62,25 +90,29 @@ static void change0(struct ReachabilityCollector_pvt* rcp,
|
|
|
{
|
|
|
for (int i = 0; i < rcp->piList->length; i++) {
|
|
|
struct PeerInfo_pvt* pi = ArrayList_OfPeerInfo_pvt_get(rcp->piList, i);
|
|
|
- if (Address_isSameIp(nodeAddr, &pi->pub.addr)) {
|
|
|
- if (nodeAddr->path == 0) {
|
|
|
- Log_debug(rcp->log, "Peer [%s] dropped",
|
|
|
- Address_toString(&pi->pub.addr, tempAlloc)->bytes);
|
|
|
- ArrayList_OfPeerInfo_pvt_remove(rcp->piList, i);
|
|
|
- Allocator_free(pi->alloc);
|
|
|
- } else if (nodeAddr->path != pi->pub.addr.path) {
|
|
|
- Log_debug(rcp->log, "Peer [%s] changed path",
|
|
|
- Address_toString(&pi->pub.addr, tempAlloc)->bytes);
|
|
|
- pi->pub.pathThemToUs = -1;
|
|
|
- pi->pathToCheck = 1;
|
|
|
- pi->pub.querying = true;
|
|
|
- pi->pub.addr.path = nodeAddr->path;
|
|
|
- }
|
|
|
- if (rcp->pub.onChange) {
|
|
|
- rcp->pub.onChange(&rcp->pub, nodeAddr->ip6.bytes, 0, 0, 0, 0xffff, 0xffff, 0xffff);
|
|
|
- }
|
|
|
- return;
|
|
|
+ if (!Address_isSameIp(nodeAddr, &pi->pub.addr)) { continue; }
|
|
|
+ if (nodeAddr->path == 0) {
|
|
|
+ Log_debug(rcp->log, "Peer [%s] dropped",
|
|
|
+ Address_toString(&pi->pub.addr, tempAlloc)->bytes);
|
|
|
+ ArrayList_OfPeerInfo_pvt_remove(rcp->piList, i);
|
|
|
+ Allocator_free(pi->alloc);
|
|
|
+ rcp->pub.onChange(&rcp->pub, nodeAddr->ip6.bytes, 0, 0);
|
|
|
+ } else if (nodeAddr->path != pi->pub.addr.path) {
|
|
|
+ Log_debug(rcp->log, "Peer [%s] changed path",
|
|
|
+ Address_toString(&pi->pub.addr, tempAlloc)->bytes);
|
|
|
+ pi->pub.pathThemToUs = -1;
|
|
|
+ pi->pathToCheck = 1;
|
|
|
+ pi->pub.querying = true;
|
|
|
+ pi->pub.addr.path = nodeAddr->path;
|
|
|
+ //rcp->pub.onChange(
|
|
|
+ // &rcp->pub, nodeAddr->ip6.bytes, pi->pub.pathThemToUs, nodeAddr->path);
|
|
|
+ // Lets leave the peer in the list as working, our path to it changed
|
|
|
+ // but it's path to us didn't necessarily change.
|
|
|
+ } else {
|
|
|
+ Log_debug(rcp->log, "Peer [%s] message, peer already registered",
|
|
|
+ Address_toString(&pi->pub.addr, tempAlloc)->bytes);
|
|
|
}
|
|
|
+ return;
|
|
|
}
|
|
|
if (nodeAddr->path == 0) {
|
|
|
Log_debug(rcp->log, "Nonexistant peer [%s] dropped",
|
|
@@ -130,6 +162,17 @@ static void onReplyTimeout(struct MsgCore_Promise* prom)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void latencyUpdate(
|
|
|
+ struct ReachabilityCollector_pvt* rcp,
|
|
|
+ struct PeerInfo_pvt* pip,
|
|
|
+ uint32_t lag)
|
|
|
+{
|
|
|
+ Log_debug(rcp->log, "Latency update for [%016x] [%u]ms", pip->pub.addr.path, lag);
|
|
|
+ pip->sumOfLag += lag;
|
|
|
+ pip->lagSamples++;
|
|
|
+ pip->timeOfLastLagUpdate = Time_currentTimeMilliseconds(rcp->base);
|
|
|
+}
|
|
|
+
|
|
|
static void onReply(Dict* msg, struct Address* src, struct MsgCore_Promise* prom)
|
|
|
{
|
|
|
struct Query* q = (struct Query*) prom->userData;
|
|
@@ -153,6 +196,7 @@ static void onReply(Dict* msg, struct Address* src, struct MsgCore_Promise* prom
|
|
|
Log_debug(rcp->log, "Got message from peer which is gone from list");
|
|
|
return;
|
|
|
}
|
|
|
+ latencyUpdate(rcp, pi, prom->lag);
|
|
|
|
|
|
pi->waitForResponse = false;
|
|
|
|
|
@@ -173,10 +217,7 @@ static void onReply(Dict* msg, struct Address* src, struct MsgCore_Promise* prom
|
|
|
Log_debug(rcp->log, "Found back-route for [%s]",
|
|
|
Address_toString(src, prom->alloc)->bytes);
|
|
|
pi->pub.pathThemToUs = path;
|
|
|
- if (rcp->pub.onChange) {
|
|
|
- rcp->pub.onChange(
|
|
|
- &rcp->pub, src->ip6.bytes, path, src->path, 0, 0xffff, 0xffff, 0xffff);
|
|
|
- }
|
|
|
+ rcp->pub.onChange(&rcp->pub, src->ip6.bytes, path, src->path);
|
|
|
}
|
|
|
pi->pub.querying = false;
|
|
|
mkNextRequest(rcp);
|
|
@@ -193,21 +234,8 @@ static void onReply(Dict* msg, struct Address* src, struct MsgCore_Promise* prom
|
|
|
mkNextRequest(rcp);
|
|
|
}
|
|
|
|
|
|
-static void mkNextRequest(struct ReachabilityCollector_pvt* rcp)
|
|
|
+static void queryPeer(struct ReachabilityCollector_pvt* rcp, struct PeerInfo_pvt* pi)
|
|
|
{
|
|
|
- struct PeerInfo_pvt* pi = NULL;
|
|
|
- for (int i = 0; i < rcp->piList->length; i++) {
|
|
|
- pi = ArrayList_OfPeerInfo_pvt_get(rcp->piList, i);
|
|
|
- if (pi->pub.querying && !pi->waitForResponse) { break; }
|
|
|
- }
|
|
|
- if (!pi || !pi->pub.querying) {
|
|
|
- Log_debug(rcp->log, "All [%u] peers have been queried", rcp->piList->length);
|
|
|
- return;
|
|
|
- }
|
|
|
- if (pi->waitForResponse) {
|
|
|
- Log_debug(rcp->log, "Peer is waiting for response.");
|
|
|
- return;
|
|
|
- }
|
|
|
struct MsgCore_Promise* query =
|
|
|
MsgCore_createQuery(rcp->msgCore, TIMEOUT_MILLISECONDS, rcp->alloc);
|
|
|
struct Query* q = Allocator_calloc(query->alloc, sizeof(struct Query), 1);
|
|
@@ -217,6 +245,7 @@ static void mkNextRequest(struct ReachabilityCollector_pvt* rcp)
|
|
|
query->cb = onReply;
|
|
|
Assert_true(AddressCalc_validAddress(pi->pub.addr.ip6.bytes));
|
|
|
query->target = Address_clone(&pi->pub.addr, query->alloc);
|
|
|
+ Assert_true(pi->pub.addr.path);
|
|
|
Dict* d = query->msg = Dict_new(query->alloc);
|
|
|
Dict_putStringCC(d, "q", "gp", query->alloc);
|
|
|
uint64_t label_be = Endian_hostToBigEndian64(pi->pathToCheck);
|
|
@@ -233,10 +262,66 @@ static void mkNextRequest(struct ReachabilityCollector_pvt* rcp)
|
|
|
pi->waitForResponse = true;
|
|
|
}
|
|
|
|
|
|
+static void mkNextRequest(struct ReachabilityCollector_pvt* rcp)
|
|
|
+{
|
|
|
+ struct PeerInfo_pvt* pi = NULL;
|
|
|
+ for (int i = 0; i < rcp->piList->length; i++) {
|
|
|
+ pi = ArrayList_OfPeerInfo_pvt_get(rcp->piList, i);
|
|
|
+ if (pi->pub.querying && !pi->waitForResponse) { break; }
|
|
|
+ }
|
|
|
+ if (!pi || !pi->pub.querying) {
|
|
|
+ Log_debug(rcp->log, "All [%u] peers have been queried", rcp->piList->length);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (pi->waitForResponse) {
|
|
|
+ Log_debug(rcp->log, "Peer is waiting for response.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ queryPeer(rcp, pi);
|
|
|
+}
|
|
|
+
|
|
|
static void cycle(void* vrc)
|
|
|
{
|
|
|
struct ReachabilityCollector_pvt* rcp = Identity_check((struct ReachabilityCollector_pvt*) vrc);
|
|
|
mkNextRequest(rcp);
|
|
|
+
|
|
|
+ // 10 second window is cut into 5 intervals
|
|
|
+ // second 0, 2, 4, 6, 8
|
|
|
+ // number 1, 2, 3, 4, 5
|
|
|
+ // in number 4, we will ping any peer who has not received one yet to get latency
|
|
|
+ // in number 5, we will collect everything back
|
|
|
+ rcp->resampleCycle++;
|
|
|
+ if (rcp->resampleCycle < 4) { return; }
|
|
|
+
|
|
|
+ for (int j = 0; j < rcp->piList->length; j++) {
|
|
|
+ struct PeerInfo_pvt* pi = ArrayList_OfPeerInfo_pvt_get(rcp->piList, j);
|
|
|
+ Log_debug(rcp->log, "Visiting peer [%016x] samples [%u]",
|
|
|
+ pi->pub.addr.path, pi->lagSamples);
|
|
|
+ if (pi->lagSamples == 0) {
|
|
|
+ Log_debug(rcp->log, "Triggering a ping to peer [%016x]", pi->pub.addr.path);
|
|
|
+ queryPeer(rcp, pi);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rcp->resampleCycle < 5) { continue; }
|
|
|
+
|
|
|
+ int sampleNum = pi->pub.linkState.samples % ReachabilityCollector_SLOTS;
|
|
|
+
|
|
|
+ uint64_t drops = pi->sumOfDrops - pi->sumOfDropsLastSlot;
|
|
|
+ uint64_t packets = pi->sumOfPackets - pi->sumOfPacketsLastSlot;
|
|
|
+ uint64_t dropRateShl18 = packets ? (drops << 18) / packets : 0;
|
|
|
+ pi->pub.linkState.dropSlots[sampleNum] = dropRateShl18 > 0xfffe ? 0xfffe : dropRateShl18;
|
|
|
+ pi->sumOfDropsLastSlot = pi->sumOfDrops;
|
|
|
+
|
|
|
+ pi->pub.linkState.kbRecvSlots[sampleNum] = pi->sumOfKb - pi->sumOfKbLastSlot;
|
|
|
+ pi->sumOfKbLastSlot = pi->sumOfKb;
|
|
|
+
|
|
|
+ pi->pub.linkState.lagSlots[sampleNum] = pi->lagSamples ? pi->sumOfLag / pi->lagSamples : 0;
|
|
|
+ pi->sumOfLag = 0;
|
|
|
+ pi->lagSamples = 0;
|
|
|
+
|
|
|
+ pi->pub.linkState.samples++;
|
|
|
+ }
|
|
|
+ if (rcp->resampleCycle >= 5) { rcp->resampleCycle = 0; }
|
|
|
}
|
|
|
|
|
|
struct ReachabilityCollector_PeerInfo*
|
|
@@ -247,6 +332,40 @@ struct ReachabilityCollector_PeerInfo*
|
|
|
return pi ? &pi->pub : NULL;
|
|
|
}
|
|
|
|
|
|
+void ReachabilityCollector_lagSample(
|
|
|
+ struct ReachabilityCollector* rc, uint64_t label, uint32_t milliseconds)
|
|
|
+{
|
|
|
+ struct ReachabilityCollector_pvt* rcp = Identity_check((struct ReachabilityCollector_pvt*) rc);
|
|
|
+ struct PeerInfo_pvt* pi = piForLabel(rcp, label);
|
|
|
+ if (!pi) { return; }
|
|
|
+ latencyUpdate(rcp, pi, milliseconds);
|
|
|
+}
|
|
|
+
|
|
|
+void ReachabilityCollector_updateBandwidthAndDrops(
|
|
|
+ struct ReachabilityCollector* rc,
|
|
|
+ uint64_t label,
|
|
|
+ uint32_t sumOfPackets,
|
|
|
+ uint32_t sumOfDrops,
|
|
|
+ uint32_t sumOfKb)
|
|
|
+{
|
|
|
+ struct ReachabilityCollector_pvt* rcp = Identity_check((struct ReachabilityCollector_pvt*) rc);
|
|
|
+ struct PeerInfo_pvt* pi = piForLabel(rcp, label);
|
|
|
+ if (!pi) { return; }
|
|
|
+ pi->sumOfPackets = sumOfPackets;
|
|
|
+ pi->sumOfDrops = sumOfDrops;
|
|
|
+ pi->sumOfKb = sumOfKb;
|
|
|
+}
|
|
|
+
|
|
|
+static void dummyOnChange(
|
|
|
+ struct ReachabilityCollector* rc,
|
|
|
+ uint8_t nodeIpv6[16],
|
|
|
+ uint32_t pathThemToUs,
|
|
|
+ uint32_t pathUsToThem)
|
|
|
+{
|
|
|
+ struct ReachabilityCollector_pvt* rcp = Identity_check((struct ReachabilityCollector_pvt*) rc);
|
|
|
+ Log_debug(rcp->log, "dummyOnChange called, onChange unassigned");
|
|
|
+}
|
|
|
+
|
|
|
struct ReachabilityCollector* ReachabilityCollector_new(struct Allocator* allocator,
|
|
|
struct MsgCore* msgCore,
|
|
|
struct Log* log,
|
|
@@ -263,6 +382,8 @@ struct ReachabilityCollector* ReachabilityCollector_new(struct Allocator* alloca
|
|
|
rcp->piList = ArrayList_OfPeerInfo_pvt_new(alloc);
|
|
|
rcp->log = log;
|
|
|
rcp->br = br;
|
|
|
+ rcp->base = base;
|
|
|
+ rcp->pub.onChange = dummyOnChange;
|
|
|
Identity_set(rcp);
|
|
|
Timeout_setInterval(cycle, rcp, 2000, base, alloc);
|
|
|
return &rcp->pub;
|