Browse Source

Guard channel content filter behind WITH_CHANNEL_FILTER

Feature is now fully compiled out unless built with -D WITH_CHANNEL_FILTER.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
pull/2733/head
Kemal Hadimli 4 days ago
parent
commit
7f1265bfe8
  1. 4
      docs/cli_commands.md
  2. 13
      examples/simple_repeater/MyMesh.cpp
  3. 9
      examples/simple_repeater/MyMesh.h

4
docs/cli_commands.md

@ -118,6 +118,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore
## Channel Content Filter (Repeater Only) ## Channel Content Filter (Repeater Only)
> **Compile-time feature.** These commands only exist when the firmware is built with `-D WITH_CHANNEL_FILTER`. Without that build flag the feature is fully compiled out (no code, no storage, no CLI commands) and the repeater behaves exactly as stock.
Lets a repeater decrypt channels it holds the key for, inspect the plaintext, and refuse to retransmit messages that match a blocked keyword or sender name. With nothing configured, behaviour is identical to a stock repeater. Lets a repeater decrypt channels it holds the key for, inspect the plaintext, and refuse to retransmit messages that match a blocked keyword or sender name. With nothing configured, behaviour is identical to a stock repeater.
**How it works:** the repeater only decrypts channels you explicitly load a key for (see `filter channel`). For those channels it reads the sender name and message text; any message matching a blocked keyword (text, case-insensitive substring) or blocked sender (case-insensitive substring of the sender name) is dropped instead of forwarded. All other channels — and direct messages — are never decrypted and forward exactly as before. **How it works:** the repeater only decrypts channels you explicitly load a key for (see `filter channel`). For those channels it reads the sender name and message text; any message matching a blocked keyword (text, case-insensitive substring) or blocked sender (case-insensitive substring of the sender name) is dropped instead of forwarded. All other channels — and direct messages — are never decrypted and forward exactly as before.
@ -131,7 +133,7 @@ Lets a repeater decrypt channels it holds the key for, inspect the plaintext, an
**Config storage:** persisted to `/channel_filter` on the node's filesystem. **Config storage:** persisted to `/channel_filter` on the node's filesystem.
**Build flags:** `MAX_FILTER_CHANNELS` (default `4`), `MAX_FILTER_TERMS` (default `8`, applies to keywords and senders independently). **Build flags:** `WITH_CHANNEL_FILTER` (required, enables the feature), `MAX_FILTER_CHANNELS` (default `4`), `MAX_FILTER_TERMS` (default `8`, applies to keywords and senders independently).
--- ---

13
examples/simple_repeater/MyMesh.cpp

