mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-17 23:08:23 +00:00
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:
@ -0,0 +1,5 @@
|
||||
alter table tenant
|
||||
drop column private_gateways_down;
|
||||
|
||||
alter table tenant
|
||||
rename column private_gateways_up to private_gateways;
|
@ -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;
|
@ -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,
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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(),
|
||||
|
@ -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, ());
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -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(),
|
||||
});
|
||||
|
@ -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(),
|
||||
|
@ -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(®ion_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());
|
||||
}
|
||||
|
Reference in New Issue
Block a user