Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/modules/TrafficManagementModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,12 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
if (!sendResponse)
return true;

// Checked here, once a reply would actually go out, so declined requests do not consume the budget.
if (!directResponseAllowed(getFrom(p), clockMs())) {
TM_LOG_DEBUG("NodeInfo direct response throttled for 0x%08x", getFrom(p));
return false;
}

meshtastic_MeshPacket *reply = router->allocForSending();
if (!reply) {
TM_LOG_WARN("NodeInfo direct response dropped: no packet buffer");
Expand Down Expand Up @@ -1144,6 +1150,43 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
return true;
}

bool TrafficManagementModule::directResponseAllowed(NodeNum requestor, uint32_t nowMs)
{
// Reached from the packet path and from runOnce, so the throttle state needs the same lock as the cache.
concurrency::LockGuard guard(&cacheLock);

if (lastDirectResponseMs != 0 && (nowMs - lastDirectResponseMs) < kDirectResponseGlobalMs)
return false;

DirectResponseThrottleEntry *slot = nullptr;
for (size_t i = 0; i < kDirectResponseTrackedRequestors; i++) {
if (directResponseSeen[i].requestor == requestor) {
if ((nowMs - directResponseSeen[i].lastReplyMs) < kDirectResponsePerRequestorMs)
return false;
slot = &directResponseSeen[i];
break;
}
}

if (slot == nullptr) {
// Unseen requester: take a free slot, otherwise reuse the least recently used one.
slot = &directResponseSeen[0];
for (size_t i = 0; i < kDirectResponseTrackedRequestors; i++) {
if (directResponseSeen[i].requestor == 0) {
slot = &directResponseSeen[i];
break;
}
if (directResponseSeen[i].lastReplyMs < slot->lastReplyMs)
slot = &directResponseSeen[i];
}
}

slot->requestor = requestor;
slot->lastReplyMs = nowMs;
lastDirectResponseMs = nowMs;
return true;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

bool TrafficManagementModule::isMinHopsFromRequestor(const meshtastic_MeshPacket *p) const
{
int8_t hopsAway = getHopsAway(*p, -1);
Expand Down
13 changes: 13 additions & 0 deletions src/modules/TrafficManagementModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,19 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread

bool shouldDropPosition(const meshtastic_MeshPacket *p, const meshtastic_Position *pos, uint32_t nowMs);
bool shouldRespondToNodeInfo(const meshtastic_MeshPacket *p, bool sendResponse);

// Replies go to the requesting packet's unauthenticated `from`, so space them per requester to bound
// what any one node can be made to receive, plus a global floor on airtime.
static constexpr uint32_t kDirectResponsePerRequestorMs = 60'000UL;
static constexpr uint32_t kDirectResponseGlobalMs = 1'000UL;
static constexpr size_t kDirectResponseTrackedRequestors = 8;
struct DirectResponseThrottleEntry {
NodeNum requestor;
uint32_t lastReplyMs;
};
DirectResponseThrottleEntry directResponseSeen[kDirectResponseTrackedRequestors] = {};
uint32_t lastDirectResponseMs = 0;
bool directResponseAllowed(NodeNum requestor, uint32_t nowMs);
bool isMinHopsFromRequestor(const meshtastic_MeshPacket *p) const;
bool isRateLimited(NodeNum from, uint32_t nowMs);
bool shouldDropUnknown(const meshtastic_MeshPacket *p, uint32_t nowMs);
Expand Down
Loading