From 34c03bb462b3febe834575bedb2f9dae4fc7cd72 Mon Sep 17 00:00:00 2001 From: Wouter Bokslag Date: Mon, 23 Feb 2026 17:05:55 +0100 Subject: [PATCH] Added config fields for brew white/blacklist --- bins/bluestation-bs/src/main.rs | 2 + crates/tetra-config/src/stack_config_brew.rs | 21 +++++++++- crates/tetra-config/src/toml_config.rs | 5 +++ .../src/brew/components/brew_routable.rs | 40 +++++++++++++++++++ .../tetra-entities/src/brew/components/mod.rs | 2 +- crates/tetra-entities/src/brew/mod.rs | 1 + crates/tetra-entities/src/brew/worker.rs | 4 ++ 7 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 crates/tetra-entities/src/brew/components/brew_routable.rs diff --git a/bins/bluestation-bs/src/main.rs b/bins/bluestation-bs/src/main.rs index 7bd7f0e..72290d2 100644 --- a/bins/bluestation-bs/src/main.rs +++ b/bins/bluestation-bs/src/main.rs @@ -75,6 +75,8 @@ fn build_bs_stack(cfg: &mut SharedConfig) -> MessageRouter { groups: brew_cfg.groups, reconnect_delay: Duration::from_secs(brew_cfg.reconnect_delay_secs), jitter_initial_latency_frames: brew_cfg.jitter_initial_latency_frames, + whitelisted_ssi_ranges: brew_cfg.whitelisted_ssi_ranges, + blacklisted_ssi_ranges: brew_cfg.blacklisted_ssi_ranges, }; let brew_entity = BrewEntity::new(cfg.clone(), brew_config); router.register_entity(Box::new(brew_entity)); diff --git a/crates/tetra-config/src/stack_config_brew.rs b/crates/tetra-config/src/stack_config_brew.rs index d9977d1..901a9b3 100644 --- a/crates/tetra-config/src/stack_config_brew.rs +++ b/crates/tetra-config/src/stack_config_brew.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; use serde::Deserialize; +use tetra_core::ranges::SortedDisjointSsiRanges; use toml::Value; /// Brew protocol (TetraPack/BrandMeister) configuration -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone)] pub struct CfgBrew { /// TetraPack server hostname or IP pub host: String, @@ -24,6 +25,9 @@ pub struct CfgBrew { pub reconnect_delay_secs: u64, /// Extra initial jitter playout delay in frames (added on top of adaptive baseline) pub jitter_initial_latency_frames: u8, + + pub whitelisted_ssi_ranges: Option, + pub blacklisted_ssi_ranges: Option, } #[derive(Default, Deserialize)] @@ -50,6 +54,9 @@ pub struct CfgBrewDto { #[serde(default)] pub jitter_initial_latency_frames: u8, + pub whitelisted_ssi_ranges: Option>, + pub blacklisted_ssi_ranges: Option>, + #[serde(flatten)] pub extra: HashMap, } @@ -64,6 +71,16 @@ fn default_brew_reconnect_delay() -> u64 { /// Convert a CfgBrewDto (from TOML) into a CfgBrew (used in the stack config) pub fn apply_brew_patch(src: CfgBrewDto) -> CfgBrew { + let whitelist = src + .whitelisted_ssi_ranges + .map(|ranges| SortedDisjointSsiRanges::from_vec_tuple(ranges)); + let blacklist = src + .blacklisted_ssi_ranges + .map(|ranges| SortedDisjointSsiRanges::from_vec_tuple(ranges)); + assert!( + !(whitelist.is_some() && blacklist.is_some()), + "Cannot specify both whitelisted_ssi_ranges and blacklisted_ssi_ranges in brew config" + ); CfgBrew { host: src.host, port: src.port, @@ -74,5 +91,7 @@ pub fn apply_brew_patch(src: CfgBrewDto) -> CfgBrew { groups: src.groups, reconnect_delay_secs: src.reconnect_delay_secs, jitter_initial_latency_frames: src.jitter_initial_latency_frames, + whitelisted_ssi_ranges: whitelist, + blacklisted_ssi_ranges: blacklist, } } diff --git a/crates/tetra-config/src/toml_config.rs b/crates/tetra-config/src/toml_config.rs index a17766b..adb830c 100644 --- a/crates/tetra-config/src/toml_config.rs +++ b/crates/tetra-config/src/toml_config.rs @@ -4,6 +4,7 @@ use std::io::{BufReader, Read}; use std::path::Path; use serde::Deserialize; +use tetra_core::ranges::SortedDisjointSsiRanges; use toml::Value; use super::stack_config_brew::{CfgBrewDto, apply_brew_patch}; @@ -237,6 +238,10 @@ fn apply_cell_info_patch(dst: &mut CfgCellInfo, ci: CellInfoDto) { if let Some(v) = ci.frame_18_ext { dst.frame_18_ext = v; } + + if let Some(ranges) = ci.local_ssi_ranges { + dst.local_ssi_ranges = SortedDisjointSsiRanges::from_vec_tuple(ranges) + } } fn sorted_keys(map: &HashMap) -> Vec<&str> { diff --git a/crates/tetra-entities/src/brew/components/brew_routable.rs b/crates/tetra-entities/src/brew/components/brew_routable.rs new file mode 100644 index 0000000..ebedb61 --- /dev/null +++ b/crates/tetra-entities/src/brew/components/brew_routable.rs @@ -0,0 +1,40 @@ +use tetra_config::SharedConfig; + +use crate::brew::worker::BrewConfig; + +pub fn is_brew_routable(config: &SharedConfig, brew_config: &BrewConfig, ssi: u32) -> bool { + if ssi <= 90 { + // Brew doesn't route 0..=90 + return false; + } + if config.config().cell.local_ssi_ranges.contains(ssi) { + // Range overridden as local + return false; + } + + // We can either have whitelist or blacklist, not both. Check if any one present, then use that + // If none present, default to allow + + if let Some(whitelist) = &brew_config.whitelisted_ssi_ranges { + if whitelist.contains(ssi) { + // Range explicitly whitelisted for routing to Brew + return true; + } else { + // Not in whitelist - block routing to Brew + return false; + } + } + + if let Some(blacklist) = &brew_config.blacklisted_ssi_ranges { + if blacklist.contains(ssi) { + // Range explicitly blacklisted from routing to Brew + return false; + } else { + // Not in blacklist - allow routing to Brew + return true; + } + } + + // No whitelist or blacklist present, default to allow + true +} diff --git a/crates/tetra-entities/src/brew/components/mod.rs b/crates/tetra-entities/src/brew/components/mod.rs index ae2125b..14f8bc8 100644 --- a/crates/tetra-entities/src/brew/components/mod.rs +++ b/crates/tetra-entities/src/brew/components/mod.rs @@ -1 +1 @@ -pub mod socket_health; +pub mod brew_routable; diff --git a/crates/tetra-entities/src/brew/mod.rs b/crates/tetra-entities/src/brew/mod.rs index 92ce7f1..9026ce9 100644 --- a/crates/tetra-entities/src/brew/mod.rs +++ b/crates/tetra-entities/src/brew/mod.rs @@ -1,5 +1,6 @@ //! Brew protocol integration for TETRA group call bridging via TetraPack/BrandMeister WebSocket API +pub mod components; pub mod entity; pub mod protocol; pub mod worker; diff --git a/crates/tetra-entities/src/brew/worker.rs b/crates/tetra-entities/src/brew/worker.rs index 46e1155..cb51b83 100644 --- a/crates/tetra-entities/src/brew/worker.rs +++ b/crates/tetra-entities/src/brew/worker.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use crossbeam_channel::{Receiver, Sender}; +use tetra_core::ranges::SortedDisjointSsiRanges; use tungstenite::{Message, WebSocket, stream::MaybeTlsStream}; use uuid::Uuid; @@ -97,6 +98,9 @@ pub struct BrewConfig { pub reconnect_delay: Duration, /// Extra initial jitter playout delay in frames (added on top of adaptive baseline) pub jitter_initial_latency_frames: u8, + + pub whitelisted_ssi_ranges: Option, + pub blacklisted_ssi_ranges: Option, } // ─── TLS helper ──────────────────────────────────────────────────