Specialize repeater ACL session state, compact recent and flood records, stream Merkle proofs, and release cold OTA buffers safely. Preserve saved formats, stack sizes, table capacities, and OTA source limits. Account for lazy buffers when active and add compatibility, ARM alignment, allocation failure, and queued-response tests. Validated six representative board/role builds, 1736 native test cases, sanitizer fixtures, and strict documentation. Document the preexisting Wio-E5 Mini flash overflow separately.
111 lines
4.0 KiB
C++
111 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include "RoomServerFeatures.h"
|
|
|
|
#if MESH_ENABLE_ROOM_FLOOD_RULE_ENGINE
|
|
|
|
#include <Arduino.h>
|
|
#include <Mesh.h>
|
|
#include <new>
|
|
|
|
#include <helpers/IdentityStore.h>
|
|
#include <helpers/RegionMap.h>
|
|
|
|
class FloodRuleEngine {
|
|
public:
|
|
static constexpr uint8_t RULE_SLOTS = 31;
|
|
static constexpr uint8_t ANY_TYPE = 0xFF;
|
|
static constexpr uint8_t MAX_HOPS = 63;
|
|
static constexpr uint8_t NAME_LEN = 32;
|
|
static constexpr uint8_t PATH_PREFIX_HOPS_MAX = 3;
|
|
static constexpr uint8_t PATH_PREFIX_BYTES_MAX = 9;
|
|
static constexpr uint16_t RATE_UNLIMITED = 0xFFFF;
|
|
|
|
FloodRuleEngine();
|
|
|
|
void begin(FILESYSTEM* fs, RegionMap* regions);
|
|
|
|
uint32_t evaluate(const mesh::Packet* packet, bool temp_radio_active,
|
|
bool incoming_region_allowed,
|
|
const RegionEntry* incoming_region, uint8_t context = 0);
|
|
bool applyScope(mesh::Packet* packet, uint32_t match_mask,
|
|
bool& scope_set, bool& fast_track,
|
|
bool log_change = true);
|
|
bool shouldBlock(const mesh::Packet* packet, uint32_t match_mask,
|
|
uint32_t now_millis) const;
|
|
void commitRates(const mesh::Packet* packet, uint32_t match_mask,
|
|
uint32_t now_millis);
|
|
bool hasRetryRules() const;
|
|
bool allowsRetry(uint32_t match_mask) const;
|
|
|
|
bool handleCommand(const char* command, char* reply);
|
|
|
|
private:
|
|
struct Entry {
|
|
// Native alignment for counters; explicit FPF7 serialization does not
|
|
// expose the in-memory flag bit order. Keep active/retry addressable for
|
|
// the stored rule decoders, and mutate rules only on the mesh loop.
|
|
uint32_t rate_window_started;
|
|
uint16_t rate_per_minute;
|
|
uint16_t rate_window_count;
|
|
bool suspend_on_temp_radio : 1;
|
|
bool match_blacklisted_path : 1;
|
|
bool scope_uses_slow_timing : 1;
|
|
bool drop_on_match : 1;
|
|
bool rate_limit_enabled : 1;
|
|
bool stop_on_match : 1;
|
|
bool rate_window_active : 1;
|
|
bool active;
|
|
bool retry_on_match;
|
|
uint8_t payload_type;
|
|
uint8_t min_hops;
|
|
uint8_t max_hops;
|
|
char scope_name[NAME_LEN];
|
|
uint8_t incoming_scope_kind;
|
|
char incoming_scope_name[NAME_LEN];
|
|
uint8_t channel_key_len;
|
|
uint8_t channel_hash;
|
|
uint8_t channel_secret[PUB_KEY_SIZE];
|
|
char channel_name[NAME_LEN];
|
|
uint8_t path_hash_size;
|
|
uint8_t path_hops;
|
|
uint8_t path[PATH_PREFIX_BYTES_MAX];
|
|
char target_region_name[NAME_LEN];
|
|
uint8_t priority;
|
|
uint8_t transport_modes;
|
|
};
|
|
|
|
// This table is at most 5,952 bytes. Keep it in the constructor-time heap rather
|
|
// than inside the global MyMesh object: classic ESP32 has a small static
|
|
// DRAM window but substantially more internal heap at this point in boot.
|
|
// Allocation is deliberately all-or-nothing. A node must never fall back
|
|
// to silently running without its configured flood rules.
|
|
static_assert(sizeof(Entry) <= 192,
|
|
"Update the flood-rule runtime RAM budget in check_firmware_ram.py");
|
|
FILESYSTEM* _fs;
|
|
RegionMap* _regions;
|
|
Entry* _entries;
|
|
|
|
void seedDefaults();
|
|
void load();
|
|
bool save();
|
|
bool fieldsMatch(const Entry& entry, const mesh::Packet* packet,
|
|
bool temp_radio_active, bool incoming_is_scoped,
|
|
uint16_t incoming_transport_code,
|
|
bool incoming_region_allowed,
|
|
const RegionEntry* incoming_region, uint8_t context) const;
|
|
bool authenticateChannel(const Entry& entry,
|
|
const mesh::Packet* packet) const;
|
|
int nextMatch(uint32_t match_mask, uint32_t visited_mask) const;
|
|
bool resolveTargetRegion(const char* name, TransportKey& scope,
|
|
const char*& canonical_name);
|
|
uint32_t applyStop(uint32_t match_mask);
|
|
void format(const char* args, char* reply, bool compact = false) const;
|
|
void formatDetail(int index, char* reply, size_t reply_len, bool compact = false) const;
|
|
void set(const char* args, char* reply,
|
|
bool require_explicit_action = false);
|
|
void remove(const char* args, char* reply);
|
|
};
|
|
|
|
#endif
|