Split private gateways under tenant in uplink / downlink.

This makes it possible to share uplink data with other tenants, but do
not allow other tenants to use these gateways for downlinks.
This commit is contained in:
Orne Brocaar
2023-02-14 13:36:21 +00:00
parent d3defb7dff
commit f776dd3898
39 changed files with 1090 additions and 527 deletions

View File

@ -0,0 +1,5 @@
alter table tenant
drop column private_gateways_down;
alter table tenant
rename column private_gateways_up to private_gateways;

View File

@ -0,0 +1,8 @@
alter table tenant
rename column private_gateways to private_gateways_up;
alter table tenant
add column private_gateways_down boolean not null default false;
alter table tenant
alter column private_gateways_down drop default;

View File

@ -238,7 +238,8 @@ async fn _handle_pr_start_req_join(
phy_payload: phy,
tx_info,
rx_info_set: rx_info,
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name,
region_config_id,
@ -269,7 +270,8 @@ async fn _handle_pr_start_req_data(
phy_payload: phy,
tx_info,
rx_info_set: rx_info,
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name,
region_config_id,
@ -444,7 +446,8 @@ async fn _handle_xmit_data_req(
phy_payload: phy,
tx_info,
rx_info_set: rx_info,
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name,
region_config_id,

View File

@ -47,7 +47,8 @@ impl TenantService for Tenant {
can_have_gateways: req_tenant.can_have_gateways,
max_device_count: req_tenant.max_device_count as i32,
max_gateway_count: req_tenant.max_gateway_count as i32,
private_gateways: req_tenant.private_gateways,
private_gateways_up: req_tenant.private_gateways_up,
private_gateways_down: req_tenant.private_gateways_down,
..Default::default()
};
@ -86,7 +87,8 @@ impl TenantService for Tenant {
can_have_gateways: t.can_have_gateways,
max_gateway_count: t.max_gateway_count as u32,
max_device_count: t.max_device_count as u32,
private_gateways: t.private_gateways,
private_gateways_up: t.private_gateways_up,
private_gateways_down: t.private_gateways_down,
}),
created_at: Some(helpers::datetime_to_prost_timestamp(&t.created_at)),
updated_at: Some(helpers::datetime_to_prost_timestamp(&t.updated_at)),
@ -124,7 +126,8 @@ impl TenantService for Tenant {
can_have_gateways: req_tenant.can_have_gateways,
max_device_count: req_tenant.max_device_count as i32,
max_gateway_count: req_tenant.max_gateway_count as i32,
private_gateways: req_tenant.private_gateways,
private_gateways_up: req_tenant.private_gateways_up,
private_gateways_down: req_tenant.private_gateways_down,
..Default::default()
})
.await
@ -219,7 +222,8 @@ impl TenantService for Tenant {
updated_at: Some(helpers::datetime_to_prost_timestamp(&t.updated_at)),
name: t.name.clone(),
can_have_gateways: t.can_have_gateways,
private_gateways: t.private_gateways,
private_gateways_up: t.private_gateways_up,
private_gateways_down: t.private_gateways_down,
max_gateway_count: t.max_gateway_count as u32,
max_device_count: t.max_device_count as u32,
})

View File

