mirror of https://github.com/meshcore-dev/MeshCore
21 changed files with 1238 additions and 14 deletions
@ -0,0 +1,37 @@ |
|||||
|
#include "GroupChannel.h" |
||||
|
#include "Utils.h" |
||||
|
|
||||
|
#define DEFAULT_PUBLIC_CHANNEL_SECRET_HEX "8b3387e9c5cdea6ac9e5edbaa115cd72" |
||||
|
|
||||
|
namespace mesh { |
||||
|
|
||||
|
bool GroupChannel::deriveHash(int secret_len) { |
||||
|
if (secret_len != 16 && secret_len != 32) return false; |
||||
|
|
||||
|
Utils::sha256(hash, PATH_HASH_SIZE, secret, secret_len); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void GroupChannel::deriveHash() { |
||||
|
static uint8_t zeroes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
||||
|
if (memcmp(&secret[16], zeroes, 16) == 0) { |
||||
|
deriveHash(16); |
||||
|
} else { |
||||
|
deriveHash(32); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool GroupChannel::derivePublicSecret(const char* name, uint8_t* dest_secret) { |
||||
|
if (name == NULL || dest_secret == NULL) return false; |
||||
|
|
||||
|
memset(dest_secret, 0, PUB_KEY_SIZE); |
||||
|
|
||||
|
if (strcmp(name, "Public") == 0) { |
||||
|
return Utils::fromHex(dest_secret, 16, DEFAULT_PUBLIC_CHANNEL_SECRET_HEX); |
||||
|
} |
||||
|
|
||||
|
Utils::sha256(dest_secret, 16, (const uint8_t*)name, strlen(name)); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <MeshCore.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
namespace mesh { |
||||
|
|
||||
|
class GroupChannel { |
||||
|
public: |
||||
|
uint8_t hash[PATH_HASH_SIZE]; |
||||
|
uint8_t secret[PUB_KEY_SIZE]; |
||||
|
|
||||
|
void deriveHash(); |
||||
|
bool deriveHash(int secret_len); |
||||
|
static bool derivePublicSecret(const char* name, uint8_t* dest_secret); |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,281 @@ |
|||||
|
#include "PacketFilter.h" |
||||
|
|
||||
|
#include <stdlib.h> |
||||
|
|
||||
|
static File openPacketFilterRead(FILESYSTEM* fs, const char* path) { |
||||
|
#if defined(RP2040_PLATFORM) |
||||
|
return fs->open(path, "r"); |
||||
|
#else |
||||
|
return fs->open(path); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
static File openPacketFilterWrite(FILESYSTEM* fs, const char* path) { |
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
fs->remove(path); |
||||
|
return fs->open(path, FILE_O_WRITE); |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
return fs->open(path, "w"); |
||||
|
#else |
||||
|
return fs->open(path, "w", true); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
static bool appendRuleLine(char* reply, size_t reply_len, size_t* used, const char* line) { |
||||
|
int written = snprintf(&reply[*used], reply_len - *used, "%s%s", *used == 0 ? "" : "\n", line); |
||||
|
if (written < 0) return false; |
||||
|
if ((size_t)written >= reply_len - *used) { |
||||
|
reply[reply_len - 1] = 0; |
||||
|
return false; |
||||
|
} |
||||
|
*used += written; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
static bool canAppendPagedLine(size_t used, size_t reply_len, const char* line) { |
||||
|
const size_t continuation_reserve = 8; // "\n..255" plus terminator
|
||||
|
size_t line_len = strlen(line); |
||||
|
size_t needed = line_len + (used == 0 ? 0 : 1); |
||||
|
|
||||
|
return reply_len > continuation_reserve && needed < reply_len - continuation_reserve - used; |
||||
|
} |
||||
|
|
||||
|
static void consumePagedLine(size_t* used, const char* line) { |
||||
|
*used += strlen(line) + (*used == 0 ? 0 : 1); |
||||
|
} |
||||
|
|
||||
|
static bool appendContinuation(char* reply, size_t reply_len, size_t* used, uint8_t next_page) { |
||||
|
char line[8]; |
||||
|
snprintf(line, sizeof(line), "..%u", next_page); |
||||
|
return appendRuleLine(reply, reply_len, used, line); |
||||
|
} |
||||
|
|
||||
|
PacketFilter::PacketFilter(mesh::RNG* rng) { |
||||
|
_rule_count = 0; |
||||
|
_filtered_count = 0; |
||||
|
_rng = rng; |
||||
|
} |
||||
|
|
||||
|
bool PacketFilter::shouldRepeatPacket(const mesh::Packet* packet) { |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_MAX_RULES; i++) { |
||||
|
if (!_rules[i].shouldRepeatPacket(packet)) { |
||||
|
_filtered_count++; |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool PacketFilter::block(const char* classifier_name, char* args) { |
||||
|
int empty_rule_index = -1; |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_MAX_RULES; i++) { |
||||
|
if (!_rules[i].isActive()) { |
||||
|
empty_rule_index = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (empty_rule_index < 0) return false; |
||||
|
|
||||
|
PacketFilterRule& new_rule = _rules[empty_rule_index]; |
||||
|
if (!new_rule.configure(classifier_name, args, nullptr, _rng)) return false; |
||||
|
|
||||
|
char new_rule_text[160]; |
||||
|
char existing_rule_text[160]; |
||||
|
new_rule.formatRule(new_rule_text, sizeof(new_rule_text)); |
||||
|
|
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_MAX_RULES; i++) { |
||||
|
if (i == empty_rule_index || !_rules[i].isActive()) continue; |
||||
|
|
||||
|
_rules[i].formatRule(existing_rule_text, sizeof(existing_rule_text)); |
||||
|
if (strcmp(new_rule_text, existing_rule_text) == 0) { |
||||
|
new_rule.clear(); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_rule_count++; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool PacketFilter::deleteRule(uint8_t rule_index) { |
||||
|
if (rule_index == 0 || rule_index > PACKET_FILTER_MAX_RULES) return false; |
||||
|
|
||||
|
PacketFilterRule& rule = _rules[rule_index - 1]; |
||||
|
if (!rule.isActive()) return false; |
||||
|
|
||||
|
rule.clear(); |
||||
|
if (_rule_count > 0) _rule_count--; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void PacketFilter::clear() { |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_MAX_RULES; i++) { |
||||
|
_rules[i].clear(); |
||||
|
} |
||||
|
_rule_count = 0; |
||||
|
} |
||||
|
|
||||
|
void PacketFilter::formatRules(char* reply, size_t reply_len, uint8_t page) const { |
||||
|
if (reply_len == 0) return; |
||||
|
reply[0] = 0; |
||||
|
|
||||
|
if (page == 0) page = 1; |
||||
|
|
||||
|
size_t used = 0; |
||||
|
uint8_t current_page = 1; |
||||
|
bool has_active_rules = false; |
||||
|
bool has_page_rules = false; |
||||
|
char rule_text[128]; |
||||
|
char line[144]; |
||||
|
|
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_MAX_RULES; i++) { |
||||
|
if (!_rules[i].isActive()) continue; |
||||
|
has_active_rules = true; |
||||
|
|
||||
|
_rules[i].formatRule(rule_text, sizeof(rule_text)); |
||||
|
snprintf(line, sizeof(line), "%u: %s", (uint32_t)i + 1, rule_text); |
||||
|
|
||||
|
while (!canAppendPagedLine(used, reply_len, line)) { |
||||
|
if (current_page == page) { |
||||
|
appendContinuation(reply, reply_len, &used, current_page + 1); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
current_page++; |
||||
|
used = 0; |
||||
|
} |
||||
|
|
||||
|
if (current_page == page) { |
||||
|
if (!appendRuleLine(reply, reply_len, &used, line)) return; |
||||
|
has_page_rules = true; |
||||
|
} else { |
||||
|
consumePagedLine(&used, line); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!has_active_rules) { |
||||
|
strncpy(reply, "-none-", reply_len - 1); |
||||
|
reply[reply_len - 1] = 0; |
||||
|
} else if (!has_page_rules) { |
||||
|
snprintf(reply, reply_len, "Err - no such page"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void formatBlockHelp(char* reply, size_t reply_len) { |
||||
|
snprintf(reply, reply_len, "block list [page]\nblock del <index>\nblock clear\nblock stats\nblock advert help\nblock channelmessage help"); |
||||
|
} |
||||
|
|
||||
|
static bool addBlockRule(void* context, const char* classifier_name, char* args) { |
||||
|
if (context == NULL) return false; |
||||
|
return ((PacketFilter*)context)->block(classifier_name, args); |
||||
|
} |
||||
|
|
||||
|
void PacketFilter::handleBlockCommand(FILESYSTEM* fs, const char* path, char* command, char* reply, size_t reply_len) { |
||||
|
if (reply_len == 0) return; |
||||
|
|
||||
|
char* args = command + 5; |
||||
|
char* rule = BasePacketFilterClassifier::nextToken(args); |
||||
|
|
||||
|
bool should_save = false; |
||||
|
if (rule == NULL || strcmp(rule, "help") == 0) { |
||||
|
formatBlockHelp(reply, reply_len); |
||||
|
return; |
||||
|
} else if (strcmp(rule, "list") == 0) { |
||||
|
char* page_token = BasePacketFilterClassifier::nextToken(args); |
||||
|
char* extra = BasePacketFilterClassifier::nextToken(args); |
||||
|
unsigned long page = 1; |
||||
|
if (extra != NULL || (page_token != NULL && !BasePacketFilterClassifier::parseUnsigned(page_token, 1, 255, &page))) { |
||||
|
snprintf(reply, reply_len, "Usage: block list [page]"); |
||||
|
return; |
||||
|
} |
||||
|
formatRules(reply, reply_len, (uint8_t)page); |
||||
|
return; |
||||
|
} else if (strcmp(rule, "del") == 0) { |
||||
|
char* index_token = BasePacketFilterClassifier::nextToken(args); |
||||
|
char* extra = BasePacketFilterClassifier::nextToken(args); |
||||
|
unsigned long rule_index = 0; |
||||
|
if (extra != NULL || !BasePacketFilterClassifier::parseUnsigned(index_token, 1, PACKET_FILTER_MAX_RULES, &rule_index)) { |
||||
|
snprintf(reply, reply_len, "Usage: block del <index>"); |
||||
|
return; |
||||
|
} |
||||
|
if (deleteRule((uint8_t)rule_index)) { |
||||
|
snprintf(reply, reply_len, "OK - packet filter rule deleted"); |
||||
|
should_save = true; |
||||
|
} else { |
||||
|
snprintf(reply, reply_len, "Err - packet filter rule not found"); |
||||
|
} |
||||
|
} else if (strcmp(rule, "clear") == 0) { |
||||
|
clear(); |
||||
|
snprintf(reply, reply_len, "OK - packet filter rules cleared"); |
||||
|
should_save = true; |
||||
|
} else if (strcmp(rule, "stats") == 0) { |
||||
|
snprintf(reply, reply_len, "Filtered packets: %u", _filtered_count); |
||||
|
} else if (strcmp(rule, "channelmessage") == 0) { |
||||
|
should_save = ChannelMessageClassifier::handleBlockCommand(args, reply, reply_len, addBlockRule, this); |
||||
|
} else if (strcmp(rule, "advert") == 0) { |
||||
|
should_save = AdvertRateLimitClassifier::handleBlockCommand(args, reply, reply_len, addBlockRule, this); |
||||
|
} else { |
||||
|
snprintf(reply, reply_len, "Err - unknown block rule"); |
||||
|
} |
||||
|
|
||||
|
if (should_save && !save(fs, path)) { |
||||
|
snprintf(reply, reply_len, "Err - packet filter rule save failed"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void loadPacketFilterRuleLine(PacketFilter* packet_filter, char* line) { |
||||
|
char* args = line; |
||||
|
char* classifier_name = BasePacketFilterClassifier::nextToken(args); |
||||
|
if (classifier_name == NULL) return; |
||||
|
|
||||
|
packet_filter->block(classifier_name, args); |
||||
|
} |
||||
|
|
||||
|
bool PacketFilter::load(FILESYSTEM* fs, const char* path) { |
||||
|
clear(); |
||||
|
if (fs == NULL || path == NULL || !fs->exists(path)) return true; |
||||
|
|
||||
|
File file = openPacketFilterRead(fs, path); |
||||
|
if (!file) return false; |
||||
|
|
||||
|
char line[160]; |
||||
|
size_t line_len = 0; |
||||
|
|
||||
|
while (file.available()) { |
||||
|
int c = file.read(); |
||||
|
if (c == '\r') continue; |
||||
|
if (c == '\n') { |
||||
|
line[line_len] = 0; |
||||
|
loadPacketFilterRuleLine(this, line); |
||||
|
line_len = 0; |
||||
|
} else if (line_len < sizeof(line) - 1) { |
||||
|
line[line_len++] = (char)c; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (line_len > 0) { |
||||
|
line[line_len] = 0; |
||||
|
loadPacketFilterRuleLine(this, line); |
||||
|
} |
||||
|
|
||||
|
file.close(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool PacketFilter::save(FILESYSTEM* fs, const char* path) const { |
||||
|
if (fs == NULL || path == NULL) return false; |
||||
|
|
||||
|
File file = openPacketFilterWrite(fs, path); |
||||
|
if (!file) return false; |
||||
|
|
||||
|
char line[160]; |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_MAX_RULES; i++) { |
||||
|
if (!_rules[i].isActive()) continue; |
||||
|
_rules[i].formatRule(line, sizeof(line)); |
||||
|
file.print(line); |
||||
|
file.print('\n'); |
||||
|
} |
||||
|
|
||||
|
file.close(); |
||||
|
return true; |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <helpers/IdentityStore.h> |
||||
|
#include <helpers/packet_filter/PacketFilterRule.h> |
||||
|
|
||||
|
#ifndef PACKET_FILTER_MAX_RULES |
||||
|
#define PACKET_FILTER_MAX_RULES 20 |
||||
|
#endif |
||||
|
|
||||
|
class PacketFilter { |
||||
|
PacketFilterRule _rules[PACKET_FILTER_MAX_RULES]; |
||||
|
uint8_t _rule_count; |
||||
|
uint32_t _filtered_count; |
||||
|
mesh::RNG* _rng; |
||||
|
|
||||
|
public: |
||||
|
PacketFilter(mesh::RNG* rng = nullptr); |
||||
|
|
||||
|
bool shouldRepeatPacket(const mesh::Packet* packet); |
||||
|
bool block(const char* classifier_name, char* args); |
||||
|
bool deleteRule(uint8_t rule_index); |
||||
|
void clear(); |
||||
|
|
||||
|
uint32_t getFilteredCount() const { return _filtered_count; } |
||||
|
void resetStats() { _filtered_count = 0; } |
||||
|
|
||||
|
uint8_t getRuleCount() const { return _rule_count; } |
||||
|
void formatRules(char* reply, size_t reply_len, uint8_t page = 1) const; |
||||
|
void handleBlockCommand(FILESYSTEM* fs, const char* path, char* command, char* reply, size_t reply_len); |
||||
|
|
||||
|
bool load(FILESYSTEM* fs, const char* path); |
||||
|
bool save(FILESYSTEM* fs, const char* path) const; |
||||
|
}; |
||||
@ -0,0 +1,140 @@ |
|||||
|
#include <helpers/SimpleBloomFilter.h> |
||||
|
|
||||
|
#include <SHA256.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define BLOOM_GOLDEN_RATIO_32 0x9E3779B9UL |
||||
|
|
||||
|
SimpleBloomFilter::SimpleBloomFilter() { |
||||
|
_data = NULL; |
||||
|
_byte_count = 0; |
||||
|
_bit_count = 0; |
||||
|
_hash_count = 0; |
||||
|
_salt = NULL; |
||||
|
_salt_len = 0; |
||||
|
_set_bits = 0; |
||||
|
} |
||||
|
|
||||
|
bool SimpleBloomFilter::begin( |
||||
|
uint8_t* storage, |
||||
|
uint16_t storage_len, |
||||
|
uint32_t bit_count, |
||||
|
uint8_t hash_count |
||||
|
) { |
||||
|
if (storage == NULL) return false; |
||||
|
if (storage_len == 0) return false; |
||||
|
if (bit_count == 0) return false; |
||||
|
if (hash_count == 0) return false; |
||||
|
if (bit_count > ((uint32_t)storage_len * 8UL)) return false; |
||||
|
|
||||
|
_data = storage; |
||||
|
_byte_count = storage_len; |
||||
|
_bit_count = bit_count; |
||||
|
_hash_count = hash_count; |
||||
|
|
||||
|
clear(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void SimpleBloomFilter::setSalt(const uint8_t* salt, uint8_t salt_len) { |
||||
|
_salt = salt; |
||||
|
_salt_len = salt == NULL ? 0 : salt_len; |
||||
|
} |
||||
|
|
||||
|
void SimpleBloomFilter::clear() { |
||||
|
if (_data != NULL && _byte_count > 0) { |
||||
|
memset(_data, 0, _byte_count); |
||||
|
} |
||||
|
|
||||
|
_set_bits = 0; |
||||
|
} |
||||
|
|
||||
|
bool SimpleBloomFilter::getBit(uint32_t bit) const { |
||||
|
return (_data[bit >> 3] & (uint8_t)(1U << (bit & 7))) != 0; |
||||
|
} |
||||
|
|
||||
|
void SimpleBloomFilter::setBit(uint32_t bit) { |
||||
|
uint8_t mask = (uint8_t)(1U << (bit & 7)); |
||||
|
uint8_t* byte = &_data[bit >> 3]; |
||||
|
|
||||
|
if ((*byte & mask) == 0) { |
||||
|
*byte |= mask; |
||||
|
_set_bits++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void SimpleBloomFilter::calculateDigest( |
||||
|
uint8_t digest[], |
||||
|
const uint8_t* data, |
||||
|
size_t data_len |
||||
|
) const { |
||||
|
SHA256 sha; |
||||
|
|
||||
|
if (_salt != NULL && _salt_len > 0) { |
||||
|
sha.update(_salt, _salt_len); |
||||
|
} |
||||
|
|
||||
|
sha.update(data, data_len); |
||||
|
sha.finalize(digest, SHA256::HASH_SIZE); |
||||
|
} |
||||
|
|
||||
|
uint32_t SimpleBloomFilter::calculateBit( |
||||
|
const uint8_t digest[], |
||||
|
uint8_t index |
||||
|
) const { |
||||
|
uint32_t h1; |
||||
|
uint32_t h2; |
||||
|
|
||||
|
memcpy(&h1, digest, sizeof(h1)); |
||||
|
memcpy(&h2, digest + sizeof(h1), sizeof(h2)); |
||||
|
|
||||
|
if (h2 == 0) { |
||||
|
h2 = BLOOM_GOLDEN_RATIO_32; |
||||
|
} |
||||
|
|
||||
|
uint32_t i = (uint32_t)index; |
||||
|
uint32_t mixed = h1 + (i * h2) + (i * i); |
||||
|
|
||||
|
return mixed % _bit_count; |
||||
|
} |
||||
|
|
||||
|
bool SimpleBloomFilter::contains(const uint8_t* data, size_t data_len) const { |
||||
|
if (_data == NULL) return false; |
||||
|
if (_bit_count == 0) return false; |
||||
|
if (_hash_count == 0) return false; |
||||
|
if (data == NULL) return false; |
||||
|
|
||||
|
uint8_t digest[SHA256::HASH_SIZE]; |
||||
|
calculateDigest(digest, data, data_len); |
||||
|
|
||||
|
for (uint8_t i = 0; i < _hash_count; i++) { |
||||
|
uint32_t bit = calculateBit(digest, i); |
||||
|
|
||||
|
if (!getBit(bit)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void SimpleBloomFilter::add(const uint8_t* data, size_t data_len) { |
||||
|
if (_data == NULL) return; |
||||
|
if (_bit_count == 0) return; |
||||
|
if (_hash_count == 0) return; |
||||
|
if (data == NULL) return; |
||||
|
|
||||
|
uint8_t digest[SHA256::HASH_SIZE]; |
||||
|
calculateDigest(digest, data, data_len); |
||||
|
|
||||
|
for (uint8_t i = 0; i < _hash_count; i++) { |
||||
|
uint32_t bit = calculateBit(digest, i); |
||||
|
setBit(bit); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool SimpleBloomFilter::isSaturated(uint8_t max_fill_percent) const { |
||||
|
if (_bit_count == 0) return true; |
||||
|
|
||||
|
return (_set_bits * 100UL) >= (_bit_count * (uint32_t)max_fill_percent); |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <stddef.h> |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
class SimpleBloomFilter { |
||||
|
public: |
||||
|
SimpleBloomFilter(); |
||||
|
|
||||
|
bool begin(uint8_t* storage, uint16_t storage_len, uint32_t bit_count, uint8_t hash_count); |
||||
|
|
||||
|
void setSalt(const uint8_t* salt, uint8_t salt_len); |
||||
|
void clear(); |
||||
|
|
||||
|
bool contains(const uint8_t* data, size_t data_len) const; |
||||
|
void add(const uint8_t* data, size_t data_len); |
||||
|
|
||||
|
bool isSaturated(uint8_t max_fill_percent) const; |
||||
|
|
||||
|
private: |
||||
|
uint8_t* _data; |
||||
|
uint16_t _byte_count; |
||||
|
uint32_t _bit_count; |
||||
|
uint8_t _hash_count; |
||||
|
|
||||
|
const uint8_t* _salt; |
||||
|
uint8_t _salt_len; |
||||
|
|
||||
|
uint32_t _set_bits; |
||||
|
|
||||
|
bool getBit(uint32_t bit) const; |
||||
|
void setBit(uint32_t bit); |
||||
|
|
||||
|
void calculateDigest(uint8_t digest[], const uint8_t* data, size_t data_len) const; |
||||
|
uint32_t calculateBit(const uint8_t digest[], uint8_t index) const; |
||||
|
}; |
||||
@ -0,0 +1,33 @@ |
|||||
|
#include "SimplePatternMatcher.h" |
||||
|
|
||||
|
bool SimplePatternMatcher::matches(const char* pattern, const char* text) { |
||||
|
if (pattern == nullptr || text == nullptr) return false; |
||||
|
return matchHere(pattern, text); |
||||
|
} |
||||
|
|
||||
|
bool SimplePatternMatcher::matchHere(const char* pattern, const char* text) { |
||||
|
const char* starPattern = nullptr; |
||||
|
const char* starText = nullptr; |
||||
|
|
||||
|
while (*text) { |
||||
|
if (*pattern == '*') { |
||||
|
while (*pattern == '*') pattern++; |
||||
|
if (*pattern == '\0') return true; |
||||
|
|
||||
|
starPattern = pattern; |
||||
|
starText = text; |
||||
|
} else if (*pattern == '?' || *pattern == *text) { |
||||
|
pattern++; |
||||
|
text++; |
||||
|
} else if (starPattern != nullptr) { |
||||
|
pattern = starPattern; |
||||
|
text = ++starText; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
while (*pattern == '*') pattern++; |
||||
|
|
||||
|
return *pattern == '\0'; |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
class SimplePatternMatcher { |
||||
|
public: |
||||
|
static bool matches(const char* pattern, const char* text); |
||||
|
|
||||
|
private: |
||||
|
static bool matchHere(const char* pattern, const char* text); |
||||
|
}; |
||||
@ -0,0 +1,182 @@ |
|||||
|
#include "helpers/packet_filter/AdvertRateLimitClassifier.h" |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
AdvertRateLimitClassifier::AdvertRateLimitClassifier(mesh::RNG* rng) { |
||||
|
_rng = rng; |
||||
|
_saturated = false; |
||||
|
_advert_type = ADV_TYPE_REPEATER; |
||||
|
_period_minutes = 0; |
||||
|
_max_per_period = 0; |
||||
|
_window_id = 0xFFFFFFFFUL; |
||||
|
memset(_salts, 0, sizeof(_salts)); |
||||
|
configureFilters(); |
||||
|
} |
||||
|
|
||||
|
void AdvertRateLimitClassifier::generateSalts() { |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_ADVERT_MAX_PER_PERIOD; i++) { |
||||
|
if (_rng != nullptr) { |
||||
|
_rng->random(_salts[i], sizeof(_salts[i])); |
||||
|
} |
||||
|
_filters[i].setSalt(_salts[i], sizeof(_salts[i])); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void AdvertRateLimitClassifier::clearFilters() { |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_ADVERT_MAX_PER_PERIOD; i++) { |
||||
|
_filters[i].clear(); |
||||
|
} |
||||
|
_saturated = false; |
||||
|
} |
||||
|
|
||||
|
void AdvertRateLimitClassifier::configureFilters() { |
||||
|
for (uint8_t i = 0; i < PACKET_FILTER_ADVERT_MAX_PER_PERIOD; i++) { |
||||
|
_filters[i].begin(_filter_storage[i], sizeof(_filter_storage[i]), PACKET_FILTER_ADVERT_BLOOM_BITS, PACKET_FILTER_ADVERT_BLOOM_HASH_COUNT); |
||||
|
_filters[i].setSalt(_salts[i], sizeof(_salts[i])); |
||||
|
} |
||||
|
clearFilters(); |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::configureParsed(uint8_t advert_type, uint16_t period_minutes, uint8_t max_per_period) { |
||||
|
if (period_minutes == 0 || max_per_period == 0) return false; |
||||
|
if (max_per_period > PACKET_FILTER_ADVERT_MAX_PER_PERIOD) return false; |
||||
|
if (advert_type > 0x0F) return false; |
||||
|
if (PACKET_FILTER_ADVERT_BLOOM_BITS == 0 || PACKET_FILTER_ADVERT_BLOOM_HASH_COUNT == 0) return false; |
||||
|
|
||||
|
_advert_type = advert_type; |
||||
|
_period_minutes = period_minutes; |
||||
|
_max_per_period = max_per_period; |
||||
|
_window_id = 0xFFFFFFFFUL; |
||||
|
generateSalts(); |
||||
|
clearFilters(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::configure(char* args) { |
||||
|
char* advert_type = nextToken(args); |
||||
|
char* period = nextToken(args); |
||||
|
char* max_count = nextToken(args); |
||||
|
char* extra = nextToken(args); |
||||
|
|
||||
|
unsigned long period_minutes = 0; |
||||
|
unsigned long max_per_period = 1; |
||||
|
uint8_t parsed_type; |
||||
|
if (!parseAdvertType(advert_type, &parsed_type)) return false; |
||||
|
if (!parseUnsigned(period, 1, 65535UL, &period_minutes)) return false; |
||||
|
if (max_count != NULL && !parseUnsigned(max_count, 1, PACKET_FILTER_ADVERT_MAX_PER_PERIOD, &max_per_period)) return false; |
||||
|
if (extra != NULL) return false; |
||||
|
|
||||
|
return configureParsed(parsed_type, (uint16_t)period_minutes, (uint8_t)max_per_period); |
||||
|
} |
||||
|
|
||||
|
void AdvertRateLimitClassifier::formatRule(char* dest, size_t dest_len) const { |
||||
|
char type_name[4]; |
||||
|
snprintf( |
||||
|
dest, |
||||
|
dest_len, |
||||
|
"advert %s %u %u", |
||||
|
formatAdvertType(_advert_type, type_name, sizeof(type_name)), |
||||
|
_period_minutes, |
||||
|
_max_per_period |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
void AdvertRateLimitClassifier::resetWindowIfNeeded() { |
||||
|
uint32_t period_ms = ((uint32_t)_period_minutes) * 60UL * 1000UL; |
||||
|
uint32_t window_id = period_ms == 0 ? 0 : millis() / period_ms; |
||||
|
if (window_id != _window_id) { |
||||
|
generateSalts(); |
||||
|
clearFilters(); |
||||
|
_window_id = window_id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::filterContains(uint8_t filter_idx, const uint8_t* cert) const { |
||||
|
return _filters[filter_idx].contains(cert, PUB_KEY_SIZE); |
||||
|
} |
||||
|
|
||||
|
void AdvertRateLimitClassifier::addToFilter(uint8_t filter_idx, const uint8_t* cert) { |
||||
|
_filters[filter_idx].add(cert, PUB_KEY_SIZE); |
||||
|
if (isFilterSaturated(filter_idx)) _saturated = true; |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::isFilterSaturated(uint8_t filter_idx) const { |
||||
|
return _filters[filter_idx].isSaturated(PACKET_FILTER_ADVERT_BLOOM_MAX_FILL_PERCENT); |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::shouldRepeatPacket(const mesh::Packet* packet) { |
||||
|
if (packet == NULL || packet->getPayloadType() != PAYLOAD_TYPE_ADVERT) return true; |
||||
|
|
||||
|
const uint16_t app_data_offset = PUB_KEY_SIZE + 4 + SIGNATURE_SIZE; |
||||
|
if (packet->payload_len <= app_data_offset) return true; |
||||
|
|
||||
|
AdvertDataParser parser(&packet->payload[app_data_offset], packet->payload_len - app_data_offset); |
||||
|
if (!parser.isValid() || parser.getType() != _advert_type) return true; |
||||
|
|
||||
|
resetWindowIfNeeded(); |
||||
|
if (_saturated) return true; |
||||
|
|
||||
|
uint8_t seen_count = 0; |
||||
|
uint8_t first_available = 0xFF; |
||||
|
for (uint8_t i = 0; i < _max_per_period; i++) { |
||||
|
if (filterContains(i, packet->payload)) { |
||||
|
seen_count++; |
||||
|
} else if (first_available == 0xFF) { |
||||
|
first_available = i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (seen_count >= _max_per_period) return false; |
||||
|
if (first_available != 0xFF) addToFilter(first_available, packet->payload); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::handleBlockCommand(char* args, char* reply, size_t reply_len, BlockRuleFn block_rule, void* block_context) { |
||||
|
return BasePacketFilterClassifier::handleBlockCommand( |
||||
|
"advert", |
||||
|
args, |
||||
|
reply, |
||||
|
reply_len, |
||||
|
"Usage: block advert repeater 60 [max]", |
||||
|
"OK - advert block added", |
||||
|
block_rule, |
||||
|
block_context |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
bool AdvertRateLimitClassifier::parseAdvertType(const char* name, uint8_t* advert_type) { |
||||
|
if (name == NULL || advert_type == NULL || name[0] == 0) return false; |
||||
|
|
||||
|
if (strcmp(name, "companion") == 0) { |
||||
|
*advert_type = ADV_TYPE_CHAT; |
||||
|
} else if (strcmp(name, "repeater") == 0) { |
||||
|
*advert_type = ADV_TYPE_REPEATER; |
||||
|
} else if (strcmp(name, "room") == 0) { |
||||
|
*advert_type = ADV_TYPE_ROOM; |
||||
|
} else { |
||||
|
char* end = NULL; |
||||
|
long value = strtol(name, &end, 10); |
||||
|
if (end == name || *end != 0 || value <= 0 || value > 15) return false; |
||||
|
*advert_type = (uint8_t)value; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
const char* AdvertRateLimitClassifier::advertTypeName(uint8_t advert_type) { |
||||
|
switch (advert_type) { |
||||
|
case ADV_TYPE_CHAT: return "companion"; |
||||
|
case ADV_TYPE_REPEATER: return "repeater"; |
||||
|
case ADV_TYPE_ROOM: return "room"; |
||||
|
default: return "?"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const char* AdvertRateLimitClassifier::formatAdvertType(uint8_t advert_type, char* dest, size_t dest_len) { |
||||
|
const char* name = advertTypeName(advert_type); |
||||
|
if (strcmp(name, "?") != 0) return name; |
||||
|
snprintf(dest, dest_len, "%u", advert_type); |
||||
|
return dest; |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "BasePacketFilterClassifier.h" |
||||
|
|
||||
|
#include <Mesh.h> |
||||
|
#include <helpers/AdvertDataHelpers.h> |
||||
|
#include <helpers/SimpleBloomFilter.h> |
||||
|
|
||||
|
#ifndef PACKET_FILTER_ADVERT_BLOOM_BITS |
||||
|
#define PACKET_FILTER_ADVERT_BLOOM_BITS 1024 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef PACKET_FILTER_ADVERT_BLOOM_STORAGE_BYTES |
||||
|
#define PACKET_FILTER_ADVERT_BLOOM_STORAGE_BYTES ((PACKET_FILTER_ADVERT_BLOOM_BITS + 7) / 8) |
||||
|
#endif |
||||
|
|
||||
|
#ifndef PACKET_FILTER_ADVERT_MAX_PER_PERIOD |
||||
|
#define PACKET_FILTER_ADVERT_MAX_PER_PERIOD 4 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef PACKET_FILTER_ADVERT_BLOOM_MAX_FILL_PERCENT |
||||
|
#define PACKET_FILTER_ADVERT_BLOOM_MAX_FILL_PERCENT 75 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef PACKET_FILTER_ADVERT_BLOOM_HASH_COUNT |
||||
|
#define PACKET_FILTER_ADVERT_BLOOM_HASH_COUNT 4 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef PACKET_FILTER_ADVERT_BLOOM_SALT_BYTES |
||||
|
#define PACKET_FILTER_ADVERT_BLOOM_SALT_BYTES 16 |
||||
|
#endif |
||||
|
|
||||
|
class AdvertRateLimitClassifier : public BasePacketFilterClassifier { |
||||
|
bool _saturated; |
||||
|
uint8_t _advert_type; |
||||
|
uint16_t _period_minutes; |
||||
|
uint8_t _max_per_period; |
||||
|
uint32_t _window_id; |
||||
|
mesh::RNG* _rng; |
||||
|
uint8_t _salts[PACKET_FILTER_ADVERT_MAX_PER_PERIOD][PACKET_FILTER_ADVERT_BLOOM_SALT_BYTES]; |
||||
|
uint8_t _filter_storage[PACKET_FILTER_ADVERT_MAX_PER_PERIOD][PACKET_FILTER_ADVERT_BLOOM_STORAGE_BYTES]; |
||||
|
SimpleBloomFilter _filters[PACKET_FILTER_ADVERT_MAX_PER_PERIOD]; |
||||
|
|
||||
|
void generateSalts(); |
||||
|
void clearFilters(); |
||||
|
void configureFilters(); |
||||
|
void resetWindowIfNeeded(); |
||||
|
bool filterContains(uint8_t filter_idx, const uint8_t* cert) const; |
||||
|
void addToFilter(uint8_t filter_idx, const uint8_t* cert); |
||||
|
bool isFilterSaturated(uint8_t filter_idx) const; |
||||
|
bool configureParsed(uint8_t advert_type, uint16_t period_minutes, uint8_t max_per_period); |
||||
|
|
||||
|
public: |
||||
|
AdvertRateLimitClassifier(mesh::RNG* rng = nullptr); |
||||
|
|
||||
|
bool configure(char* args); |
||||
|
|
||||
|
void formatRule(char* dest, size_t dest_len) const; |
||||
|
bool shouldRepeatPacket(const mesh::Packet* packet); |
||||
|
|
||||
|
static bool handleBlockCommand(char* args, char* reply, size_t reply_len, BlockRuleFn block_rule, void* block_context); |
||||
|
|
||||
|
private: |
||||
|
static bool parseAdvertType(const char* name, uint8_t* advert_type); |
||||
|
static const char* advertTypeName(uint8_t advert_type); |
||||
|
static const char* formatAdvertType(uint8_t advert_type, char* dest, size_t dest_len); |
||||
|
}; |
||||
@ -0,0 +1,69 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
class BasePacketFilterClassifier { |
||||
|
public: |
||||
|
typedef bool (*BlockRuleFn)(void* context, const char* classifier_name, char* args); |
||||
|
|
||||
|
static bool isArgSpace(char c) { |
||||
|
return c == ' ' || c == '\t'; |
||||
|
} |
||||
|
|
||||
|
static char* nextToken(char*& cursor) { |
||||
|
if (cursor == NULL) return NULL; |
||||
|
while (isArgSpace(*cursor)) cursor++; |
||||
|
if (*cursor == 0) return NULL; |
||||
|
|
||||
|
char* token = cursor; |
||||
|
while (*cursor && !isArgSpace(*cursor)) cursor++; |
||||
|
if (*cursor) *cursor++ = 0; |
||||
|
return token; |
||||
|
} |
||||
|
|
||||
|
static char* remainingArgs(char*& cursor) { |
||||
|
if (cursor == NULL) return NULL; |
||||
|
while (isArgSpace(*cursor)) cursor++; |
||||
|
return cursor; |
||||
|
} |
||||
|
|
||||
|
static char* unquote(char* value) { |
||||
|
if (value == NULL || *value != '"') return value; |
||||
|
|
||||
|
value++; |
||||
|
char* end = strrchr(value, '"'); |
||||
|
if (end != NULL) *end = 0; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
static bool parseUnsigned(const char* token, unsigned long min_value, unsigned long max_value, unsigned long* value) { |
||||
|
if (token == NULL || value == NULL) return false; |
||||
|
|
||||
|
char* end = NULL; |
||||
|
unsigned long parsed = strtoul(token, &end, 10); |
||||
|
if (end == token || *end != 0 || parsed < min_value || parsed > max_value) return false; |
||||
|
|
||||
|
*value = parsed; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
static bool handleBlockCommand(const char* classifier_name, char* args, char* reply, size_t reply_len, |
||||
|
const char* usage, const char* success_message, |
||||
|
BlockRuleFn block_rule, void* block_context) { |
||||
|
char* classifier_args = remainingArgs(args); |
||||
|
if (classifier_args == NULL || *classifier_args == 0 || strcmp(classifier_args, "help") == 0) { |
||||
|
snprintf(reply, reply_len, "%s", usage); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (block_rule != NULL && block_rule(block_context, classifier_name, classifier_args)) { |
||||
|
snprintf(reply, reply_len, "%s", success_message); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
snprintf(reply, reply_len, "Err - packet filter rule invalid or full"); |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,69 @@ |
|||||
|
#include "helpers/packet_filter/ChannelMessageClassifier.h" |
||||
|
|
||||
|
#include <helpers/ChannelDetails.h> |
||||
|
#include <helpers/SimplePatternMatcher.h> |
||||
|
#include <helpers/TxtDataHelpers.h> |
||||
|
#include <Utils.h> |
||||
|
|
||||
|
ChannelMessageClassifier::ChannelMessageClassifier() { |
||||
|
memset(_channel_name, 0, sizeof(_channel_name)); |
||||
|
memset(_pattern, 0, sizeof(_pattern)); |
||||
|
memset(_channel_hash, 0, sizeof(_channel_hash)); |
||||
|
memset(_channel_secret, 0, sizeof(_channel_secret)); |
||||
|
} |
||||
|
|
||||
|
bool ChannelMessageClassifier::configure(char* args) { |
||||
|
char* channel_name = nextToken(args); |
||||
|
char* pattern = unquote(remainingArgs(args)); |
||||
|
|
||||
|
if (channel_name == NULL || pattern == NULL || channel_name[0] == 0 || pattern[0] == 0) return false; |
||||
|
if (strlen(channel_name) >= sizeof(_channel_name) || strlen(pattern) >= sizeof(_pattern)) return false; |
||||
|
|
||||
|
uint8_t secret[PUB_KEY_SIZE]; |
||||
|
if (!mesh::GroupChannel::derivePublicSecret(channel_name, secret)) return false; |
||||
|
|
||||
|
memset(_channel_name, 0, sizeof(_channel_name)); |
||||
|
memset(_pattern, 0, sizeof(_pattern)); |
||||
|
strncpy(_channel_name, channel_name, sizeof(_channel_name) - 1); |
||||
|
strncpy(_pattern, pattern, sizeof(_pattern) - 1); |
||||
|
memcpy(_channel_secret, secret, sizeof(_channel_secret)); |
||||
|
mesh::GroupChannel channel; |
||||
|
memcpy(channel.secret, _channel_secret, sizeof(channel.secret)); |
||||
|
channel.deriveHash(); |
||||
|
memcpy(_channel_hash, channel.hash, sizeof(_channel_hash)); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void ChannelMessageClassifier::formatRule(char* dest, size_t dest_len) const { |
||||
|
snprintf(dest, dest_len, "channelmessage %s %s", _channel_name, _pattern); |
||||
|
} |
||||
|
|
||||
|
bool ChannelMessageClassifier::shouldRepeatPacket(const mesh::Packet* packet) { |
||||
|
if (packet == NULL || packet->getPayloadType() != PAYLOAD_TYPE_GRP_TXT || packet->payload_len <= 1) return true; |
||||
|
if (packet->payload[0] != _channel_hash[0]) return true; |
||||
|
|
||||
|
uint8_t data[MAX_PACKET_PAYLOAD + 1]; |
||||
|
int len = mesh::Utils::MACThenDecrypt(_channel_secret, data, &packet->payload[1], packet->payload_len - 1); |
||||
|
if (len <= 5) return true; |
||||
|
|
||||
|
uint8_t txt_type = data[4] >> 2; |
||||
|
if (txt_type != TXT_TYPE_PLAIN) return true; |
||||
|
|
||||
|
data[len] = 0; |
||||
|
const char* text = (const char*)&data[5]; |
||||
|
return !SimplePatternMatcher::matches(_pattern, text); |
||||
|
} |
||||
|
|
||||
|
bool ChannelMessageClassifier::handleBlockCommand(char* args, char* reply, size_t reply_len, |
||||
|
BlockRuleFn block_rule, void* block_context) { |
||||
|
return BasePacketFilterClassifier::handleBlockCommand( |
||||
|
"channelmessage", |
||||
|
args, |
||||
|
reply, |
||||
|
reply_len, |
||||
|
"Usage: block channelmessage #channel \"pattern\"", |
||||
|
"OK - channel message block added", |
||||
|
block_rule, |
||||
|
block_context |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "BasePacketFilterClassifier.h" |
||||
|
|
||||
|
#include <Mesh.h> |
||||
|
|
||||
|
#ifndef PACKET_FILTER_CHANNEL_NAME_SIZE |
||||
|
#define PACKET_FILTER_CHANNEL_NAME_SIZE 32 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef PACKET_FILTER_PATTERN_SIZE |
||||
|
#define PACKET_FILTER_PATTERN_SIZE 80 |
||||
|
#endif |
||||
|
|
||||
|
class ChannelMessageClassifier : public BasePacketFilterClassifier { |
||||
|
char _channel_name[PACKET_FILTER_CHANNEL_NAME_SIZE]; |
||||
|
char _pattern[PACKET_FILTER_PATTERN_SIZE]; |
||||
|
uint8_t _channel_hash[PATH_HASH_SIZE]; |
||||
|
uint8_t _channel_secret[PUB_KEY_SIZE]; |
||||
|
|
||||
|
public: |
||||
|
ChannelMessageClassifier(); |
||||
|
|
||||
|
bool configure(char* args); |
||||
|
|
||||
|
void formatRule(char* dest, size_t dest_len) const; |
||||
|
bool shouldRepeatPacket(const mesh::Packet* packet); |
||||
|
|
||||
|
static bool handleBlockCommand(char* args, char* reply, size_t reply_len, BlockRuleFn block_rule, void* block_context); |
||||
|
}; |
||||
@ -0,0 +1,96 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "AdvertRateLimitClassifier.h" |
||||
|
#include "ChannelMessageClassifier.h" |
||||
|
|
||||
|
#include <new> |
||||
|
#include <string.h> |
||||
|
|
||||
|
class PacketFilterRule { |
||||
|
public: |
||||
|
enum Type { |
||||
|
RULE_NONE, |
||||
|
RULE_CHANNEL_MESSAGE, |
||||
|
RULE_ADVERT |
||||
|
}; |
||||
|
|
||||
|
private: |
||||
|
Type _type; |
||||
|
|
||||
|
union Classifier { |
||||
|
ChannelMessageClassifier channel_message; |
||||
|
AdvertRateLimitClassifier advert; |
||||
|
|
||||
|
Classifier() {} |
||||
|
~Classifier() {} |
||||
|
} _classifier; |
||||
|
|
||||
|
public: |
||||
|
PacketFilterRule() : _type(RULE_NONE) {} |
||||
|
~PacketFilterRule() { clear(); } |
||||
|
|
||||
|
void clear() { |
||||
|
switch (_type) { |
||||
|
case RULE_CHANNEL_MESSAGE: |
||||
|
_classifier.channel_message.~ChannelMessageClassifier(); |
||||
|
break; |
||||
|
case RULE_ADVERT: |
||||
|
_classifier.advert.~AdvertRateLimitClassifier(); |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
_type = RULE_NONE; |
||||
|
} |
||||
|
|
||||
|
bool configure(const char* classifier_name, char* args, const mesh::Identity* self = NULL, mesh::RNG* rng = NULL) { |
||||
|
(void)self; |
||||
|
if (classifier_name == NULL) return false; |
||||
|
|
||||
|
if (strcmp(classifier_name, "channelmessage") == 0) { |
||||
|
ChannelMessageClassifier next; |
||||
|
if (!next.configure(args)) return false; |
||||
|
clear(); |
||||
|
new (&_classifier.channel_message) ChannelMessageClassifier(next); |
||||
|
_type = RULE_CHANNEL_MESSAGE; |
||||
|
return true; |
||||
|
} else if (strcmp(classifier_name, "advert") == 0) { |
||||
|
AdvertRateLimitClassifier next(rng); |
||||
|
if (!next.configure(args)) return false; |
||||
|
clear(); |
||||
|
new (&_classifier.advert) AdvertRateLimitClassifier(next); |
||||
|
_type = RULE_ADVERT; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
bool isActive() const { return _type != RULE_NONE; } |
||||
|
Type getType() const { return _type; } |
||||
|
|
||||
|
void formatRule(char* dest, size_t dest_len) const { |
||||
|
switch (_type) { |
||||
|
case RULE_CHANNEL_MESSAGE: |
||||
|
_classifier.channel_message.formatRule(dest, dest_len); |
||||
|
break; |
||||
|
case RULE_ADVERT: |
||||
|
_classifier.advert.formatRule(dest, dest_len); |
||||
|
break; |
||||
|
default: |
||||
|
if (dest_len > 0) dest[0] = 0; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool shouldRepeatPacket(const mesh::Packet* packet) { |
||||
|
switch (_type) { |
||||
|
case RULE_CHANNEL_MESSAGE: |
||||
|
return _classifier.channel_message.shouldRepeatPacket(packet); |
||||
|
case RULE_ADVERT: |
||||
|
return _classifier.advert.shouldRepeatPacket(packet); |
||||
|
default: |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
Loading…
Reference in new issue