@@ -1092,6 +1092,12 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
10921092 if (!sendResponse)
10931093 return true ;
10941094
1095+ // Checked here, once a reply would actually go out, so declined requests do not consume the budget.
1096+ if (!directResponseAllowed (getFrom (p), clockMs ())) {
1097+ TM_LOG_DEBUG (" NodeInfo direct response throttled for 0x%08x" , getFrom (p));
1098+ return false ;
1099+ }
1100+
10951101 meshtastic_MeshPacket *reply = router->allocForSending ();
10961102 if (!reply) {
10971103 TM_LOG_WARN (" NodeInfo direct response dropped: no packet buffer" );
@@ -1144,6 +1150,43 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
11441150 return true ;
11451151}
11461152
1153+ bool TrafficManagementModule::directResponseAllowed (NodeNum requestor, uint32_t nowMs)
1154+ {
1155+ // Reached from the packet path and from runOnce, so the throttle state needs the same lock as the cache.
1156+ concurrency::LockGuard guard (&cacheLock);
1157+
1158+ if (lastDirectResponseMs != 0 && (nowMs - lastDirectResponseMs) < kDirectResponseGlobalMs )
1159+ return false ;
1160+
1161+ DirectResponseThrottleEntry *slot = nullptr ;
1162+ for (size_t i = 0 ; i < kDirectResponseTrackedRequestors ; i++) {
1163+ if (directResponseSeen[i].requestor == requestor) {
1164+ if ((nowMs - directResponseSeen[i].lastReplyMs ) < kDirectResponsePerRequestorMs )
1165+ return false ;
1166+ slot = &directResponseSeen[i];
1167+ break ;
1168+ }
1169+ }
1170+
1171+ if (slot == nullptr ) {
1172+ // Unseen requester: take a free slot, otherwise reuse the least recently used one.
1173+ slot = &directResponseSeen[0 ];
1174+ for (size_t i = 0 ; i < kDirectResponseTrackedRequestors ; i++) {
1175+ if (directResponseSeen[i].requestor == 0 ) {
1176+ slot = &directResponseSeen[i];
1177+ break ;
1178+ }
1179+ if (directResponseSeen[i].lastReplyMs < slot->lastReplyMs )
1180+ slot = &directResponseSeen[i];
1181+ }
1182+ }
1183+
1184+ slot->requestor = requestor;
1185+ slot->lastReplyMs = nowMs;
1186+ lastDirectResponseMs = nowMs;
1187+ return true ;
1188+ }
1189+
11471190bool TrafficManagementModule::isMinHopsFromRequestor (const meshtastic_MeshPacket *p) const
11481191{
11491192 int8_t hopsAway = getHopsAway (*p, -1 );
0 commit comments