@ -1,8 +1,10 @@
#include "MyMesh.h" #include "MyMesh.h"
#include "UnicodeFold.h"
#include <algorithm> #include <algorithm>
#include <ctype.h> #include <ctype.h>
#ifdef WITH_CHANNEL_FILTER
#include "UnicodeFold.h"
/* --------------------- public-channel content filter ------------------ */ /* --------------------- public-channel content filter ------------------ */
// The well-known MeshCore public channel PSK ("izOH6cXN6mrJ5e26oRXNcg==") // The well-known MeshCore public channel PSK ("izOH6cXN6mrJ5e26oRXNcg==")
@ -35,6 +37,7 @@ static int decodeBase64(const char* in, uint8_t* out, int max_out) {
} }
return n; return n;
} }
#endif // WITH_CHANNEL_FILTER
/* ------------------------------ Config -------------------------------- */ /* ------------------------------ Config -------------------------------- */
@ -663,6 +666,7 @@ void MyMesh::getPeerSharedSecret(uint8_t *dest_secret, int peer_idx) {
} }
} }
#ifdef WITH_CHANNEL_FILTER
bool MyMesh::addFilterChannel(const char *psk_b64) { bool MyMesh::addFilterChannel(const char *psk_b64) {
if (num_filter_channels >= MAX_FILTER_CHANNELS) return false; if (num_filter_channels >= MAX_FILTER_CHANNELS) return false;
@ -865,6 +869,7 @@ void MyMesh::handleFilterCommand(char *command, char *reply) {
} }
strcpy(reply, "Err - usage: filter [list|stats [reset]|channel <b64|public|clear>|block <kw>|sender <name>|clear|reset]"); strcpy(reply, "Err - usage: filter [list|stats [reset]|channel <b64|public|clear>|block <kw>|sender <name>|clear|reset]");
} }
#endif // WITH_CHANNEL_FILTER
static bool isShare(const mesh::Packet *packet) { static bool isShare(const mesh::Packet *packet) {
if (packet->hasTransportCodes()) { if (packet->hasTransportCodes()) {
@ -1101,10 +1106,12 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
{ {
last_millis = 0; last_millis = 0;
uptime_millis = 0; uptime_millis = 0;
#ifdef WITH_CHANNEL_FILTER
num_filter_channels = 0; num_filter_channels = 0;
num_block_keywords = 0; num_block_keywords = 0;
num_block_senders = 0; num_block_senders = 0;
n_filtered = 0; n_filtered = 0;
#endif
next_local_advert = next_flood_advert = 0; next_local_advert = next_flood_advert = 0;
dirty_contacts_expiry = 0; dirty_contacts_expiry = 0;
set_radio_at = revert_radio_at = 0; set_radio_at = revert_radio_at = 0;
@ -1173,7 +1180,9 @@ void MyMesh::begin(FILESYSTEM *fs) {
// load persisted prefs // load persisted prefs
_cli.loadPrefs(_fs); _cli.loadPrefs(_fs);
acl.load(_fs, self_id); acl.load(_fs, self_id);
#ifdef WITH_CHANNEL_FILTER
loadChannelFilter(); loadChannelFilter();
#endif
// TODO: key_store.begin(); // TODO: key_store.begin();
region_map.load(_fs); region_map.load(_fs);
@ -1501,8 +1510,10 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
sendNodeDiscoverReq(); sendNodeDiscoverReq();
strcpy(reply, "OK - Discover sent"); strcpy(reply, "OK - Discover sent");
} }
#ifdef WITH_CHANNEL_FILTER
} else if (memcmp(command, "filter", 6) == 0 && (command[6] == 0 || command[6] == ' ')) { } else if (memcmp(command, "filter", 6) == 0 && (command[6] == 0 || command[6] == ' ')) {
handleFilterCommand(command, reply); handleFilterCommand(command, reply);
#endif
} else{ } else{
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
} }

9
examples/simple_repeater/MyMesh.h

@ -79,6 +79,8 @@ struct NeighbourInfo {
#define FIRMWARE_ROLE "repeater" #define FIRMWARE_ROLE "repeater"
#define PACKET_LOG_FILE "/packet_log" #define PACKET_LOG_FILE "/packet_log"
#ifdef WITH_CHANNEL_FILTER
#define CHANNEL_FILTER_FILE "/channel_filter" #define CHANNEL_FILTER_FILE "/channel_filter"
#ifndef MAX_FILTER_CHANNELS #ifndef MAX_FILTER_CHANNELS
@ -89,6 +91,7 @@ struct NeighbourInfo {
#endif #endif
#define FILTER_TERM_LEN 24 #define FILTER_TERM_LEN 24
#define FILTER_PSK_B64_LEN 48 #define FILTER_PSK_B64_LEN 48
#endif
class MyMesh : public mesh::Mesh, public CommonCLICallbacks { class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
FILESYSTEM* _fs; FILESYSTEM* _fs;
@ -124,6 +127,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
uint8_t pending_cr; uint8_t pending_cr;
int matching_peer_indexes[MAX_CLIENTS]; int matching_peer_indexes[MAX_CLIENTS];
#ifdef WITH_CHANNEL_FILTER
mesh::GroupChannel filter_channels[MAX_FILTER_CHANNELS]; mesh::GroupChannel filter_channels[MAX_FILTER_CHANNELS];
char filter_channel_psk[MAX_FILTER_CHANNELS][FILTER_PSK_B64_LEN]; char filter_channel_psk[MAX_FILTER_CHANNELS][FILTER_PSK_B64_LEN];
uint8_t num_filter_channels; uint8_t num_filter_channels;
@ -132,6 +136,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
char block_senders[MAX_FILTER_TERMS][FILTER_TERM_LEN]; char block_senders[MAX_FILTER_TERMS][FILTER_TERM_LEN];
uint8_t num_block_senders; uint8_t num_block_senders;
uint32_t n_filtered; uint32_t n_filtered;
#endif
#if defined(WITH_RS232_BRIDGE) #if defined(WITH_RS232_BRIDGE)
RS232Bridge bridge; RS232Bridge bridge;
#elif defined(WITH_ESPNOW_BRIDGE) #elif defined(WITH_ESPNOW_BRIDGE)
@ -149,10 +154,12 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
File openAppend(const char* fname); File openAppend(const char* fname);
bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]); bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]);
#ifdef WITH_CHANNEL_FILTER
bool addFilterChannel(const char* psk_b64); bool addFilterChannel(const char* psk_b64);
void loadChannelFilter(); void loadChannelFilter();
void saveChannelFilter(); void saveChannelFilter();
void handleFilterCommand(char* command, char* reply); void handleFilterCommand(char* command, char* reply);
#endif
protected: protected:
float getAirtimeBudgetFactor() const override { float getAirtimeBudgetFactor() const override {
@ -191,8 +198,10 @@ protected:
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
int searchPeersByHash(const uint8_t* hash) override; int searchPeersByHash(const uint8_t* hash) override;
#ifdef WITH_CHANNEL_FILTER
int searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel channels[], int max_matches) override; int searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel channels[], int max_matches) override;
void onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mesh::GroupChannel& channel, uint8_t* data, size_t len) override; void onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mesh::GroupChannel& channel, uint8_t* data, size_t len) override;
#endif
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override; void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len); void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len);
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override; void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;

Loading…
Cancel
Save