Skip to content

Commit e988aa3

Browse files
committed
Implement stats message.
This message is used to indicate that a Relay Gateway is still alive and to collect the path between Relay Gateway and Border Gateway (in case there are multiple Relay Gateways in-between).
1 parent eff371f commit e988aa3

15 files changed

+587
-7
lines changed

Cargo.lock

Lines changed: 19 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616
"usage",
1717
"derive",
1818
] }
19-
chirpstack_api = { version = "4.8", default-features = false }
19+
chirpstack_api = { version = "4.9.0-test.1", default-features = false }
2020
lrwn_filters = { version = "4.7", features = ["serde"] }
2121
log = "0.4"
2222
simple_logger = "5.0"
2323
syslog = "6.1"
2424
toml = "0.8"
2525
handlebars = "5.1"
2626
anyhow = "1.0"
27+
humantime-serde = "1.1"
2728
serde = { version = "1.0", features = ["derive"] }
2829
tokio = { version = "1.37", features = [
2930
"macros",

src/cache.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::time::UNIX_EPOCH;
12
use std::{collections::VecDeque, usize};
23

34
use crate::packets;
@@ -37,6 +38,7 @@ impl<T> Cache<T> {
3738
pub struct PayloadCache {
3839
p_type: packets::PayloadType,
3940
uplink_id: u16,
41+
timestamp: u32,
4042
relay_id: [u8; 4],
4143
}
4244

@@ -49,11 +51,23 @@ impl From<&packets::MeshPacket> for PayloadCache {
4951
p_type,
5052
uplink_id: v.metadata.uplink_id,
5153
relay_id: v.relay_id,
54+
timestamp: 0,
5255
},
5356
packets::Payload::Downlink(v) => PayloadCache {
5457
p_type,
5558
uplink_id: v.metadata.uplink_id,
5659
relay_id: v.relay_id,
60+
timestamp: 0,
61+
},
62+
packets::Payload::Stats(v) => PayloadCache {
63+
p_type,
64+
uplink_id: 0,
65+
relay_id: v.relay_id,
66+
timestamp: v
67+
.timestamp
68+
.duration_since(UNIX_EPOCH)
69+
.unwrap_or_default()
70+
.as_secs() as u32,
5771
},
5872
}
5973
}

src/cmd/configfile.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ pub fn run() {
3838
# uplinks and forward these to the proxy API, rather than relaying these.
3939
border_gateway={{ mesh.border_gateway }}
4040
41+
# Stats interval (Relay Gateway only).
42+
#
43+
# This defines the interval in which a Relay Gateway (border_gateway=false)
44+
# will emit stats messages.
45+
stats_interval="{{ mesh.stats_interval }}"
46+
4147
# Max hop count.
4248
#
4349
# This defines the maximum number of hops a relayed payload will pass.

src/cmd/root.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use signal_hook::consts::signal::*;
44
use signal_hook_tokio::Signals;
55

66
use crate::config::Configuration;
7-
use crate::{backend, proxy};
7+
use crate::{backend, proxy, stats};
88

99
pub async fn run(conf: &Configuration) -> Result<()> {
1010
proxy::setup(conf).await?;
1111
backend::setup(conf).await?;
12+
stats::setup(conf).await?;
1213

1314
let mut signals = Signals::new([SIGINT, SIGTERM])?;
1415
let handle = signals.handle();

src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fs;
22
use std::sync::{Arc, Mutex};
3+
use std::time::Duration;
34

45
use anyhow::Result;
56
use once_cell::sync::OnceCell;
@@ -51,6 +52,8 @@ impl Default for Logging {
5152
#[serde(default)]
5253
pub struct Mesh {
5354
pub signing_key: Aes128Key,
55+
#[serde(with = "humantime_serde")]
56+
pub stats_interval: Duration,
5457
pub frequencies: Vec<u32>,
5558
pub data_rate: DataRate,
5659
pub tx_power: i32,
@@ -65,6 +68,7 @@ impl Default for Mesh {
6568
fn default() -> Self {
6669
Mesh {
6770
signing_key: Aes128Key::null(),
71+
stats_interval: Duration::from_secs(300),
6872
frequencies: vec![868100000, 868300000, 868500000],
6973
data_rate: DataRate {
7074
modulation: Modulation::LORA,

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ pub mod logging;
1111
pub mod mesh;
1212
pub mod packets;
1313
pub mod proxy;
14+
pub mod stats;

src/mesh.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ pub async fn handle_mesh(border_gateway: bool, pl: gw::UplinkFrame) -> Result<()
5454
};
5555

5656
match border_gateway {
57-
// In this case we only care about proxy-ing relayed uplinks
57+
// Proxy relayed uplink
5858
true => match packet.mhdr.payload_type {
5959
PayloadType::Uplink => proxy_uplink_mesh_packet(&pl, packet).await,
60+
PayloadType::Stats => proxy_stats_mesh_packet(&pl, packet).await,
6061
_ => Ok(()),
6162
},
6263
false => relay_mesh_packet(&pl, packet).await,
@@ -151,11 +152,35 @@ async fn proxy_uplink_mesh_packet(pl: &gw::UplinkFrame, packet: MeshPacket) -> R
151152
proxy::send_uplink(&pl).await
152153
}
153154

155+
async fn proxy_stats_mesh_packet(pl: &gw::UplinkFrame, packet: MeshPacket) -> Result<()> {
156+
let mesh_pl = match &packet.payload {
157+
Payload::Stats(v) => v,
158+
_ => {
159+
return Err(anyhow!("Expected Stats payload"));
160+
}
161+
};
162+
163+
info!(
164+
"Unwrapping relay stats packet, uplink_id: {}, mesh_packet: {}",
165+
pl.rx_info.as_ref().map(|v| v.uplink_id).unwrap_or_default(),
166+
packet
167+
);
168+
169+
let stats_pl = gw::MeshStats {
170+
gateway_id: hex::encode(backend::get_gateway_id().await?),
171+
relay_id: hex::encode(mesh_pl.relay_id),
172+
relay_path: mesh_pl.relay_path.iter().map(hex::encode).collect(),
173+
time: Some(mesh_pl.timestamp.into()),
174+
};
175+
176+
proxy::send_mesh_stats(&stats_pl).await
177+
}
178+
154179
async fn relay_mesh_packet(_: &gw::UplinkFrame, mut packet: MeshPacket) -> Result<()> {
155180
let conf = config::get();
156181
let relay_id = backend::get_relay_id().await?;
157182

158-
match &packet.payload {
183+
match &mut packet.payload {
159184
packets::Payload::Uplink(pl) => {
160185
if pl.relay_id == relay_id {
161186
trace!("Dropping packet as this relay was the sender");
@@ -203,13 +228,27 @@ async fn relay_mesh_packet(_: &gw::UplinkFrame, mut packet: MeshPacket) -> Resul
203228
return helpers::tx_ack_to_err(&backend::send_downlink(&pl).await?);
204229
}
205230
}
231+
packets::Payload::Stats(pl) => {
232+
if pl.relay_id == relay_id {
233+
trace!("Dropping packet as this relay was the sender");
234+
235+
// Drop the packet, as we are the sender.
236+
return Ok(());
237+
}
238+
239+
// Add our Relay ID to the path.
240+
pl.relay_path.push(relay_id);
241+
}
206242
}
207243

208244
// In any other case, we increment the hop_count and re-transmit the mesh encapsulated
209245
// packet.
210246

211247
// Increment hop count.
212248
packet.mhdr.hop_count += 1;
249+
250+
// We need to re-set the MIC as we have changed the payload by incrementing
251+
// the hop count (and in casee of stats, we have modified the Relay path).
213252
packet.set_mic(conf.mesh.signing_key)?;
214253

215254
if packet.mhdr.hop_count > conf.mesh.max_hop_count {
@@ -429,7 +468,7 @@ async fn relay_downlink_lora_packet(pl: &gw::DownlinkFrame) -> Result<gw::Downli
429468
})
430469
}
431470

432-
fn get_mesh_frequency(conf: &Configuration) -> Result<u32> {
471+
pub fn get_mesh_frequency(conf: &Configuration) -> Result<u32> {
433472
if conf.mesh.frequencies.is_empty() {
434473
return Err(anyhow!("No mesh frequencies are configured"));
435474
}

0 commit comments

Comments
 (0)