@ -217,6 +217,7 @@ impl Data {
trace!("Selecting downlink gateway");
let gw_down = helpers::select_downlink_gateway(
Some(self.tenant.id),
&self.device_session.region_config_id,
self.network_conf.gateway_prefer_min_margin,
self.device_gateway_rx_info.as_mut().unwrap(),

View File

@ -2,23 +2,49 @@ use std::str::FromStr;
use anyhow::Result;
use rand::seq::SliceRandom;
use uuid::Uuid;
use chirpstack_api::gw;
use chirpstack_api::{gw, internal};
use lrwn::region::DataRateModulation;
use crate::config;
use crate::region;
// Returns the gateway to use for downlink.
// In the current implementation it will sort the given slice based on SNR / RSSI,
// and return:
// It will filter out private gateways (gateways from a different tenant ID,
// that do not allow downlinks). The result will be sorted based on SNR / RSSI.
// The returned value is:
// * A random item from the elements with an SNR > minSNR
// * The first item of the sorted slice (failing the above)
// * An error in case no gateways are available
pub fn select_downlink_gateway(
tenant_id: Option<Uuid>,
region_config_id: &str,
min_snr_margin: f32,
rx_info: &mut chirpstack_api::internal::DeviceGatewayRxInfo,
) -> Result<chirpstack_api::internal::DeviceGatewayRxInfoItem> {
rx_info: &mut internal::DeviceGatewayRxInfo,
) -> Result<internal::DeviceGatewayRxInfoItem> {
rx_info.items = rx_info
.items
.iter()
.filter(|rx_info| {
if let Some(tenant_id) = &tenant_id {
if tenant_id.as_bytes().to_vec() == rx_info.tenant_id {
// The tenant is the same as the gateway tenant.
true
} else {
// If tenant_id is different, filter out rx_info elements that have
// is_private_down=true.
!rx_info.is_private_down
}
} else {
// If tenant_id is None, filter out rx_info elements that have
// is_private_down=true.
!rx_info.is_private_down
}
})
.cloned()
.collect();
if rx_info.items.is_empty() {
return Err(anyhow!("rx_info.items can not be empty"));
}
@ -96,11 +122,13 @@ mod tests {
use std::collections::HashMap;
use super::*;
use crate::storage::tenant;
use crate::test;
struct Test {
min_snr_margin: f32,
rx_info: chirpstack_api::internal::DeviceGatewayRxInfo,
tenant_id: Option<Uuid>,
rx_info: internal::DeviceGatewayRxInfo,
expected_gws: Vec<Vec<u8>>,
}
@ -108,13 +136,21 @@ mod tests {
async fn test_select_downlink_gateway() {
let _guard = test::prepare().await;
let t = tenant::create(tenant::Tenant {
name: "test-tenant".into(),
..Default::default()
})
.await
.unwrap();
let tests = vec![
// single item
Test {
tenant_id: None,
min_snr_margin: 0.0,
rx_info: chirpstack_api::internal::DeviceGatewayRxInfo {
rx_info: internal::DeviceGatewayRxInfo {
dr: 0,
items: vec![chirpstack_api::internal::DeviceGatewayRxInfoItem {
items: vec![internal::DeviceGatewayRxInfoItem {
lora_snr: -5.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
..Default::default()
@ -125,16 +161,17 @@ mod tests {
},
// two items, below min snr
Test {
tenant_id: None,
min_snr_margin: 5.0,
rx_info: chirpstack_api::internal::DeviceGatewayRxInfo {
rx_info: internal::DeviceGatewayRxInfo {
dr: 2, // -15 is required
items: vec![
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -12.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
..Default::default()
},
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -11.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
..Default::default()
@ -146,16 +183,17 @@ mod tests {
},
// two items, one below min snr
Test {
tenant_id: None,
min_snr_margin: 5.0,
rx_info: chirpstack_api::internal::DeviceGatewayRxInfo {
rx_info: internal::DeviceGatewayRxInfo {
dr: 2, // -15 is required
items: vec![
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -12.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
..Default::default()
},
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -10.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
..Default::default()
@ -167,26 +205,27 @@ mod tests {
},
// four items, two below min snr
Test {
tenant_id: None,
min_snr_margin: 5.0,
rx_info: chirpstack_api::internal::DeviceGatewayRxInfo {
rx_info: internal::DeviceGatewayRxInfo {
dr: 2, // -15 is required
items: vec![
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -12.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
..Default::default()
},
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -11.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
..Default::default()
},
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -10.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03],
..Default::default()
},
chirpstack_api::internal::DeviceGatewayRxInfoItem {
internal::DeviceGatewayRxInfoItem {
lora_snr: -9.0,
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04],
..Default::default()
@ -199,6 +238,74 @@ mod tests {
vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04],
],
},
// is_private_down is set, first gateway matches tenant.
Test {
tenant_id: Some(t.id),
min_snr_margin: 0.0,
rx_info: internal::DeviceGatewayRxInfo {
items: vec![
internal::DeviceGatewayRxInfoItem {
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
is_private_down: true,
tenant_id: t.id.as_bytes().to_vec(),
..Default::default()
},
internal::DeviceGatewayRxInfoItem {
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
is_private_down: true,
tenant_id: Uuid::new_v4().as_bytes().to_vec(),
..Default::default()
},
],
..Default::default()
},
expected_gws: vec![vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]],
},
// is_private_down is set, second gateway matches tenant.
Test {
tenant_id: Some(t.id),
min_snr_margin: 0.0,
rx_info: internal::DeviceGatewayRxInfo {
items: vec![
internal::DeviceGatewayRxInfoItem {
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
is_private_down: true,
tenant_id: Uuid::new_v4().as_bytes().to_vec(),
..Default::default()
},
internal::DeviceGatewayRxInfoItem {
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
is_private_down: true,
tenant_id: t.id.as_bytes().to_vec(),
..Default::default()
},
],
..Default::default()
},
expected_gws: vec![vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]],
},
// is_private_down is set for one gateway, no tenant id given.
Test {
tenant_id: None,
min_snr_margin: 0.0,
rx_info: internal::DeviceGatewayRxInfo {
items: vec![
internal::DeviceGatewayRxInfoItem {
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
is_private_down: true,
tenant_id: t.id.as_bytes().to_vec(),
..Default::default()
},
internal::DeviceGatewayRxInfoItem {
gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
is_private_down: false,
..Default::default()
},
],
..Default::default()
},
expected_gws: vec![vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]],
},
];
for test in &tests {
@ -211,8 +318,13 @@ mod tests {
}
for _ in 0..100 {
let out =
select_downlink_gateway(&"eu868", test.min_snr_margin, &mut rx_info).unwrap();
let out = select_downlink_gateway(
test.tenant_id,
&"eu868",
test.min_snr_margin,
&mut rx_info,
)
.unwrap();
gw_map.insert(out.gateway_id, ());
}

View File

@ -1,20 +1,22 @@
use std::str::FromStr;
use std::sync::Arc;
use anyhow::{Context, Result};
use rand::Rng;
use tracing::{span, trace, Instrument, Level};
use lrwn::PhyPayload;
use lrwn::{PhyPayload, EUI64};
use super::helpers;
use crate::gateway::backend::send_downlink;
use crate::storage::{device, downlink_frame};
use crate::storage::{device, downlink_frame, tenant};
use crate::uplink::UplinkFrameSet;
use crate::{config, region};
use chirpstack_api::{gw, internal};
pub struct JoinAccept<'a> {
uplink_frame_set: &'a UplinkFrameSet,
tenant: &'a tenant::Tenant,
device: &'a device::Device,
device_session: &'a internal::DeviceSession,
join_accept: &'a PhyPayload,
@ -29,24 +31,27 @@ pub struct JoinAccept<'a> {
impl JoinAccept<'_> {
pub async fn handle(
ufs: &UplinkFrameSet,
tenant: &tenant::Tenant,
device: &device::Device,
device_session: &internal::DeviceSession,
join_accept: &PhyPayload,
) -> Result<()> {
let span = span!(Level::TRACE, "join_accept", downlink_id = %ufs.uplink_set_id);
let fut = JoinAccept::_handle(ufs, device, device_session, join_accept);
let fut = JoinAccept::_handle(ufs, tenant, device, device_session, join_accept);
fut.instrument(span).await
}
async fn _handle(
ufs: &UplinkFrameSet,
tenant: &tenant::Tenant,
device: &device::Device,
device_session: &internal::DeviceSession,
join_accept: &PhyPayload,
) -> Result<()> {
let mut ctx = JoinAccept {
uplink_frame_set: ufs,
tenant,
device,
device_session,
join_accept,
@ -74,26 +79,45 @@ impl JoinAccept<'_> {
fn set_device_gateway_rx_info(&mut self) -> Result<()> {
trace!("Set device-gateway rx-info");
let mut d_gw_rx_info = chirpstack_api::internal::DeviceGatewayRxInfo {
self.device_gateway_rx_info = Some(internal::DeviceGatewayRxInfo {
dev_eui: self.device.dev_eui.to_be_bytes().to_vec(),
dr: self.uplink_frame_set.dr as u32,
items: vec![],
};
items: self
.uplink_frame_set
.rx_info_set
.iter()
.map(|rx_info| {
let gw_id = EUI64::from_str(&rx_info.gateway_id).unwrap_or_default();
for rx_info in &self.uplink_frame_set.rx_info_set {
d_gw_rx_info
.items
.push(chirpstack_api::internal::DeviceGatewayRxInfoItem {
gateway_id: hex::decode(&rx_info.gateway_id)?,
rssi: rx_info.rssi,
lora_snr: rx_info.snr,
antenna: rx_info.antenna,
board: rx_info.board,
context: rx_info.context.clone(),
});
}
self.device_gateway_rx_info = Some(d_gw_rx_info);
internal::DeviceGatewayRxInfoItem {
gateway_id: gw_id.to_vec(),
rssi: rx_info.rssi,
lora_snr: rx_info.snr,
antenna: rx_info.antenna,
board: rx_info.board,
context: rx_info.context.clone(),
is_private_up: self
.uplink_frame_set
.gateway_private_up_map
.get(&gw_id)
.cloned()
.unwrap_or_default(),
is_private_down: self
.uplink_frame_set
.gateway_private_down_map
.get(&gw_id)
.cloned()
.unwrap_or_default(),
tenant_id: self
.uplink_frame_set
.gateway_tenant_id_map
.get(&gw_id)
.map(|v| v.into_bytes().to_vec())
.unwrap_or_else(|| Vec::new()),
}
})
.collect(),
});
Ok(())
}
@ -102,6 +126,7 @@ impl JoinAccept<'_> {
trace!("Select downlink gateway");
let gw_down = helpers::select_downlink_gateway(
Some(self.tenant.id),
&self.uplink_frame_set.region_config_id,
self.network_conf.gateway_prefer_min_margin,
self.device_gateway_rx_info.as_mut().unwrap(),

View File

@ -1,3 +1,4 @@
use std::str::FromStr;
use std::sync::Arc;
use anyhow::{Context, Result};
@ -10,6 +11,7 @@ use crate::uplink::UplinkFrameSet;
use crate::{config, gateway, region};
use backend::DLMetaData;
use chirpstack_api::{gw, internal};
use lrwn::EUI64;
pub struct PassiveRoamingDownlink {
uplink_frame_set: UplinkFrameSet,
@ -63,18 +65,41 @@ impl PassiveRoamingDownlink {
.uplink_frame_set
.rx_info_set
.iter()
.map(|rx_info| internal::DeviceGatewayRxInfoItem {
gateway_id: hex::decode(&rx_info.gateway_id).unwrap(),
rssi: rx_info.rssi,
lora_snr: rx_info.snr,
antenna: rx_info.antenna,
board: rx_info.board,
context: rx_info.context.clone(),
.map(|rx_info| {
let gw_id = EUI64::from_str(&rx_info.gateway_id).unwrap_or_default();
internal::DeviceGatewayRxInfoItem {
gateway_id: gw_id.to_vec(),
rssi: rx_info.rssi,
lora_snr: rx_info.snr,
antenna: rx_info.antenna,
board: rx_info.board,
context: rx_info.context.clone(),
is_private_up: self
.uplink_frame_set
.gateway_private_up_map
.get(&gw_id)
.cloned()
.unwrap_or_default(),
is_private_down: self
.uplink_frame_set
.gateway_private_down_map
.get(&gw_id)
.cloned()
.unwrap_or_default(),
tenant_id: self
.uplink_frame_set
.gateway_tenant_id_map
.get(&gw_id)
.map(|v| v.into_bytes().to_vec())
.unwrap_or_else(|| Vec::new()),
}
})
.collect(),
};
let gw_down = helpers::select_downlink_gateway(
None,
&self.uplink_frame_set.region_config_id,
self.network_conf.gateway_prefer_min_margin,
&mut dev_gw_rx_info,

View File

@ -139,7 +139,8 @@ pub mod test {
time: Some(rx_time.into()),
..Default::default()
}],
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name: lrwn::region::CommonName::EU868,
region_config_id: "eu868".into(),

View File

@ -68,7 +68,8 @@ pub mod test {
time: Some(rx_time.into()),
..Default::default()
}],
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name: lrwn::region::CommonName::EU868,
region_config_id: "eu868".into(),

View File

@ -336,7 +336,8 @@ pub mod test {
},
tx_info: Default::default(),
rx_info_set: vec![],
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name: lrwn::region::CommonName::EU868,
region_config_id: "eu868".into(),

View File

@ -97,7 +97,8 @@ pub mod test {
..Default::default()
},
],
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
region_common_name: lrwn::region::CommonName::EU868,
region_config_id: "eu868".into(),

View File

@ -181,7 +181,8 @@ pub mod test {
},
tx_info: Default::default(),
rx_info_set: Default::default(),
gateway_private_map: Default::default(),
gateway_private_up_map: Default::default(),
gateway_private_down_map: Default::default(),
gateway_tenant_id_map: Default::default(),
region_common_name: lrwn::region::CommonName::EU868,
region_config_id: "eu868".into(),

View File

@ -137,7 +137,8 @@ pub async fn create(d: Device) -> Result<Device, Error> {
tenant::dsl::can_have_gateways,
tenant::dsl::max_device_count,
tenant::dsl::max_gateway_count,
tenant::dsl::private_gateways,
tenant::dsl::private_gateways_up,
tenant::dsl::private_gateways_down,
))
.inner_join(application::table)
.filter(application::dsl::id.eq(&d.application_id))

View File

@ -64,7 +64,8 @@ pub struct GatewayMeta {
pub latitude: f64,
pub longitude: f64,
pub altitude: f32,
pub is_private: bool,
pub is_private_up: bool,
pub is_private_down: bool,
}
#[derive(Default, Clone)]
@ -396,7 +397,8 @@ pub async fn get_meta(gateway_id: &EUI64) -> Result<GatewayMeta, Error> {
gateway::latitude,
gateway::longitude,
gateway::altitude,
tenant::private_gateways,
tenant::private_gateways_up,
tenant::private_gateways_down,
))
.filter(gateway::dsl::gateway_id.eq(&gateway_id))
.first(&mut c)

View File

@ -236,7 +236,8 @@ diesel::table! {
can_have_gateways -> Bool,
max_device_count -> Int4,
max_gateway_count -> Int4,
private_gateways -> Bool,
private_gateways_up -> Bool,
private_gateways_down -> Bool,
}
}

View File

@ -21,7 +21,8 @@ pub struct Tenant {
pub can_have_gateways: bool,
pub max_device_count: i32,
pub max_gateway_count: i32,
pub private_gateways: bool,
pub private_gateways_up: bool,
pub private_gateways_down: bool,
}
impl Tenant {
@ -46,7 +47,8 @@ impl Default for Tenant {
can_have_gateways: false,
max_device_count: 0,
max_gateway_count: 0,
private_gateways: false,
private_gateways_up: false,
private_gateways_down: false,
}
}
}
@ -141,7 +143,8 @@ pub async fn update(t: Tenant) -> Result<Tenant, Error> {
tenant::can_have_gateways.eq(&t.can_have_gateways),
tenant::max_device_count.eq(&t.max_device_count),
tenant::max_gateway_count.eq(&t.max_gateway_count),
tenant::private_gateways.eq(&t.private_gateways),
tenant::private_gateways_up.eq(&t.private_gateways_up),
tenant::private_gateways_down.eq(&t.private_gateways_down),
))
.get_result(&mut c)
.map_err(|e| Error::from_diesel(e, t.id.to_string()))
@ -403,7 +406,8 @@ pub mod test {
can_have_gateways: true,
max_device_count: 20,
max_gateway_count: 10,
private_gateways: true,
private_gateways_up: true,
private_gateways_down: true,
};
create(t).await.unwrap()
}

View File

@ -33,7 +33,7 @@ async fn test_gateway_filtering() {
let t_a = tenant::create(tenant::Tenant {
name: "tenant-a".into(),
private_gateways: true,
private_gateways_up: true,
can_have_gateways: true,
..Default::default()
})
@ -41,7 +41,7 @@ async fn test_gateway_filtering() {
.unwrap();
let t_b = tenant::create(tenant::Tenant {
name: "tenant-b".into(),
private_gateways: true,
private_gateways_up: true,
can_have_gateways: true,
..Default::default()
})

View File

@ -31,7 +31,7 @@ async fn test_gateway_filtering() {
let _guard = test::prepare().await;
let t_a = tenant::create(tenant::Tenant {
name: "tenant-a".into(),
private_gateways: true,
private_gateways_up: true,
can_have_gateways: true,
..Default::default()
})
@ -39,7 +39,7 @@ async fn test_gateway_filtering() {
.unwrap();
let t_b = tenant::create(tenant::Tenant {
name: "tenant-b".into(),
private_gateways: true,
private_gateways_up: true,
can_have_gateways: true,
..Default::default()
})

View File

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::str::FromStr;
use anyhow::{Context, Result};
use chrono::{DateTime, Duration, Local, Utc};
@ -18,7 +19,7 @@ use crate::storage::{
};
use crate::{codec, config, downlink, framelog, integration, maccommand, metalog, region};
use chirpstack_api::{api, common, integration as integration_pb, internal, meta};
use lrwn::AES128Key;
use lrwn::{AES128Key, EUI64};
pub struct Data {
uplink_frame_set: UplinkFrameSet,
@ -260,13 +261,35 @@ impl Data {
.uplink_frame_set
.rx_info_set
.iter()
.map(|rx_info| internal::DeviceGatewayRxInfoItem {
gateway_id: hex::decode(&rx_info.gateway_id).unwrap(),
rssi: rx_info.rssi,
lora_snr: rx_info.snr,
antenna: rx_info.antenna,
board: rx_info.board,
context: rx_info.context.clone(),
.map(|rx_info| {
let gw_id = EUI64::from_str(&rx_info.gateway_id).unwrap_or_default();
internal::DeviceGatewayRxInfoItem {
gateway_id: gw_id.to_vec(),
rssi: rx_info.rssi,
lora_snr: rx_info.snr,
antenna: rx_info.antenna,
board: rx_info.board,
context: rx_info.context.clone(),
is_private_up: self
.uplink_frame_set
.gateway_private_up_map
.get(&gw_id)
.cloned()
.unwrap_or_default(),
is_private_down: self
.uplink_frame_set
.gateway_private_down_map
.get(&gw_id)
.cloned()
.unwrap_or_default(),
tenant_id: self
.uplink_frame_set
.gateway_tenant_id_map
.get(&gw_id)
.map(|v| v.into_bytes().to_vec())
.unwrap_or_else(|| Vec::new()),
}
})
.collect(),
});

View File

@ -730,6 +730,7 @@ impl JoinRequest {
trace!("Starting downlink join-accept flow");
downlink::join::JoinAccept::handle(
&self.uplink_frame_set,
self.tenant.as_ref().unwrap(),
self.device.as_ref().unwrap(),
self.device_session.as_ref().unwrap(),
self.join_accept.as_ref().unwrap(),

View File

@ -36,7 +36,8 @@ pub struct UplinkFrameSet {
pub phy_payload: PhyPayload,
pub tx_info: gw::UplinkTxInfo,
pub rx_info_set: Vec<gw::UplinkRxInfo>,
pub gateway_private_map: HashMap<EUI64, bool>,
pub gateway_private_up_map: HashMap<EUI64, bool>,
pub gateway_private_down_map: HashMap<EUI64, bool>,
pub gateway_tenant_id_map: HashMap<EUI64, Uuid>,
pub region_common_name: CommonName,
pub region_config_id: String,
@ -271,7 +272,8 @@ pub async fn handle_uplink(deduplication_id: Uuid, uplink: gw::UplinkFrameSet) -
phy_payload: PhyPayload::from_slice(&uplink.phy_payload)?,
tx_info: uplink.tx_info.context("tx_info must not be None")?,
rx_info_set: uplink.rx_info,
gateway_private_map: HashMap::new(),
gateway_private_up_map: HashMap::new(),
gateway_private_down_map: HashMap::new(),
gateway_tenant_id_map: HashMap::new(),
roaming_meta_data: None,
};
@ -320,7 +322,8 @@ async fn update_gateway_metadata(ufs: &mut UplinkFrameSet) -> Result<()> {
Err(e) => {
if conf.gateway.allow_unknown_gateways {
if let StorageError::NotFound(_) = e {
ufs.gateway_private_map.insert(gw_id, false);
ufs.gateway_private_up_map.insert(gw_id, false);
ufs.gateway_private_down_map.insert(gw_id, false);
continue;
}
}
@ -341,7 +344,10 @@ async fn update_gateway_metadata(ufs: &mut UplinkFrameSet) -> Result<()> {
..Default::default()
});
ufs.gateway_private_map.insert(gw_id, gw_meta.is_private);
ufs.gateway_private_up_map
.insert(gw_id, gw_meta.is_private_up);
ufs.gateway_private_down_map
.insert(gw_id, gw_meta.is_private_down);
ufs.gateway_tenant_id_map.insert(gw_id, gw_meta.tenant_id);
}
@ -361,9 +367,9 @@ fn filter_rx_info_by_tenant_id(tenant_id: &Uuid, uplink: &mut UplinkFrameSet) ->
let force_gws_private = config::get_force_gws_private(&region_config_id)?;
if !(*uplink
.gateway_private_map
.gateway_private_up_map
.get(&gateway_id)
.ok_or_else(|| anyhow!("gateway_id missing in gateway_private_map"))?
.ok_or_else(|| anyhow!("gateway_id missing in gateway_private_up_map"))?
|| force_gws_private)
|| uplink
.gateway_tenant_id_map
@ -389,9 +395,9 @@ fn filter_rx_info_by_public_only(uplink: &mut UplinkFrameSet) -> Result<()> {
for rx_info in &uplink.rx_info_set {
let gateway_id = EUI64::from_str(&rx_info.gateway_id)?;
if !(*uplink
.gateway_private_map
.gateway_private_up_map
.get(&gateway_id)
.ok_or_else(|| anyhow!("gateway_id missing in gateway_private_map"))?)
.ok_or_else(|| anyhow!("gateway_id missing in gateway_private_up_map"))?)
{
rx_info_set.push(rx_info.clone());
}