mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-04-15 15:49:15 +00:00
Compare commits
5 Commits
api/go/v4.
...
migrate_ds
Author | SHA1 | Date | |
---|---|---|---|
411cd681a9 | |||
8a986d04ce | |||
da6b7e1b37 | |||
fae182aa3d | |||
5c3624cfbe |
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -876,6 +876,7 @@ dependencies = [
|
||||
name = "chirpstack_api"
|
||||
version = "4.7.0-test.3"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"hex",
|
||||
"pbjson",
|
||||
"pbjson-build",
|
||||
|
6
api/proto/internal/internal.proto
vendored
6
api/proto/internal/internal.proto
vendored
@ -7,15 +7,9 @@ import "gw/gw.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message DeviceSession {
|
||||
// Device EUI.
|
||||
bytes dev_eui = 1;
|
||||
|
||||
// Device address.
|
||||
bytes dev_addr = 2;
|
||||
|
||||
// Join EUI.
|
||||
bytes join_eui = 3;
|
||||
|
||||
// LoRaWAN mac-version.
|
||||
common.MacVersion mac_version = 4;
|
||||
|
||||
|
2
api/rust/Cargo.toml
vendored
2
api/rust/Cargo.toml
vendored
@ -12,6 +12,7 @@ edition = "2021"
|
||||
default = ["api", "json"]
|
||||
api = ["tonic/transport", "tonic-build/transport", "tokio"]
|
||||
json = ["pbjson", "pbjson-types", "serde"]
|
||||
diesel = ["dep:diesel"]
|
||||
internal = []
|
||||
|
||||
[dependencies]
|
||||
@ -25,6 +26,7 @@ tokio = { version = "1.32", features = ["macros"], optional = true }
|
||||
pbjson = { version = "0.6", optional = true }
|
||||
pbjson-types = { version = "0.6", optional = true }
|
||||
serde = { version = "1.0", optional = true }
|
||||
diesel = { version = "2.1", features = ["postgres_backend"], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = { version = "0.10", features = ["prost"], default-features = false }
|
||||
|
22
api/rust/build.rs
vendored
22
api/rust/build.rs
vendored
@ -73,13 +73,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
// internal
|
||||
tonic_build::configure()
|
||||
.out_dir(out_dir.join("internal"))
|
||||
.file_descriptor_set_path(out_dir.join("internal").join("proto_descriptor.bin"))
|
||||
.compile_well_known_types(true)
|
||||
.extern_path(".google.protobuf", well_known_types_path)
|
||||
.extern_path(".common", "crate::common")
|
||||
.compile(
|
||||
{
|
||||
let mut builder = tonic_build::configure()
|
||||
.out_dir(out_dir.join("internal"))
|
||||
.file_descriptor_set_path(out_dir.join("internal").join("proto_descriptor.bin"))
|
||||
.compile_well_known_types(true)
|
||||
.extern_path(".google.protobuf", well_known_types_path)
|
||||
.extern_path(".common", "crate::common");
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
{
|
||||
builder = builder.message_attribute("internal.DeviceSession", "#[derive(diesel::expression::AsExpression, diesel::deserialize::FromSqlRow)] #[diesel(sql_type = diesel::sql_types::Binary)]");
|
||||
}
|
||||
|
||||
builder.compile(
|
||||
&[cs_dir
|
||||
.join("internal")
|
||||
.join("internal.proto")
|
||||
@ -90,6 +97,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
proto_dir.join("google").to_str().unwrap(),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
{
|
||||
|
@ -7,15 +7,9 @@ import "gw/gw.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message DeviceSession {
|
||||
// Device EUI.
|
||||
bytes dev_eui = 1;
|
||||
|
||||
// Device address.
|
||||
bytes dev_addr = 2;
|
||||
|
||||
// Join EUI.
|
||||
bytes join_eui = 3;
|
||||
|
||||
// LoRaWAN mac-version.
|
||||
common.MacVersion mac_version = 4;
|
||||
|
||||
|
33
api/rust/src/internal.rs
vendored
33
api/rust/src/internal.rs
vendored
@ -2,6 +2,14 @@ include!(concat!(env!("OUT_DIR"), "/internal/internal.rs"));
|
||||
#[cfg(feature = "json")]
|
||||
include!(concat!(env!("OUT_DIR"), "/internal/internal.serde.rs"));
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
use std::io::Cursor;
|
||||
#[cfg(feature = "diesel")]
|
||||
use diesel::{backend::Backend, deserialize, serialize, sql_types::Binary};
|
||||
#[cfg(feature = "diesel")]
|
||||
use prost::Message;
|
||||
|
||||
|
||||
impl DeviceSession {
|
||||
pub fn get_a_f_cnt_down(&self) -> u32 {
|
||||
if self.mac_version().to_string().starts_with("1.0") {
|
||||
@ -23,3 +31,28 @@ impl DeviceSession {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
impl<ST, DB> deserialize::FromSql<ST, DB> for DeviceSession
|
||||
where
|
||||
DB: Backend,
|
||||
*const [u8]: deserialize::FromSql<ST, DB>,
|
||||
{
|
||||
fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result<Self> {
|
||||
let bytes = <Vec<u8> as deserialize::FromSql<ST, DB>>::from_sql(value)?;
|
||||
Ok(DeviceSession::decode(&mut Cursor::new(bytes))?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
impl serialize::ToSql<Binary, diesel::pg::Pg> for DeviceSession
|
||||
where
|
||||
[u8]: serialize::ToSql<Binary, diesel::pg::Pg>,
|
||||
{
|
||||
fn to_sql<'b>(&self, out: &mut serialize::Output<'b, '_, diesel::pg::Pg>) -> serialize::Result {
|
||||
<[u8] as serialize::ToSql<Binary, diesel::pg::Pg>>::to_sql(
|
||||
&self.encode_to_vec(),
|
||||
&mut out.reborrow(),
|
||||
)
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ tracing-subscriber = { version = "0.3", features = [
|
||||
], default-features = true }
|
||||
|
||||
# ChirpStack API definitions
|
||||
chirpstack_api = { path = "../api/rust", features = ["default", "internal"] }
|
||||
chirpstack_api = { path = "../api/rust", features = ["default", "internal", "diesel"] }
|
||||
lrwn = { path = "../lrwn", features = ["serde", "diesel", "regions", "crypto"] }
|
||||
backend = { path = "../backend" }
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
drop index idx_device_dev_addr;
|
||||
drop index idx_device_secondary_dev_addr;
|
||||
|
||||
alter table device
|
||||
drop column secondary_dev_addr,
|
||||
drop column device_session;
|
@ -0,0 +1,6 @@
|
||||
alter table device
|
||||
add column secondary_dev_addr bytea,
|
||||
add column device_session bytea;
|
||||
|
||||
create index idx_device_dev_addr on device (dev_addr);
|
||||
create index idx_device_secondary_dev_addr on device (secondary_dev_addr);
|
@ -18,7 +18,7 @@ use crate::backend::{joinserver, keywrap, roaming};
|
||||
use crate::downlink::data_fns;
|
||||
use crate::helpers::errors::PrintFullError;
|
||||
use crate::storage::{
|
||||
device_session, error::Error as StorageError, get_async_redis_conn, passive_roaming, redis_key,
|
||||
device, error::Error as StorageError, get_async_redis_conn, passive_roaming, redis_key,
|
||||
};
|
||||
use crate::uplink::{
|
||||
data_sns, error::Error as UplinkError, helpers, join_sns, RoamingMetaData, UplinkFrameSet,
|
||||
@ -312,9 +312,10 @@ async fn _handle_pr_start_req_data(
|
||||
};
|
||||
|
||||
// get device-session
|
||||
let ds = device_session::get_for_phypayload(&mut ufs.phy_payload, ufs.dr, ufs.ch as u8).await?;
|
||||
let d = device::get_for_phypayload(&mut ufs.phy_payload, ufs.dr, ufs.ch as u8).await?;
|
||||
let pr_lifetime = roaming::get_passive_roaming_lifetime(sender_id)?;
|
||||
let kek_label = roaming::get_passive_roaming_kek_label(sender_id)?;
|
||||
let ds = d.get_device_session()?;
|
||||
|
||||
let nwk_s_key = if ds.mac_version().to_string().starts_with("1.0") {
|
||||
Some(keywrap::wrap(
|
||||
@ -343,7 +344,7 @@ async fn _handle_pr_start_req_data(
|
||||
base: pl
|
||||
.base
|
||||
.to_base_payload_result(backend::ResultCode::Success, ""),
|
||||
dev_eui: ds.dev_eui.clone(),
|
||||
dev_eui: d.dev_eui.to_vec(),
|
||||
lifetime: if pr_lifetime.is_zero() {
|
||||
None
|
||||
} else {
|
||||
|
@ -15,10 +15,11 @@ use lrwn::{AES128Key, DevAddr, EUI64};
|
||||
use super::auth::validator;
|
||||
use super::error::ToStatus;
|
||||
use super::helpers::{self, FromProto, ToProto};
|
||||
use crate::storage::error::Error;
|
||||
use crate::storage::{
|
||||
device::{self, DeviceClass},
|
||||
device_keys, device_profile, device_queue, device_session, fields, metrics,
|
||||
device_keys, device_profile, device_queue,
|
||||
error::Error as StorageError,
|
||||
fields, metrics,
|
||||
};
|
||||
use crate::{codec, devaddr::get_random_dev_addr};
|
||||
|
||||
@ -514,7 +515,6 @@ impl DeviceService for Device {
|
||||
|
||||
let mut ds = internal::DeviceSession {
|
||||
region_config_id: "".to_string(),
|
||||
dev_eui: dev_eui.to_vec(),
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
mac_version: dp.mac_version.to_proto().into(),
|
||||
s_nwk_s_int_key: s_nwk_s_int_key.to_vec(),
|
||||
@ -532,12 +532,12 @@ impl DeviceService for Device {
|
||||
};
|
||||
dp.reset_session_to_boot_params(&mut ds);
|
||||
|
||||
device_session::save(&ds).await.map_err(|e| e.status())?;
|
||||
|
||||
// Set device DevAddr.
|
||||
device::set_dev_addr(dev_eui, dev_addr)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
let mut device_changeset = device::DeviceChangeset {
|
||||
device_session: Some(Some(ds)),
|
||||
dev_addr: Some(Some(dev_addr)),
|
||||
secondary_dev_addr: Some(None),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Flush queue (if configured).
|
||||
if dp.flush_queue_on_activate {
|
||||
@ -548,15 +548,15 @@ impl DeviceService for Device {
|
||||
|
||||
// LoRaWAN 1.1 devices send a mac-command when changing to Class-C. Change the class here for LoRaWAN 1.0 devices.
|
||||
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
|
||||
let _ = device::set_enabled_class(&dev_eui, DeviceClass::C)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
device_changeset.enabled_class = Some(DeviceClass::C);
|
||||
} else {
|
||||
let _ = device::set_enabled_class(&dev_eui, DeviceClass::A)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
device_changeset.enabled_class = Some(DeviceClass::A);
|
||||
}
|
||||
|
||||
device::partial_update(dev_eui, &device_changeset)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
|
||||
let mut resp = Response::new(());
|
||||
resp.metadata_mut()
|
||||
.insert("x-log-dev_eui", req_da.dev_eui.parse().unwrap());
|
||||
@ -581,9 +581,18 @@ impl DeviceService for Device {
|
||||
device_queue::flush_for_dev_eui(&dev_eui)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
device_session::delete(&dev_eui)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
|
||||
device::partial_update(
|
||||
dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
dev_addr: Some(None),
|
||||
secondary_dev_addr: Some(None),
|
||||
device_session: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.status())?;
|
||||
|
||||
let mut resp = Response::new(());
|
||||
resp.metadata_mut()
|
||||
@ -606,25 +615,24 @@ impl DeviceService for Device {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ds = match device_session::get(&dev_eui).await {
|
||||
let d = device::get(&dev_eui).await.map_err(|e| e.status())?;
|
||||
let ds = match d.get_device_session() {
|
||||
Ok(v) => v,
|
||||
Err(e) => match e {
|
||||
Error::NotFound(_) => {
|
||||
return Ok(Response::new(api::GetDeviceActivationResponse {
|
||||
device_activation: None,
|
||||
join_server_context: None,
|
||||
}));
|
||||
}
|
||||
_ => {
|
||||
return Err(e.status());
|
||||
}
|
||||
},
|
||||
Err(StorageError::NotFound(_)) => {
|
||||
return Ok(Response::new(api::GetDeviceActivationResponse {
|
||||
device_activation: None,
|
||||
join_server_context: None,
|
||||
}));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e.status());
|
||||
}
|
||||
};
|
||||
|
||||
let mut resp = Response::new(api::GetDeviceActivationResponse {
|
||||
device_activation: Some(api::DeviceActivation {
|
||||
dev_eui: hex::encode(&ds.dev_eui),
|
||||
dev_addr: hex::encode(&ds.dev_addr),
|
||||
dev_eui: d.dev_eui.to_string(),
|
||||
dev_addr: d.get_dev_addr().map_err(|e| e.status())?.to_string(),
|
||||
app_s_key: match &ds.app_s_key {
|
||||
Some(v) => hex::encode(&v.aes_key),
|
||||
None => "".to_string(),
|
||||
@ -1188,7 +1196,14 @@ impl DeviceService for Device {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ds = device_session::get(&dev_eui).await.unwrap_or_default();
|
||||
let d = device::get(&dev_eui).await.map_err(|e| e.status())?;
|
||||
let ds = match d.get_device_session() {
|
||||
Ok(v) => v.clone(),
|
||||
Err(StorageError::NotFound(_)) => Default::default(),
|
||||
Err(e) => {
|
||||
return Err(e.status());
|
||||
}
|
||||
};
|
||||
|
||||
let max_f_cnt_down_queue = device_queue::get_max_f_cnt_down(dev_eui)
|
||||
.await
|
||||
@ -1210,7 +1225,7 @@ pub mod test {
|
||||
use super::*;
|
||||
use crate::api::auth::validator::RequestValidator;
|
||||
use crate::api::auth::AuthID;
|
||||
use crate::storage::{application, tenant, user};
|
||||
use crate::storage::{application, device, tenant, user};
|
||||
use crate::test;
|
||||
use lrwn::NetID;
|
||||
|
||||
@ -1526,12 +1541,18 @@ pub mod test {
|
||||
assert!(get_activation_resp.get_ref().device_activation.is_none());
|
||||
|
||||
// test get activation with JS session-key ID.
|
||||
device_session::save(&internal::DeviceSession {
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
js_session_key_id: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
..Default::default()
|
||||
})
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
dev_addr: Some(Some(DevAddr::from_be_bytes([1, 2, 3, 4]))),
|
||||
device_session: Some(Some(internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
js_session_key_id: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let get_activation_req = get_request(
|
||||
@ -1550,17 +1571,23 @@ pub mod test {
|
||||
);
|
||||
|
||||
// test activation with AppSKey key-envelope.
|
||||
device_session::save(&internal::DeviceSession {
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
app_s_key: Some(common::KeyEnvelope {
|
||||
kek_label: "test-key".into(),
|
||||
aes_key: vec![8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(Some(internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
app_s_key: Some(common::KeyEnvelope {
|
||||
kek_label: "test-key".into(),
|
||||
aes_key: vec![8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let get_activation_req = get_request(
|
||||
&u.id,
|
||||
api::GetDeviceActivationRequest {
|
||||
|
56
chirpstack/src/cmd/migrate_ds_to_pg.rs
Normal file
56
chirpstack/src/cmd/migrate_ds_to_pg.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use anyhow::Result;
|
||||
use diesel::prelude::*;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::storage::{self, device_session, error::Error, get_async_db_conn, schema::device};
|
||||
use lrwn::{DevAddr, EUI64};
|
||||
|
||||
pub async fn run() -> Result<()> {
|
||||
storage::setup().await?;
|
||||
|
||||
info!("Migrating device-sessions from Redis to PostgreSQL");
|
||||
info!("Getting DevEUIs from PostgreSQL without device-session");
|
||||
|
||||
let dev_euis: Vec<EUI64> = device::dsl::device
|
||||
.select(device::dsl::dev_eui)
|
||||
.filter(device::dsl::device_session.is_null())
|
||||
.load(&mut get_async_db_conn().await?)
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
"There are {} devices in PostgreSQL without device-session set",
|
||||
dev_euis.len()
|
||||
);
|
||||
|
||||
for dev_eui in &dev_euis {
|
||||
debug!(dev_eui = %dev_eui, "Migrating device-session");
|
||||
|
||||
let ds = match device_session::get(dev_eui).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => match e {
|
||||
Error::NotFound(_) => {
|
||||
debug!(dev_eui = %dev_eui, "Device does not have a device-session");
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow::Error::new(e));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
storage::device::partial_update(
|
||||
*dev_eui,
|
||||
&storage::device::DeviceChangeset {
|
||||
dev_addr: Some(Some(DevAddr::from_slice(&ds.dev_addr)?)),
|
||||
device_session: Some(Some(ds)),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
debug!(dev_eui = %dev_eui, "Device-session migrated");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod configfile;
|
||||
pub mod create_api_key;
|
||||
pub mod import_legacy_lorawan_devices_repository;
|
||||
pub mod migrate_ds_to_pg;
|
||||
pub mod print_ds;
|
||||
pub mod root;
|
||||
|
@ -1,14 +1,14 @@
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::storage;
|
||||
use crate::storage::device_session;
|
||||
use crate::storage::device;
|
||||
use lrwn::EUI64;
|
||||
|
||||
pub async fn run(dev_eui: &EUI64) -> Result<()> {
|
||||
storage::setup().await.context("Setup storage")?;
|
||||
let ds = device_session::get(dev_eui)
|
||||
.await
|
||||
.context("Get device-session")?;
|
||||
|
||||
let d = device::get(dev_eui).await.context("Get device")?;
|
||||
let ds = d.get_device_session()?;
|
||||
let json = serde_json::to_string_pretty(&ds)?;
|
||||
println!("{}", json);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@ pub struct JoinAccept<'a> {
|
||||
relay_context: Option<&'a RelayContext>,
|
||||
tenant: &'a tenant::Tenant,
|
||||
device: &'a device::Device,
|
||||
device_session: &'a internal::DeviceSession,
|
||||
join_accept: &'a PhyPayload,
|
||||
network_conf: config::RegionNetwork,
|
||||
region_conf: Arc<Box<dyn lrwn::region::Region + Sync + Send>>,
|
||||
@ -36,20 +35,12 @@ impl JoinAccept<'_> {
|
||||
ufs: &UplinkFrameSet,
|
||||
tenant: &tenant::Tenant,
|
||||
device: &device::Device,
|
||||
device_session: &internal::DeviceSession,
|
||||
join_accept: &PhyPayload,
|
||||
) -> Result<()> {
|
||||
let downlink_id: u32 = rand::thread_rng().gen();
|
||||
let span = span!(Level::INFO, "join_accept", downlink_id = downlink_id);
|
||||
|
||||
let fut = JoinAccept::_handle(
|
||||
downlink_id,
|
||||
ufs,
|
||||
tenant,
|
||||
device,
|
||||
device_session,
|
||||
join_accept,
|
||||
);
|
||||
let fut = JoinAccept::_handle(downlink_id, ufs, tenant, device, join_accept);
|
||||
fut.instrument(span).await
|
||||
}
|
||||
|
||||
@ -58,7 +49,6 @@ impl JoinAccept<'_> {
|
||||
ufs: &UplinkFrameSet,
|
||||
tenant: &tenant::Tenant,
|
||||
device: &device::Device,
|
||||
device_session: &internal::DeviceSession,
|
||||
join_accept: &PhyPayload,
|
||||
) -> Result<()> {
|
||||
let downlink_id: u32 = rand::thread_rng().gen();
|
||||
@ -68,15 +58,8 @@ impl JoinAccept<'_> {
|
||||
downlink_id = downlink_id
|
||||
);
|
||||
|
||||
let fut = JoinAccept::_handle_relayed(
|
||||
downlink_id,
|
||||
relay_ctx,
|
||||
ufs,
|
||||
tenant,
|
||||
device,
|
||||
device_session,
|
||||
join_accept,
|
||||
);
|
||||
let fut =
|
||||
JoinAccept::_handle_relayed(downlink_id, relay_ctx, ufs, tenant, device, join_accept);
|
||||
fut.instrument(span).await
|
||||
}
|
||||
|
||||
@ -85,7 +68,6 @@ impl JoinAccept<'_> {
|
||||
ufs: &UplinkFrameSet,
|
||||
tenant: &tenant::Tenant,
|
||||
device: &device::Device,
|
||||
device_session: &internal::DeviceSession,
|
||||
join_accept: &PhyPayload,
|
||||
) -> Result<()> {
|
||||
let mut ctx = JoinAccept {
|
||||
@ -93,7 +75,6 @@ impl JoinAccept<'_> {
|
||||
relay_context: None,
|
||||
tenant,
|
||||
device,
|
||||
device_session,
|
||||
join_accept,
|
||||
network_conf: config::get_region_network(&ufs.region_config_id)?,
|
||||
region_conf: region::get(&ufs.region_config_id)?,
|
||||
@ -122,7 +103,6 @@ impl JoinAccept<'_> {
|
||||
ufs: &UplinkFrameSet,
|
||||
tenant: &tenant::Tenant,
|
||||
device: &device::Device,
|
||||
device_session: &internal::DeviceSession,
|
||||
join_accept: &PhyPayload,
|
||||
) -> Result<()> {
|
||||
let mut ctx = JoinAccept {
|
||||
@ -130,7 +110,6 @@ impl JoinAccept<'_> {
|
||||
relay_context: Some(relay_ctx),
|
||||
tenant,
|
||||
device,
|
||||
device_session,
|
||||
join_accept,
|
||||
network_conf: config::get_region_network(&ufs.region_config_id)?,
|
||||
region_conf: region::get(&ufs.region_config_id)?,
|
||||
@ -302,6 +281,7 @@ impl JoinAccept<'_> {
|
||||
|
||||
let gw_down = self.downlink_gateway.as_ref().unwrap();
|
||||
let relay_ctx = self.relay_context.unwrap();
|
||||
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||
|
||||
let mut tx_info = chirpstack_api::gw::DownlinkTxInfo {
|
||||
board: gw_down.board,
|
||||
@ -311,7 +291,7 @@ impl JoinAccept<'_> {
|
||||
};
|
||||
|
||||
// Get RX1 DR offset.
|
||||
let rx1_dr_offset = relay_ctx.device_session.rx1_dr_offset as usize;
|
||||
let rx1_dr_offset = relay_ds.rx1_dr_offset as usize;
|
||||
|
||||
// get RX1 DR.
|
||||
let rx1_dr_index = self
|
||||
@ -337,8 +317,8 @@ impl JoinAccept<'_> {
|
||||
}
|
||||
|
||||
// Set timestamp.
|
||||
let delay = if relay_ctx.device_session.rx1_delay > 0 {
|
||||
Duration::from_secs(relay_ctx.device_session.rx1_delay as u64)
|
||||
let delay = if relay_ds.rx1_delay > 0 {
|
||||
Duration::from_secs(relay_ds.rx1_delay as u64)
|
||||
} else {
|
||||
self.region_conf.get_defaults().rx1_delay
|
||||
};
|
||||
@ -415,9 +395,10 @@ impl JoinAccept<'_> {
|
||||
|
||||
let gw_down = self.downlink_gateway.as_ref().unwrap();
|
||||
let relay_ctx = self.relay_context.unwrap();
|
||||
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||
|
||||
// Get frequency.
|
||||
let frequency = relay_ctx.device_session.rx2_frequency;
|
||||
let frequency = relay_ds.rx2_frequency;
|
||||
|
||||
let mut tx_info = chirpstack_api::gw::DownlinkTxInfo {
|
||||
board: gw_down.board,
|
||||
@ -428,7 +409,7 @@ impl JoinAccept<'_> {
|
||||
};
|
||||
|
||||
// get RX2 DR
|
||||
let rx2_dr_index = relay_ctx.device_session.rx2_dr as u8;
|
||||
let rx2_dr_index = relay_ds.rx2_dr as u8;
|
||||
let rx2_dr = self.region_conf.get_data_rate(rx2_dr_index)?;
|
||||
|
||||
// set DR to tx_info
|
||||
@ -444,8 +425,8 @@ impl JoinAccept<'_> {
|
||||
}
|
||||
|
||||
// Set timestamp.
|
||||
let delay = if relay_ctx.device_session.rx1_delay > 0 {
|
||||
Duration::from_secs(relay_ctx.device_session.rx1_delay as u64 + 1)
|
||||
let delay = if relay_ds.rx1_delay > 0 {
|
||||
Duration::from_secs(relay_ds.rx1_delay as u64 + 1)
|
||||
} else {
|
||||
self.region_conf.get_defaults().rx2_delay
|
||||
};
|
||||
@ -481,6 +462,7 @@ impl JoinAccept<'_> {
|
||||
trace!("Setting ForwardDownlinkReq frame");
|
||||
|
||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||
|
||||
let mut relay_phy = lrwn::PhyPayload {
|
||||
mhdr: lrwn::MHDR {
|
||||
@ -489,16 +471,11 @@ impl JoinAccept<'_> {
|
||||
},
|
||||
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
|
||||
fhdr: lrwn::FHDR {
|
||||
devaddr: lrwn::DevAddr::from_slice(&relay_ctx.device_session.dev_addr)?,
|
||||
f_cnt: if relay_ctx
|
||||
.device_session
|
||||
.mac_version()
|
||||
.to_string()
|
||||
.starts_with("1.0")
|
||||
{
|
||||
relay_ctx.device_session.n_f_cnt_down
|
||||
devaddr: relay_ctx.device.get_dev_addr()?,
|
||||
f_cnt: if relay_ds.mac_version().to_string().starts_with("1.0") {
|
||||
relay_ds.n_f_cnt_down
|
||||
} else {
|
||||
relay_ctx.device_session.a_f_cnt_down
|
||||
relay_ds.a_f_cnt_down
|
||||
},
|
||||
f_ctrl: lrwn::FCtrl {
|
||||
adr: !self.network_conf.adr_disabled,
|
||||
@ -517,17 +494,15 @@ impl JoinAccept<'_> {
|
||||
mic: None,
|
||||
};
|
||||
|
||||
relay_phy.encrypt_frm_payload(&lrwn::AES128Key::from_slice(
|
||||
&relay_ctx.device_session.nwk_s_enc_key,
|
||||
)?)?;
|
||||
relay_phy.encrypt_frm_payload(&lrwn::AES128Key::from_slice(&relay_ds.nwk_s_enc_key)?)?;
|
||||
|
||||
// Set MIC.
|
||||
// If this is an ACK, then FCntUp has already been incremented by one. If
|
||||
// this is not an ACK, then DownlinkDataMIC will zero out ConfFCnt.
|
||||
relay_phy.set_downlink_data_mic(
|
||||
relay_ctx.device_session.mac_version().from_proto(),
|
||||
relay_ctx.device_session.f_cnt_up - 1,
|
||||
&lrwn::AES128Key::from_slice(&relay_ctx.device_session.s_nwk_s_int_key)?,
|
||||
relay_ds.mac_version().from_proto(),
|
||||
relay_ds.f_cnt_up - 1,
|
||||
&lrwn::AES128Key::from_slice(&relay_ds.s_nwk_s_int_key)?,
|
||||
)?;
|
||||
|
||||
let relay_phy_b = relay_phy.to_vec()?;
|
||||
@ -551,12 +526,13 @@ impl JoinAccept<'_> {
|
||||
|
||||
async fn save_downlink_frame(&self) -> Result<()> {
|
||||
trace!("Saving downlink frame");
|
||||
let ds = self.device.get_device_session()?;
|
||||
|
||||
let df = chirpstack_api::internal::DownlinkFrame {
|
||||
dev_eui: self.device.dev_eui.to_be_bytes().to_vec(),
|
||||
downlink_id: self.downlink_frame.downlink_id,
|
||||
downlink_frame: Some(self.downlink_frame.clone()),
|
||||
nwk_s_enc_key: self.device_session.nwk_s_enc_key.clone(),
|
||||
nwk_s_enc_key: ds.nwk_s_enc_key.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -571,14 +547,15 @@ impl JoinAccept<'_> {
|
||||
trace!("Saving ForwardDownlinkReq frame");
|
||||
|
||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||
|
||||
let df = chirpstack_api::internal::DownlinkFrame {
|
||||
dev_eui: relay_ctx.device.dev_eui.to_be_bytes().to_vec(),
|
||||
downlink_id: self.downlink_frame.downlink_id,
|
||||
downlink_frame: Some(self.downlink_frame.clone()),
|
||||
nwk_s_enc_key: relay_ctx.device_session.nwk_s_enc_key.clone(),
|
||||
a_f_cnt_down: relay_ctx.device_session.get_a_f_cnt_down(),
|
||||
n_f_cnt_down: relay_ctx.device_session.n_f_cnt_down,
|
||||
nwk_s_enc_key: relay_ds.nwk_s_enc_key.clone(),
|
||||
a_f_cnt_down: relay_ds.get_a_f_cnt_down(),
|
||||
n_f_cnt_down: relay_ds.n_f_cnt_down,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,9 @@ use crate::api::helpers::ToProto;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_profile, device_queue, device_session, downlink_frame, multicast, tenant,
|
||||
device_profile, device_queue, downlink_frame,
|
||||
helpers::get_all_device_data,
|
||||
multicast, tenant,
|
||||
};
|
||||
use crate::{integration, stream};
|
||||
use chirpstack_api::{common, gw, integration as integration_pb, internal, stream as stream_pb};
|
||||
@ -23,8 +25,6 @@ pub struct TxAck {
|
||||
downlink_frame_item: Option<gw::DownlinkFrameItem>,
|
||||
phy_payload: Option<PhyPayload>,
|
||||
phy_payload_relayed: Option<PhyPayload>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
device_session_relayed: Option<internal::DeviceSession>,
|
||||
tenant: Option<tenant::Tenant>,
|
||||
tenant_relayed: Option<tenant::Tenant>,
|
||||
application: Option<application::Application>,
|
||||
@ -66,8 +66,6 @@ impl TxAck {
|
||||
downlink_frame_item: None,
|
||||
phy_payload: None,
|
||||
phy_payload_relayed: None,
|
||||
device_session: None,
|
||||
device_session_relayed: None,
|
||||
tenant: None,
|
||||
tenant_relayed: None,
|
||||
application: None,
|
||||
@ -88,10 +86,7 @@ impl TxAck {
|
||||
|
||||
if ctx.is_error() {
|
||||
if ctx.is_application_payload() || ctx.is_mac_only_downlink() {
|
||||
ctx.get_device().await?;
|
||||
ctx.get_device_profile().await?;
|
||||
ctx.get_application().await?;
|
||||
ctx.get_tenant().await?;
|
||||
ctx.get_device_data().await?;
|
||||
ctx.log_tx_ack_error().await?;
|
||||
}
|
||||
|
||||
@ -99,30 +94,28 @@ impl TxAck {
|
||||
ctx.delete_multicast_group_queue_item().await?;
|
||||
}
|
||||
} else {
|
||||
if ctx.is_application_payload() {
|
||||
ctx.get_device().await?;
|
||||
ctx.get_device_profile().await?;
|
||||
ctx.get_application().await?;
|
||||
ctx.get_tenant().await?;
|
||||
ctx.get_device_session().await?;
|
||||
ctx.get_device_queue_item().await?;
|
||||
if ctx.is_unconfirmed_downlink() {
|
||||
ctx.delete_device_queue_item().await?;
|
||||
if ctx.is_application_payload() || ctx.is_mac_only_downlink() {
|
||||
ctx.get_device_data().await?;
|
||||
|
||||
if ctx.is_application_payload() {
|
||||
ctx.get_device_queue_item().await?;
|
||||
if ctx.is_unconfirmed_downlink() {
|
||||
ctx.delete_device_queue_item().await?;
|
||||
}
|
||||
|
||||
if ctx.is_confirmed_downlink() {
|
||||
ctx.set_device_queue_item_pending().await?;
|
||||
ctx.set_device_session_conf_f_cnt()?;
|
||||
}
|
||||
|
||||
ctx.increment_a_f_cnt_down()?;
|
||||
ctx.send_tx_ack_event().await?;
|
||||
}
|
||||
|
||||
if ctx.is_confirmed_downlink() {
|
||||
ctx.set_device_queue_item_pending().await?;
|
||||
ctx.set_device_session_conf_f_cnt()?;
|
||||
if ctx.is_mac_only_downlink() {
|
||||
ctx.increment_n_f_cnt_down()?;
|
||||
}
|
||||
|
||||
ctx.increment_a_f_cnt_down()?;
|
||||
ctx.save_device_session().await?;
|
||||
ctx.send_tx_ack_event().await?;
|
||||
}
|
||||
|
||||
if ctx.is_mac_only_downlink() {
|
||||
ctx.get_device_session().await?;
|
||||
ctx.increment_n_f_cnt_down()?;
|
||||
ctx.save_device_session().await?;
|
||||
}
|
||||
|
||||
@ -140,24 +133,22 @@ impl TxAck {
|
||||
|
||||
async fn _handle_relayed(&mut self) -> Result<()> {
|
||||
self.get_phy_payload_relayed()?;
|
||||
self.get_device_data().await?; // the device-data of the relay
|
||||
|
||||
if self.is_error() {
|
||||
// We log the tx ack error under the relay as this is the device to which the downlink
|
||||
// is sent.
|
||||
self.get_device().await?;
|
||||
self.get_device_profile().await?;
|
||||
self.get_application().await?;
|
||||
self.get_tenant().await?;
|
||||
self.log_tx_ack_error().await?;
|
||||
} else {
|
||||
// First handle the relay frame-counter increment.
|
||||
self.get_device_session().await?;
|
||||
self.increment_a_f_cnt_down()?;
|
||||
self.save_device_session().await?;
|
||||
|
||||
// Get data of relayed device.
|
||||
self.get_device_data_relayed().await?;
|
||||
|
||||
// Handle end-device frame-counter increment + queue item.
|
||||
if self.is_application_payload_relayed() {
|
||||
self.get_device_session_relayed().await?;
|
||||
self.get_device_queue_item().await?;
|
||||
if self.is_unconfirmed_downlink_relayed() {
|
||||
self.delete_device_queue_item().await?;
|
||||
@ -172,13 +163,9 @@ impl TxAck {
|
||||
self.save_device_session_relayed().await?;
|
||||
|
||||
// Log tx ack event.
|
||||
self.get_device_relayed().await?;
|
||||
self.get_device_profile_relayed().await?;
|
||||
self.get_application_relayed().await?;
|
||||
self.get_tenant_relayed().await?;
|
||||
self.get_device_data_relayed().await?;
|
||||
self.send_tx_ack_event_relayed().await?;
|
||||
} else if self.is_mac_only_downlink_relayed() {
|
||||
self.get_device_session_relayed().await?;
|
||||
self.increment_n_f_cnt_down_relayed()?;
|
||||
self.save_device_session_relayed().await?;
|
||||
}
|
||||
@ -225,75 +212,29 @@ impl TxAck {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_session(&mut self) -> Result<()> {
|
||||
trace!("Getting device-session");
|
||||
async fn get_device_data(&mut self) -> Result<()> {
|
||||
trace!("Getting device data");
|
||||
let dev_eui = EUI64::from_slice(&self.downlink_frame.as_ref().unwrap().dev_eui)?;
|
||||
self.device_session = Some(device_session::get(&dev_eui).await?);
|
||||
let (dev, app, t, dp) = get_all_device_data(dev_eui).await?;
|
||||
|
||||
self.tenant = Some(t);
|
||||
self.application = Some(app);
|
||||
self.device_profile = Some(dp);
|
||||
self.device = Some(dev);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_session_relayed(&mut self) -> Result<()> {
|
||||
trace!("Getting relayed device-session");
|
||||
async fn get_device_data_relayed(&mut self) -> Result<()> {
|
||||
trace!("Getting relayed device data");
|
||||
let dev_eui = EUI64::from_slice(&self.downlink_frame.as_ref().unwrap().dev_eui_relayed)?;
|
||||
self.device_session_relayed = Some(device_session::get(&dev_eui).await?);
|
||||
let (dev, app, t, dp) = get_all_device_data(dev_eui).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
self.tenant_relayed = Some(t);
|
||||
self.application_relayed = Some(app);
|
||||
self.device_profile_relayed = Some(dp);
|
||||
self.device_relayed = Some(dev);
|
||||
|
||||
async fn get_device(&mut self) -> Result<()> {
|
||||
trace!("Getting device");
|
||||
let dev_eui = EUI64::from_slice(&self.downlink_frame.as_ref().unwrap().dev_eui)?;
|
||||
self.device = Some(device::get(&dev_eui).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_relayed(&mut self) -> Result<()> {
|
||||
trace!("Getting relayed device");
|
||||
let dev_eui = EUI64::from_slice(&self.downlink_frame.as_ref().unwrap().dev_eui_relayed)?;
|
||||
self.device_relayed = Some(device::get(&dev_eui).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_profile(&mut self) -> Result<()> {
|
||||
trace!("Getting device-profile");
|
||||
self.device_profile =
|
||||
Some(device_profile::get(&self.device.as_ref().unwrap().device_profile_id).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_profile_relayed(&mut self) -> Result<()> {
|
||||
trace!("Getting relayed device-profile");
|
||||
self.device_profile_relayed = Some(
|
||||
device_profile::get(&self.device_relayed.as_ref().unwrap().device_profile_id).await?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_application(&mut self) -> Result<()> {
|
||||
trace!("Getting application");
|
||||
self.application =
|
||||
Some(application::get(&self.device.as_ref().unwrap().application_id).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_application_relayed(&mut self) -> Result<()> {
|
||||
trace!("Getting relayed application");
|
||||
self.application_relayed =
|
||||
Some(application::get(&self.device_relayed.as_ref().unwrap().application_id).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_tenant(&mut self) -> Result<()> {
|
||||
trace!("Getting tenant");
|
||||
self.tenant = Some(tenant::get(&self.application.as_ref().unwrap().tenant_id).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_tenant_relayed(&mut self) -> Result<()> {
|
||||
trace!("Getting relayed tenant");
|
||||
self.tenant_relayed =
|
||||
Some(tenant::get(&self.application_relayed.as_ref().unwrap().tenant_id).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -353,7 +294,8 @@ impl TxAck {
|
||||
fn set_device_session_conf_f_cnt(&mut self) -> Result<()> {
|
||||
trace!("Setting device-session conf_f_cnt");
|
||||
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
let qi = self.device_queue_item.as_ref().unwrap();
|
||||
|
||||
ds.conf_f_cnt = match qi.f_cnt_down {
|
||||
@ -370,7 +312,8 @@ impl TxAck {
|
||||
fn set_device_session_conf_f_cnt_relayed(&mut self) -> Result<()> {
|
||||
trace!("Setting relayed device-session conf_f_cnt");
|
||||
|
||||
let ds = self.device_session_relayed.as_mut().unwrap();
|
||||
let d = self.device_relayed.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
let qi = self.device_queue_item.as_ref().unwrap();
|
||||
|
||||
ds.conf_f_cnt = match qi.f_cnt_down {
|
||||
@ -387,7 +330,8 @@ impl TxAck {
|
||||
fn increment_a_f_cnt_down(&mut self) -> Result<()> {
|
||||
trace!("Incrementing a_f_cnt_down");
|
||||
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
ds.set_a_f_cnt_down(self.downlink_frame.as_ref().unwrap().a_f_cnt_down + 1);
|
||||
|
||||
Ok(())
|
||||
@ -396,7 +340,8 @@ impl TxAck {
|
||||
fn increment_a_f_cnt_down_relayed(&mut self) -> Result<()> {
|
||||
trace!("Incrementing relayed a_f_cnt_down");
|
||||
|
||||
let ds = self.device_session_relayed.as_mut().unwrap();
|
||||
let d = self.device_relayed.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
ds.set_a_f_cnt_down(ds.get_a_f_cnt_down() + 1);
|
||||
|
||||
Ok(())
|
||||
@ -405,7 +350,8 @@ impl TxAck {
|
||||
fn increment_n_f_cnt_down(&mut self) -> Result<()> {
|
||||
trace!("Incrementing n_f_cnt_down");
|
||||
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
ds.n_f_cnt_down += 1;
|
||||
|
||||
Ok(())
|
||||
@ -414,7 +360,8 @@ impl TxAck {
|
||||
fn increment_n_f_cnt_down_relayed(&mut self) -> Result<()> {
|
||||
trace!("Incrementing relayed n_f_cnt_down");
|
||||
|
||||
let ds = self.device_session_relayed.as_mut().unwrap();
|
||||
let d = self.device_relayed.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
ds.n_f_cnt_down += 1;
|
||||
|
||||
Ok(())
|
||||
@ -422,13 +369,35 @@ impl TxAck {
|
||||
|
||||
async fn save_device_session(&self) -> Result<()> {
|
||||
trace!("Saving device-session");
|
||||
device_session::save(self.device_session.as_ref().unwrap()).await?;
|
||||
|
||||
let d = self.device.as_ref().unwrap();
|
||||
|
||||
device::partial_update(
|
||||
d.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(d.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_device_session_relayed(&self) -> Result<()> {
|
||||
trace!("Saving relayed device-session");
|
||||
device_session::save(self.device_session_relayed.as_ref().unwrap()).await?;
|
||||
|
||||
let d = self.device_relayed.as_ref().unwrap();
|
||||
|
||||
device::partial_update(
|
||||
d.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(d.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,13 @@ use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
_block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending ConfigureFwdLimitReq mac-command"));
|
||||
}
|
||||
@ -27,7 +29,7 @@ pub fn handle(
|
||||
ds.relay = Some(internal::Relay::default());
|
||||
}
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, "ConfigureFwdLimitReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, "ConfigureFwdLimitReq acknowledged");
|
||||
|
||||
if let Some(relay) = &mut ds.relay {
|
||||
relay.join_req_limit_reload_rate = req_pl.reload_rate.join_req_reload_rate as u32;
|
||||
@ -115,10 +117,12 @@ mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device::default(),
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.configure_fwd_limit_ans,
|
||||
tst.configure_fwd_limit_req.as_ref(),
|
||||
);
|
||||
@ -130,7 +134,12 @@ mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,17 @@ use std::iter::zip;
|
||||
use anyhow::Result;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::storage::{device, device_session};
|
||||
use chirpstack_api::internal;
|
||||
use crate::storage::device;
|
||||
use lrwn::EUI64;
|
||||
|
||||
pub async fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending CtrlUplinkListReq mac-command"));
|
||||
}
|
||||
@ -49,7 +50,7 @@ pub async fn handle(
|
||||
if ans_pl.uplink_list_idx_ack {
|
||||
if let Some(relay) = &mut ds.relay {
|
||||
info!(
|
||||
dev_eui = %dev.dev_eui,
|
||||
dev_eui = %dev_eui,
|
||||
uplink_list_idx = req_pl.ctrl_uplink_action.uplink_list_idx,
|
||||
ctrl_uplink_action = action,
|
||||
w_f_cnt = ans_pl.w_fcnt,
|
||||
@ -59,12 +60,20 @@ pub async fn handle(
|
||||
if action == 0 {
|
||||
for rd in &relay.devices {
|
||||
if req_pl.ctrl_uplink_action.uplink_list_idx as u32 == rd.index {
|
||||
let mut ds =
|
||||
device_session::get(&EUI64::from_slice(&rd.dev_eui)?).await?;
|
||||
let dev_eui = EUI64::from_slice(&rd.dev_eui)?;
|
||||
let mut d = device::get(&dev_eui).await?;
|
||||
let ds = d.get_device_session_mut()?;
|
||||
if let Some(relay) = &mut ds.relay {
|
||||
relay.w_f_cnt = ans_pl.w_fcnt;
|
||||
};
|
||||
device_session::save(&ds).await?;
|
||||
device::partial_update(
|
||||
dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(d.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
} else if action == 1 {
|
||||
@ -75,7 +84,7 @@ pub async fn handle(
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
dev_eui = %dev.dev_eui,
|
||||
dev_eui = %dev_eui,
|
||||
uplink_list_idx = req_pl.ctrl_uplink_action.uplink_list_idx,
|
||||
"CtrlUplinkListReq not acknowledged",
|
||||
);
|
||||
@ -88,7 +97,9 @@ pub async fn handle(
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::storage;
|
||||
use crate::test;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -104,6 +115,39 @@ mod test {
|
||||
async fn test_response() {
|
||||
let _handle = test::prepare().await;
|
||||
|
||||
let t = storage::tenant::create(storage::tenant::Tenant {
|
||||
name: "test-tenant".into(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let app = storage::application::create(storage::application::Application {
|
||||
name: "test-app".into(),
|
||||
tenant_id: t.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dp = storage::device_profile::create(storage::device_profile::DeviceProfile {
|
||||
name: "test-dp".into(),
|
||||
tenant_id: t.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dev = storage::device::create(storage::device::Device {
|
||||
name: "test-dev".into(),
|
||||
dev_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
application_id: app.id,
|
||||
device_profile_id: dp.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "acked, nothing pending".into(),
|
||||
@ -120,7 +164,6 @@ mod test {
|
||||
},
|
||||
device_session_ed: internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
relay: Some(internal::Relay {
|
||||
w_f_cnt: 1,
|
||||
..Default::default()
|
||||
@ -136,7 +179,6 @@ mod test {
|
||||
]),
|
||||
expected_device_session_ed: internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
relay: Some(internal::Relay {
|
||||
w_f_cnt: 1,
|
||||
..Default::default()
|
||||
@ -160,7 +202,6 @@ mod test {
|
||||
},
|
||||
device_session_ed: internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
relay: Some(internal::Relay {
|
||||
w_f_cnt: 1,
|
||||
..Default::default()
|
||||
@ -183,7 +224,6 @@ mod test {
|
||||
]),
|
||||
expected_device_session_ed: internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
relay: Some(internal::Relay {
|
||||
w_f_cnt: 10,
|
||||
..Default::default()
|
||||
@ -207,7 +247,6 @@ mod test {
|
||||
},
|
||||
device_session_ed: internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
..Default::default()
|
||||
},
|
||||
ctrl_uplink_list_req: Some(lrwn::MACCommandSet::new(vec![
|
||||
@ -226,7 +265,6 @@ mod test {
|
||||
]),
|
||||
expected_device_session_ed: internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
..Default::default()
|
||||
},
|
||||
expected_error: None,
|
||||
@ -236,12 +274,23 @@ mod test {
|
||||
for tst in &tests {
|
||||
println!("> {}", tst.name);
|
||||
|
||||
device_session::save(&tst.device_session_ed).await.unwrap();
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(Some(tst.device_session_ed.clone())),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut relay_dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut ds = tst.device_session.clone();
|
||||
let resp = handle(
|
||||
&device::Device::default(),
|
||||
&mut ds,
|
||||
&mut relay_dev,
|
||||
&tst.ctrl_uplink_list_ans,
|
||||
tst.ctrl_uplink_list_req.as_ref(),
|
||||
)
|
||||
@ -254,11 +303,9 @@ mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
let ds =
|
||||
device_session::get(&EUI64::from_slice(&tst.device_session_ed.dev_eui).unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(tst.expected_device_session_ed, ds);
|
||||
let d = device::get(&dev.dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(&tst.expected_device_session_ed, ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,15 +23,18 @@ pub async fn handle(
|
||||
if let lrwn::MACCommand::DevStatusAns(pl) = mac {
|
||||
info!(dev_eui = %dev.dev_eui, battery = pl.battery, margin = pl.margin, "DevStatusAns received");
|
||||
|
||||
device::set_status(
|
||||
&dev.dev_eui,
|
||||
pl.margin as i32,
|
||||
pl.battery == 0,
|
||||
if pl.battery > 0 && pl.battery < 255 {
|
||||
let v: BigDecimal = ((pl.battery as f32) / 254.0 * 100.0).try_into()?;
|
||||
Some(v.with_scale(2))
|
||||
} else {
|
||||
None
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
margin: Some(pl.margin as i32),
|
||||
external_power_source: Some(pl.battery == 0),
|
||||
battery_level: Some(if pl.battery > 0 && pl.battery < 255 {
|
||||
let v: BigDecimal = ((pl.battery as f32) / 254.0 * 100.0).try_into()?;
|
||||
Some(v.with_scale(2))
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
@ -10,11 +10,14 @@ pub async fn handle(
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("Expected DeviceModeInd"))?;
|
||||
if let lrwn::MACCommand::DeviceModeInd(pl) = mac {
|
||||
device::set_enabled_class(
|
||||
&dev.dev_eui,
|
||||
match pl.class {
|
||||
lrwn::DeviceModeClass::ClassA => DeviceClass::A,
|
||||
lrwn::DeviceModeClass::ClassC => DeviceClass::C,
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
enabled_class: Some(match pl.class {
|
||||
lrwn::DeviceModeClass::ClassA => DeviceClass::A,
|
||||
lrwn::DeviceModeClass::ClassC => DeviceClass::C,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
@ -5,11 +5,13 @@ use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending EndDeviceConfReq mac-command"));
|
||||
}
|
||||
@ -41,7 +43,7 @@ pub fn handle(
|
||||
&& ans_pl.second_ch_idx_ack
|
||||
&& ans_pl.backoff_ack
|
||||
{
|
||||
info!(dev_eui = %dev.dev_eui, "EndDeviceConfReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, "EndDeviceConfReq acknowledged");
|
||||
|
||||
if let Some(relay) = &mut ds.relay {
|
||||
relay.ed_activation_mode =
|
||||
@ -57,7 +59,7 @@ pub fn handle(
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
dev_eui = %dev.dev_eui,
|
||||
dev_eui = %dev_eui,
|
||||
second_ch_freq_ack = ans_pl.second_ch_freq_ack,
|
||||
second_ch_dr_ack = ans_pl.second_ch_dr_ack,
|
||||
second_ch_idx_ack = ans_pl.second_ch_idx_ack,
|
||||
@ -175,10 +177,12 @@ mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device::default(),
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.end_device_conf_ans,
|
||||
tst.end_device_conf_req.as_ref(),
|
||||
);
|
||||
@ -190,7 +194,12 @@ mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ use anyhow::Result;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending FilterListReq mac-command"));
|
||||
}
|
||||
@ -68,6 +68,7 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -214,13 +215,11 @@ mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let resp = handle(
|
||||
&device::Device::default(),
|
||||
&mut ds,
|
||||
&tst.filter_list_ans,
|
||||
tst.filter_list_req.as_ref(),
|
||||
);
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(&mut dev, &tst.filter_list_ans, tst.filter_list_req.as_ref());
|
||||
|
||||
if let Some(e) = &tst.expected_error {
|
||||
assert_eq!(true, resp.is_err(), "{}", tst.name);
|
||||
@ -229,7 +228,12 @@ mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,16 @@ use tracing::{info, warn};
|
||||
use crate::region;
|
||||
use crate::storage::device;
|
||||
use crate::uplink::UplinkFrameSet;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
uplink_frame_set: &UplinkFrameSet,
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending LinkADRReq mac-command"));
|
||||
}
|
||||
@ -82,7 +83,7 @@ pub fn handle(
|
||||
ds.nb_trans = link_adr_req.redundancy.nb_rep as u32;
|
||||
ds.enabled_uplink_channel_indices = chans.iter().map(|i| *i as u32).collect::<Vec<u32>>();
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, tx_power_index = ds.tx_power_index, dr = ds.dr, nb_trans = ds.nb_trans, enabled_channels = ?ds.enabled_uplink_channel_indices, "LinkADRReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, tx_power_index = ds.tx_power_index, dr = ds.dr, nb_trans = ds.nb_trans, enabled_channels = ?ds.enabled_uplink_channel_indices, "LinkADRReq acknowledged");
|
||||
} else if !ds.adr && ch_mask_ack {
|
||||
// In case the device has ADR disabled, at least it must acknowledge the
|
||||
// channel-mask. It does not have to acknowledge the other parameters.
|
||||
@ -113,7 +114,7 @@ pub fn handle(
|
||||
ds.tx_power_index = link_adr_req.tx_power as u32;
|
||||
}
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, tx_power_index = ds.tx_power_index, dr = ds.dr, nb_trans = ds.nb_trans, enabled_channels = ?ds.enabled_uplink_channel_indices, "LinkADRReq acknowledged (device has ADR disabled)");
|
||||
info!(dev_eui = %dev_eui, tx_power_index = ds.tx_power_index, dr = ds.dr, nb_trans = ds.nb_trans, enabled_channels = ?ds.enabled_uplink_channel_indices, "LinkADRReq acknowledged (device has ADR disabled)");
|
||||
} else {
|
||||
// increase the error counter
|
||||
let count = ds
|
||||
@ -153,6 +154,7 @@ pub fn handle(
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::region;
|
||||
use chirpstack_api::internal;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
@ -357,11 +359,11 @@ pub mod test {
|
||||
};
|
||||
|
||||
for tst in &tests {
|
||||
let dev = device::Device {
|
||||
let mut dev = device::Device {
|
||||
dev_eui: lrwn::EUI64::from_str("0102030405060708").unwrap(),
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let mut ds = tst.device_session.clone();
|
||||
let block = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRAns(
|
||||
tst.link_adr_ans.clone(),
|
||||
)]);
|
||||
@ -372,7 +374,7 @@ pub mod test {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let res = handle(&ufs, &dev, &mut ds, &block, pending.as_ref());
|
||||
let res = handle(&ufs, &mut dev, &block, pending.as_ref());
|
||||
if let Some(e) = &tst.expected_error {
|
||||
assert_eq!(true, res.is_err(), "{}", tst.name);
|
||||
assert_eq!(e, &format!("{}", res.err().unwrap()), "{}", tst.name);
|
||||
@ -380,7 +382,12 @@ pub mod test {
|
||||
assert_eq!(true, res.unwrap().is_none(), "{}", tst.name);
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ use crate::config;
|
||||
use crate::helpers::errors::PrintFullError;
|
||||
use crate::storage::{application, device, device_profile, mac_command, tenant};
|
||||
use crate::uplink::UplinkFrameSet;
|
||||
use chirpstack_api::internal;
|
||||
use lrwn::EUI64;
|
||||
|
||||
pub mod configure_fwd_limit;
|
||||
pub mod ctrl_uplink_list;
|
||||
@ -41,15 +39,13 @@ pub async fn handle_uplink<'a>(
|
||||
tenant: &tenant::Tenant,
|
||||
app: &application::Application,
|
||||
dp: &device_profile::DeviceProfile,
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
) -> Result<(Vec<lrwn::MACCommandSet>, bool)> {
|
||||
let conf = config::get();
|
||||
if conf.network.mac_commands_disabled {
|
||||
return Ok((Vec::new(), false));
|
||||
}
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui)?;
|
||||
let mut cids: Vec<lrwn::CID> = Vec::new(); // to maintain the CID order
|
||||
let mut blocks: HashMap<lrwn::CID, lrwn::MACCommandSet> = HashMap::new();
|
||||
|
||||
@ -81,18 +77,18 @@ pub async fn handle_uplink<'a>(
|
||||
);
|
||||
|
||||
// Get pending mac-command block, this could return None.
|
||||
let pending = match mac_command::get_pending(&dev_eui, cid).await {
|
||||
let pending = match mac_command::get_pending(&dev.dev_eui, cid).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(dev_eui = %dev_eui, cid = %cid, error = %e, "Get pending mac-command block error");
|
||||
error!(dev_eui = %dev.dev_eui, cid = %cid, error = %e, "Get pending mac-command block error");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Delete the pending mac-command.
|
||||
if pending.is_some() {
|
||||
if let Err(e) = mac_command::delete_pending(&dev_eui, cid).await {
|
||||
error!(dev_eui = %dev_eui, cid = %cid, error = %e, "Delete pending mac-command error");
|
||||
if let Err(e) = mac_command::delete_pending(&dev.dev_eui, cid).await {
|
||||
error!(dev_eui = %dev.dev_eui, cid = %cid, error = %e, "Delete pending mac-command error");
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,13 +103,12 @@ pub async fn handle_uplink<'a>(
|
||||
app,
|
||||
dp,
|
||||
dev,
|
||||
ds,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
warn!(dev_eui = %dev_eui, cid = %cid, error = %e.full(), "Handle mac-command error");
|
||||
warn!(dev_eui = %dev.dev_eui, cid = %cid, error = %e.full(), "Handle mac-command error");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@ -135,8 +130,7 @@ async fn handle(
|
||||
tenant: &tenant::Tenant,
|
||||
app: &application::Application,
|
||||
dp: &device_profile::DeviceProfile,
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
match cid {
|
||||
lrwn::CID::DevStatusAns => {
|
||||
@ -144,30 +138,26 @@ async fn handle(
|
||||
}
|
||||
lrwn::CID::DeviceModeInd => device_mode_ind::handle(dev, block).await,
|
||||
lrwn::CID::DeviceTimeReq => device_time::handle(uplink_frame_set, dev, block),
|
||||
lrwn::CID::LinkADRAns => link_adr::handle(uplink_frame_set, dev, ds, block, pending_block),
|
||||
lrwn::CID::LinkADRAns => link_adr::handle(uplink_frame_set, dev, block, pending_block),
|
||||
lrwn::CID::LinkCheckReq => link_check::handle(uplink_frame_set, dev, block),
|
||||
lrwn::CID::NewChannelAns => new_channel::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::PingSlotChannelAns => ping_slot_channel::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::PingSlotInfoReq => ping_slot_info::handle(dev, ds, block),
|
||||
lrwn::CID::RejoinParamSetupAns => rejoin_param_setup::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::NewChannelAns => new_channel::handle(dev, block, pending_block),
|
||||
lrwn::CID::PingSlotChannelAns => ping_slot_channel::handle(dev, block, pending_block),
|
||||
lrwn::CID::PingSlotInfoReq => ping_slot_info::handle(dev, block),
|
||||
lrwn::CID::RejoinParamSetupAns => rejoin_param_setup::handle(dev, block, pending_block),
|
||||
lrwn::CID::RekeyInd => rekey::handle(dev, block),
|
||||
lrwn::CID::ResetInd => reset::handle(dev, dp, ds, block),
|
||||
lrwn::CID::RxParamSetupAns => rx_param_setup::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::RxTimingSetupAns => rx_timing_setup::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::TxParamSetupAns => tx_param_setup::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::RelayConfAns => relay_conf::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::EndDeviceConfAns => end_device_conf::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::FilterListAns => filter_list::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::UpdateUplinkListAns => update_uplink_list::handle(dev, ds, block, pending_block),
|
||||
lrwn::CID::ConfigureFwdLimitAns => {
|
||||
configure_fwd_limit::handle(dev, ds, block, pending_block)
|
||||
}
|
||||
lrwn::CID::ResetInd => reset::handle(dev, dp, block),
|
||||
lrwn::CID::RxParamSetupAns => rx_param_setup::handle(dev, block, pending_block),
|
||||
lrwn::CID::RxTimingSetupAns => rx_timing_setup::handle(dev, block, pending_block),
|
||||
lrwn::CID::TxParamSetupAns => tx_param_setup::handle(dev, block, pending_block),
|
||||
lrwn::CID::RelayConfAns => relay_conf::handle(dev, block, pending_block),
|
||||
lrwn::CID::EndDeviceConfAns => end_device_conf::handle(dev, block, pending_block),
|
||||
lrwn::CID::FilterListAns => filter_list::handle(dev, block, pending_block),
|
||||
lrwn::CID::UpdateUplinkListAns => update_uplink_list::handle(dev, block, pending_block),
|
||||
lrwn::CID::ConfigureFwdLimitAns => configure_fwd_limit::handle(dev, block, pending_block),
|
||||
lrwn::CID::NotifyNewEndDeviceReq => {
|
||||
notify_new_end_device::handle(tenant, dp, app, dev, block).await
|
||||
}
|
||||
lrwn::CID::CtrlUplinkListAns => {
|
||||
ctrl_uplink_list::handle(dev, ds, block, pending_block).await
|
||||
}
|
||||
lrwn::CID::CtrlUplinkListAns => ctrl_uplink_list::handle(dev, block, pending_block).await,
|
||||
_ => {
|
||||
warn!(cid = %cid, "Unexpected CID");
|
||||
// Return OK, we don't want to break out of the uplink handling.
|
||||
@ -180,6 +170,8 @@ async fn handle(
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::config;
|
||||
use chirpstack_api::internal;
|
||||
use lrwn::EUI64;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
@ -213,16 +205,18 @@ pub mod test {
|
||||
let t: tenant::Tenant = Default::default();
|
||||
let app: application::Application = Default::default();
|
||||
let dp: device_profile::DeviceProfile = Default::default();
|
||||
let dev: device::Device = Default::default();
|
||||
let mut ds = internal::DeviceSession {
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
let mut dev = device::Device {
|
||||
dev_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// must respond
|
||||
let cmds = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxTimingSetupAns]);
|
||||
|
||||
let (resp, must_respond) = handle_uplink(&upfs, &cmds, &t, &app, &dp, &dev, &mut ds)
|
||||
let (resp, must_respond) = handle_uplink(&upfs, &cmds, &t, &app, &dp, &mut dev)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(0, resp.len());
|
||||
@ -233,7 +227,7 @@ pub mod test {
|
||||
conf.network.mac_commands_disabled = true;
|
||||
config::set(conf);
|
||||
|
||||
let (resp, must_respond) = handle_uplink(&upfs, &cmds, &t, &app, &dp, &dev, &mut ds)
|
||||
let (resp, must_respond) = handle_uplink(&upfs, &cmds, &t, &app, &dp, &mut dev)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(0, resp.len());
|
||||
|
@ -62,11 +62,13 @@ pub fn request(
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending NewChannelReq"));
|
||||
}
|
||||
@ -115,7 +117,7 @@ pub fn handle(
|
||||
.push(req_pl.ch_index as u32);
|
||||
}
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, freq = req_pl.freq, channel = req_pl.ch_index, min_dr = req_pl.min_dr, max_dr = req_pl.max_dr, "NewChannelReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, freq = req_pl.freq, channel = req_pl.ch_index, min_dr = req_pl.min_dr, max_dr = req_pl.max_dr, "NewChannelReq acknowledged");
|
||||
} else {
|
||||
let count = ds
|
||||
.mac_command_error_count
|
||||
@ -124,7 +126,7 @@ pub fn handle(
|
||||
*count += 1;
|
||||
|
||||
warn!(
|
||||
dev_eui = %dev.dev_eui,
|
||||
dev_eui = %dev_eui,
|
||||
freq = req_pl.freq,
|
||||
channel = req_pl.ch_index,
|
||||
min_dr = req_pl.min_dr,
|
||||
@ -469,16 +471,12 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&tst.new_channel_ans,
|
||||
tst.new_channel_req.as_ref(),
|
||||
);
|
||||
let res = handle(&mut dev, &tst.new_channel_ans, tst.new_channel_req.as_ref());
|
||||
|
||||
if let Some(e) = &tst.expected_error {
|
||||
assert_eq!(true, res.is_err(), "{}", tst.name);
|
||||
@ -487,7 +485,12 @@ pub mod test {
|
||||
assert_eq!(true, res.unwrap().is_none(), "{}", tst.name);
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn request(dr: u8, freq: u32) -> lrwn::MACCommandSet {
|
||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::PingSlotChannelReq(
|
||||
@ -11,11 +10,12 @@ pub fn request(dr: u8, freq: u32) -> lrwn::MACCommandSet {
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Pending PingSlotChannelReq expected"));
|
||||
}
|
||||
@ -66,6 +66,7 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -181,12 +182,12 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.ping_slot_channel_ans,
|
||||
tst.ping_slot_channel_req.as_ref(),
|
||||
);
|
||||
@ -198,7 +199,10 @@ pub mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
let mac = (**block)
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("MACCommandSet is empty"))?;
|
||||
@ -21,7 +22,7 @@ pub fn handle(
|
||||
|
||||
ds.class_b_ping_slot_nb = 1 << (7 - pl.periodicity);
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, periodicity = pl.periodicity, ping_slot_nb = ds.class_b_ping_slot_nb, "PingSlotInfoReq received");
|
||||
info!(dev_eui = %dev_eui, periodicity = pl.periodicity, ping_slot_nb = ds.class_b_ping_slot_nb, "PingSlotInfoReq received");
|
||||
|
||||
Ok(Some(lrwn::MACCommandSet::new(vec![
|
||||
lrwn::MACCommand::PingSlotInfoAns,
|
||||
@ -31,22 +32,19 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
#[test]
|
||||
fn test_handle() {
|
||||
let mut ds: internal::DeviceSession = Default::default();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(internal::DeviceSession::default()),
|
||||
..Default::default()
|
||||
};
|
||||
let block = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::PingSlotInfoReq(
|
||||
lrwn::PingSlotInfoReqPayload { periodicity: 3 },
|
||||
)]);
|
||||
let res = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&block,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(16, ds.class_b_ping_slot_nb);
|
||||
let res = handle(&mut dev, &block).unwrap();
|
||||
assert_eq!(16, dev.get_device_session().unwrap().class_b_ping_slot_nb);
|
||||
assert_eq!(
|
||||
Some(lrwn::MACCommandSet::new(vec![
|
||||
lrwn::MACCommand::PingSlotInfoAns,
|
||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn request(max_time_n: u8, max_count_n: u8) -> lrwn::MACCommandSet {
|
||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RejoinParamSetupReq(
|
||||
@ -14,11 +13,12 @@ pub fn request(max_time_n: u8, max_count_n: u8) -> lrwn::MACCommandSet {
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Pending RejoinParamSetupReq expected"));
|
||||
}
|
||||
@ -57,6 +57,7 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -159,12 +160,12 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.rejoin_param_setup_ans,
|
||||
tst.rejoin_param_setup_req.as_ref(),
|
||||
);
|
||||
@ -176,7 +177,12 @@ pub mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,13 @@ use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending RelayConfReq mac-command"));
|
||||
}
|
||||
@ -43,7 +45,7 @@ pub fn handle(
|
||||
&& ans_pl.default_ch_idx_ack
|
||||
&& ans_pl.cad_periodicity_ack
|
||||
{
|
||||
info!(dev_eui = %dev.dev_eui, "RelayConfReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, "RelayConfReq acknowledged");
|
||||
|
||||
if let Some(relay) = &mut ds.relay {
|
||||
relay.enabled = req_pl.channel_settings_relay.start_stop == 1;
|
||||
@ -56,7 +58,7 @@ pub fn handle(
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
dev_eui = %dev.dev_eui,
|
||||
dev_eui = %dev_eui,
|
||||
second_ch_ack_offset_ack = ans_pl.second_ch_ack_offset_ack,
|
||||
second_ch_dr_ack = ans_pl.second_ch_dr_ack,
|
||||
second_ch_idx_ack = ans_pl.second_ch_idx_ack,
|
||||
@ -177,13 +179,11 @@ mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let resp = handle(
|
||||
&device::Device::default(),
|
||||
&mut ds,
|
||||
&tst.relay_conf_ans,
|
||||
tst.relay_conf_req.as_ref(),
|
||||
);
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(&mut dev, &tst.relay_conf_ans, tst.relay_conf_req.as_ref());
|
||||
|
||||
if let Some(e) = &tst.expected_error {
|
||||
assert_eq!(true, resp.is_err(), "{}", tst.name);
|
||||
@ -192,7 +192,12 @@ mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,17 @@ use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
use crate::storage::{device, device_profile};
|
||||
use chirpstack_api::internal;
|
||||
|
||||
const SERV_LORAWAN_VERSION: lrwn::Version = lrwn::Version::LoRaWAN1_1;
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
dev: &mut device::Device,
|
||||
dp: &device_profile::DeviceProfile,
|
||||
ds: &mut internal::DeviceSession,
|
||||
block: &lrwn::MACCommandSet,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
let block_mac = (**block)
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("MACCommandSet is empty"))?;
|
||||
@ -21,7 +22,7 @@ pub fn handle(
|
||||
return Err(anyhow!("ResetInd expected"));
|
||||
};
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, dev_lorawan_version = %block_pl.dev_lorawan_version, serv_lorawan_version = %SERV_LORAWAN_VERSION, "ResetInd received");
|
||||
info!(dev_eui = %dev_eui, dev_lorawan_version = %block_pl.dev_lorawan_version, serv_lorawan_version = %SERV_LORAWAN_VERSION, "ResetInd received");
|
||||
|
||||
dp.reset_session_to_boot_params(ds);
|
||||
|
||||
@ -41,11 +42,29 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test_handle() {
|
||||
let dev: device::Device = Default::default();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(internal::DeviceSession {
|
||||
tx_power_index: 3,
|
||||
min_supported_tx_power_index: 1,
|
||||
max_supported_tx_power_index: 5,
|
||||
extra_uplink_channels: [(3, Default::default())].iter().cloned().collect(),
|
||||
rx1_delay: 3,
|
||||
rx1_dr_offset: 1,
|
||||
rx2_dr: 5,
|
||||
rx2_frequency: 868900000,
|
||||
enabled_uplink_channel_indices: vec![0, 1],
|
||||
class_b_ping_slot_dr: 3,
|
||||
class_b_ping_slot_freq: 868100000,
|
||||
nb_trans: 3,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let dp = device_profile::DeviceProfile {
|
||||
supports_otaa: false,
|
||||
abp_rx1_delay: 1,
|
||||
@ -57,26 +76,10 @@ pub mod test {
|
||||
class_b_ping_slot_nb_k: 1,
|
||||
..Default::default()
|
||||
};
|
||||
let mut ds = internal::DeviceSession {
|
||||
tx_power_index: 3,
|
||||
min_supported_tx_power_index: 1,
|
||||
max_supported_tx_power_index: 5,
|
||||
extra_uplink_channels: [(3, Default::default())].iter().cloned().collect(),
|
||||
rx1_delay: 3,
|
||||
rx1_dr_offset: 1,
|
||||
rx2_dr: 5,
|
||||
rx2_frequency: 868900000,
|
||||
enabled_uplink_channel_indices: vec![0, 1],
|
||||
class_b_ping_slot_dr: 3,
|
||||
class_b_ping_slot_freq: 868100000,
|
||||
nb_trans: 3,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let resp = handle(
|
||||
&dev,
|
||||
&mut dev,
|
||||
&dp,
|
||||
&mut ds,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::ResetInd(lrwn::ResetIndPayload {
|
||||
dev_lorawan_version: lrwn::Version::LoRaWAN1_1,
|
||||
})]),
|
||||
@ -93,7 +96,7 @@ pub mod test {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
internal::DeviceSession {
|
||||
&internal::DeviceSession {
|
||||
rx1_delay: 1,
|
||||
rx1_dr_offset: 0,
|
||||
rx2_dr: 0,
|
||||
@ -110,7 +113,7 @@ pub mod test {
|
||||
extra_uplink_channels: HashMap::new(),
|
||||
..Default::default()
|
||||
},
|
||||
ds
|
||||
dev.get_device_session().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn request(rx1_dr_offset: u8, rx2_freq: u32, rx2_dr: u8) -> lrwn::MACCommandSet {
|
||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxParamSetupReq(
|
||||
@ -18,11 +17,12 @@ pub fn request(rx1_dr_offset: u8, rx2_freq: u32, rx2_dr: u8) -> lrwn::MACCommand
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending RxParamSetupReq"));
|
||||
}
|
||||
@ -70,6 +70,7 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -182,12 +183,12 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.rx_param_setup_ans,
|
||||
tst.rx_param_setup_req.as_ref(),
|
||||
);
|
||||
@ -199,7 +200,12 @@ pub mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn request(rx1_delay: u8) -> lrwn::MACCommandSet {
|
||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxTimingSetupReq(
|
||||
@ -11,11 +10,13 @@ pub fn request(rx1_delay: u8) -> lrwn::MACCommandSet {
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
_block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Pending RxTimingSetupReq expected"));
|
||||
}
|
||||
@ -31,7 +32,7 @@ pub fn handle(
|
||||
};
|
||||
|
||||
ds.rx1_delay = req_pl.delay as u32;
|
||||
info!(dev_eui = %dev.dev_eui, rx1_delay = req_pl.delay, "RxTimingSetupReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, rx1_delay = req_pl.delay, "RxTimingSetupReq acknowledged");
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
@ -39,6 +40,7 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -100,12 +102,12 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.rx_timing_setup_ans,
|
||||
tst.rx_timing_setup_req.as_ref(),
|
||||
);
|
||||
@ -117,7 +119,12 @@ pub mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn request(
|
||||
uplink_dwell_time_400ms: bool,
|
||||
@ -29,11 +28,13 @@ pub fn request(
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
_block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let dev_eui = dev.dev_eui;
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending TxParamSetupReq"));
|
||||
}
|
||||
@ -52,13 +53,14 @@ pub fn handle(
|
||||
ds.downlink_dwell_time_400ms = req_pl.downlink_dwell_time == lrwn::DwellTime::Limit400ms;
|
||||
ds.uplink_max_eirp_index = req_pl.max_eirp as u32;
|
||||
|
||||
info!(dev_eui = %dev.dev_eui, uplink_dwell_time_400ms = ds.uplink_dwell_time_400ms, downlink_dwell_time_400ms = ds.downlink_dwell_time_400ms, uplink_max_eirp_index = ds.uplink_max_eirp_index, "TxParamSetupReq acknowledged");
|
||||
info!(dev_eui = %dev_eui, uplink_dwell_time_400ms = ds.uplink_dwell_time_400ms, downlink_dwell_time_400ms = ds.downlink_dwell_time_400ms, uplink_max_eirp_index = ds.uplink_max_eirp_index, "TxParamSetupReq acknowledged");
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -136,12 +138,12 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let resp = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.tx_param_setup_ans,
|
||||
tst.tx_param_setup_req.as_ref(),
|
||||
);
|
||||
@ -153,7 +155,12 @@ pub mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
use crate::storage::device;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub fn handle(
|
||||
_dev: &device::Device,
|
||||
ds: &mut internal::DeviceSession,
|
||||
dev: &mut device::Device,
|
||||
_block: &lrwn::MACCommandSet,
|
||||
pending: Option<&lrwn::MACCommandSet>,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let ds = dev.get_device_session_mut()?;
|
||||
|
||||
if pending.is_none() {
|
||||
return Err(anyhow!("Expected pending UpdateUplinkListReq mac-command"));
|
||||
}
|
||||
@ -43,6 +43,7 @@ pub fn handle(
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
@ -124,12 +125,13 @@ pub mod test {
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
let mut ds = tst.device_session.clone();
|
||||
let mut dev = device::Device {
|
||||
device_session: Some(tst.device_session.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let resp = handle(
|
||||
&device::Device {
|
||||
..Default::default()
|
||||
},
|
||||
&mut ds,
|
||||
&mut dev,
|
||||
&tst.update_uplink_list_ans,
|
||||
tst.update_uplink_list_req.as_ref(),
|
||||
);
|
||||
@ -141,7 +143,12 @@ pub mod test {
|
||||
assert_eq!(true, resp.unwrap().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(tst.expected_device_session, ds, "{}", tst.name);
|
||||
assert_eq!(
|
||||
&tst.expected_device_session,
|
||||
dev.get_device_session().unwrap(),
|
||||
"{}",
|
||||
tst.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,9 @@ enum Commands {
|
||||
#[arg(short, long, value_name = "NAME")]
|
||||
name: String,
|
||||
},
|
||||
|
||||
/// Migrate device-sessions from Redis to PostgreSQL.
|
||||
MigrateDeviceSessionsToPostgres {},
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -116,6 +119,7 @@ async fn main() -> Result<()> {
|
||||
.unwrap()
|
||||
}
|
||||
Some(Commands::CreateApiKey { name }) => cmd::create_api_key::run(name).await?,
|
||||
Some(Commands::MigrateDeviceSessionsToPostgres {}) => cmd::migrate_ds_to_pg::run().await?,
|
||||
None => cmd::root::run().await?,
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,20 @@ use diesel_async::RunQueryDsl;
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
|
||||
use chirpstack_api::internal;
|
||||
use lrwn::{DevAddr, EUI64};
|
||||
|
||||
use super::schema::{application, device, device_profile, multicast_group_device, tenant};
|
||||
use super::{error::Error, fields, get_async_db_conn};
|
||||
use crate::api::helpers::FromProto;
|
||||
use crate::config;
|
||||
|
||||
pub enum ValidationStatus {
|
||||
Ok(u32, Device),
|
||||
Retransmission(u32, Device),
|
||||
Reset(u32, Device),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, AsExpression, FromSqlRow)]
|
||||
#[diesel(sql_type = Text)]
|
||||
pub enum DeviceClass {
|
||||
@ -95,6 +103,24 @@ pub struct Device {
|
||||
pub tags: fields::KeyValue,
|
||||
pub variables: fields::KeyValue,
|
||||
pub join_eui: EUI64,
|
||||
pub secondary_dev_addr: Option<DevAddr>,
|
||||
pub device_session: Option<internal::DeviceSession>,
|
||||
}
|
||||
|
||||
#[derive(AsChangeset, Debug, Clone, Default)]
|
||||
#[diesel(table_name = device)]
|
||||
pub struct DeviceChangeset {
|
||||
pub last_seen_at: Option<Option<DateTime<Utc>>>,
|
||||
pub dr: Option<Option<i16>>,
|
||||
pub dev_addr: Option<Option<DevAddr>>,
|
||||
pub enabled_class: Option<DeviceClass>,
|
||||
pub join_eui: Option<EUI64>,
|
||||
pub secondary_dev_addr: Option<Option<DevAddr>>,
|
||||
pub device_session: Option<Option<internal::DeviceSession>>,
|
||||
pub margin: Option<i32>,
|
||||
pub external_power_source: Option<bool>,
|
||||
pub battery_level: Option<Option<BigDecimal>>,
|
||||
pub scheduler_run_after: Option<Option<DateTime<Utc>>>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@ -104,6 +130,22 @@ impl Device {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_device_session(&self) -> Result<&internal::DeviceSession, Error> {
|
||||
self.device_session
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::NotFound(self.dev_eui.to_string()))
|
||||
}
|
||||
|
||||
pub fn get_device_session_mut(&mut self) -> Result<&mut internal::DeviceSession, Error> {
|
||||
self.device_session
|
||||
.as_mut()
|
||||
.ok_or_else(|| Error::NotFound(self.dev_eui.to_string()))
|
||||
}
|
||||
|
||||
pub fn get_dev_addr(&self) -> Result<DevAddr> {
|
||||
self.dev_addr.ok_or_else(|| anyhow!("DevAddr is not set"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Device {
|
||||
@ -134,6 +176,8 @@ impl Default for Device {
|
||||
tags: fields::KeyValue::new(HashMap::new()),
|
||||
variables: fields::KeyValue::new(HashMap::new()),
|
||||
join_eui: EUI64::default(),
|
||||
secondary_dev_addr: None,
|
||||
device_session: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -237,6 +281,230 @@ pub async fn get(dev_eui: &EUI64) -> Result<Device, Error> {
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
// Return the device-session matching the given PhyPayload. This will fetch all device-session
|
||||
// associated with the used DevAddr and based on f_cont and mic, decides which one to use.
|
||||
// This function will increment the uplink frame-counter and will immediately update the
|
||||
// device-session in the database, to make sure that in case this function is called multiple
|
||||
// times, at most one will be valid.
|
||||
// On Ok response, the PhyPayload f_cnt will be set to the full 32bit frame-counter based on the
|
||||
// device-session context.
|
||||
pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
||||
relayed: bool,
|
||||
phy: &mut lrwn::PhyPayload,
|
||||
tx_dr: u8,
|
||||
tx_ch: u8,
|
||||
) -> Result<ValidationStatus, Error> {
|
||||
// Get the dev_addr and original f_cnt.
|
||||
let (dev_addr, f_cnt_orig) = if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
(pl.fhdr.devaddr, pl.fhdr.f_cnt)
|
||||
} else {
|
||||
return Err(Error::InvalidPayload("MacPayload".to_string()));
|
||||
};
|
||||
|
||||
let mut c = get_async_db_conn().await?;
|
||||
|
||||
c.build_transaction()
|
||||
.run::<ValidationStatus, Error, _>(|c| {
|
||||
Box::pin(async move {
|
||||
let mut devices: Vec<Device> = device::dsl::device
|
||||
.filter(
|
||||
device::dsl::dev_addr
|
||||
.eq(&dev_addr)
|
||||
.or(device::dsl::secondary_dev_addr.eq(&dev_addr)),
|
||||
)
|
||||
.filter(device::dsl::is_disabled.eq(false))
|
||||
.for_update()
|
||||
.load(c)
|
||||
.await?;
|
||||
|
||||
if devices.is_empty() {
|
||||
return Err(Error::NotFound(dev_addr.to_string()));
|
||||
}
|
||||
|
||||
for d in &mut devices {
|
||||
let mut sessions = vec![];
|
||||
|
||||
if let Some(ds) = &d.device_session {
|
||||
sessions.push(ds.clone());
|
||||
if let Some(ds) = &ds.pending_rejoin_device_session {
|
||||
sessions.push(*ds.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for ds in &mut sessions {
|
||||
if ds.dev_addr != dev_addr.to_vec() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the full 32bit frame-counter.
|
||||
let full_f_cnt = get_full_f_cnt_up(ds.f_cnt_up, f_cnt_orig);
|
||||
let f_nwk_s_int_key = lrwn::AES128Key::from_slice(&ds.f_nwk_s_int_key)?;
|
||||
let s_nwk_s_int_key = lrwn::AES128Key::from_slice(&ds.s_nwk_s_int_key)?;
|
||||
|
||||
// Check both the full frame-counter and the received frame-counter
|
||||
// truncated to the 16LSB.
|
||||
// The latter is needed in case of a frame-counter reset as the
|
||||
// GetFullFCntUp will think the 16LSB has rolled over and will
|
||||
// increment the 16MSB bit.
|
||||
let mut mic_ok = false;
|
||||
for f_cnt in [full_f_cnt, f_cnt_orig] {
|
||||
// Set the full f_cnt.
|
||||
if let lrwn::Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = f_cnt;
|
||||
}
|
||||
|
||||
mic_ok = phy
|
||||
.validate_uplink_data_mic(
|
||||
ds.mac_version().from_proto(),
|
||||
ds.conf_f_cnt,
|
||||
tx_dr,
|
||||
tx_ch,
|
||||
&f_nwk_s_int_key,
|
||||
&s_nwk_s_int_key,
|
||||
)
|
||||
.context("Validate MIC")?;
|
||||
|
||||
if mic_ok {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if mic_ok {
|
||||
let full_f_cnt = if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
pl.fhdr.f_cnt
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if let Some(relay) = &ds.relay {
|
||||
if !relayed && relay.ed_relay_only {
|
||||
info!(
|
||||
dev_eui = %d.dev_eui,
|
||||
"Only communication through relay is allowed"
|
||||
);
|
||||
return Err(Error::NotFound(dev_addr.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
if full_f_cnt >= ds.f_cnt_up {
|
||||
// We immediately save the device-session to make sure that concurrent calls for
|
||||
// the same uplink will fail on the frame-counter validation.
|
||||
let ds_f_cnt_up = ds.f_cnt_up;
|
||||
ds.f_cnt_up = full_f_cnt + 1;
|
||||
|
||||
let _ = diesel::update(device::dsl::device.find(d.dev_eui))
|
||||
.set(device::device_session.eq(&ds.clone()))
|
||||
.execute(c)
|
||||
.await?;
|
||||
|
||||
// We do return the device-session with original frame-counter
|
||||
ds.f_cnt_up = ds_f_cnt_up;
|
||||
d.device_session = Some(ds.clone());
|
||||
return Ok(ValidationStatus::Ok(full_f_cnt, d.clone()));
|
||||
} else if ds.skip_f_cnt_check {
|
||||
// re-transmission or frame-counter reset
|
||||
ds.f_cnt_up = 0;
|
||||
d.device_session = Some(ds.clone());
|
||||
return Ok(ValidationStatus::Ok(full_f_cnt, d.clone()));
|
||||
} else if full_f_cnt == (ds.f_cnt_up - 1) {
|
||||
// re-transmission, the frame-counter did not increment
|
||||
d.device_session = Some(ds.clone());
|
||||
return Ok(ValidationStatus::Retransmission(full_f_cnt, d.clone()));
|
||||
} else {
|
||||
d.device_session = Some(ds.clone());
|
||||
return Ok(ValidationStatus::Reset(full_f_cnt, d.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the original f_cnt.
|
||||
if let lrwn::Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = f_cnt_orig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidMIC)
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_for_phypayload(
|
||||
phy: &mut lrwn::PhyPayload,
|
||||
tx_dr: u8,
|
||||
tx_ch: u8,
|
||||
) -> Result<Device, Error> {
|
||||
// Get the dev_addr and original f_cnt.
|
||||
let (dev_addr, f_cnt_orig) = if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
(pl.fhdr.devaddr, pl.fhdr.f_cnt)
|
||||
} else {
|
||||
return Err(Error::InvalidPayload("MacPayload".to_string()));
|
||||
};
|
||||
|
||||
let devices: Vec<Device> = device::dsl::device
|
||||
.filter(
|
||||
device::dsl::dev_addr
|
||||
.eq(&dev_addr)
|
||||
.or(device::dsl::secondary_dev_addr.eq(&dev_addr)),
|
||||
)
|
||||
.filter(device::dsl::is_disabled.eq(false))
|
||||
.load(&mut get_async_db_conn().await?)
|
||||
.await?;
|
||||
|
||||
if devices.is_empty() {
|
||||
return Err(Error::NotFound(dev_addr.to_string()));
|
||||
}
|
||||
|
||||
for d in &devices {
|
||||
let mut sessions = vec![];
|
||||
|
||||
if let Some(ds) = &d.device_session {
|
||||
sessions.push(ds.clone());
|
||||
if let Some(ds) = &ds.pending_rejoin_device_session {
|
||||
sessions.push(*ds.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for ds in &mut sessions {
|
||||
if ds.dev_addr != dev_addr.to_vec() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the full 32bit frame-counter.
|
||||
let full_f_cnt = get_full_f_cnt_up(ds.f_cnt_up, f_cnt_orig);
|
||||
let f_nwk_s_int_key = lrwn::AES128Key::from_slice(&ds.f_nwk_s_int_key)?;
|
||||
let s_nwk_s_int_key = lrwn::AES128Key::from_slice(&ds.s_nwk_s_int_key)?;
|
||||
|
||||
// Set the full f_cnt
|
||||
if let lrwn::Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = full_f_cnt;
|
||||
}
|
||||
|
||||
let mic_ok = phy
|
||||
.validate_uplink_data_mic(
|
||||
ds.mac_version().from_proto(),
|
||||
ds.conf_f_cnt,
|
||||
tx_dr,
|
||||
tx_ch,
|
||||
&f_nwk_s_int_key,
|
||||
&s_nwk_s_int_key,
|
||||
)
|
||||
.context("Validate MIC")?;
|
||||
|
||||
if mic_ok && full_f_cnt >= ds.f_cnt_up {
|
||||
return Ok(d.clone());
|
||||
}
|
||||
|
||||
// Restore the original f_cnt.
|
||||
if let lrwn::Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = f_cnt_orig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidMIC)
|
||||
}
|
||||
|
||||
pub async fn update(d: Device) -> Result<Device, Error> {
|
||||
d.validate()?;
|
||||
|
||||
@ -260,80 +528,14 @@ pub async fn update(d: Device) -> Result<Device, Error> {
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
pub async fn set_enabled_class(dev_eui: &EUI64, mode: DeviceClass) -> Result<Device, Error> {
|
||||
let d: Device = diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set(device::enabled_class.eq(&mode))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
pub async fn partial_update(dev_eui: EUI64, d: &DeviceChangeset) -> Result<Device, Error> {
|
||||
let d = diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set(d)
|
||||
.get_result::<Device>(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))?;
|
||||
info!(dev_eui = %dev_eui, enabled_class = %mode, "Enabled class updated");
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
pub async fn set_join_eui(dev_eui: EUI64, join_eui: EUI64) -> Result<Device, Error> {
|
||||
let d: Device = diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set(device::join_eui.eq(&join_eui))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))?;
|
||||
info!(dev_eui = %dev_eui, join_eui = %join_eui, "Updated JoinEUI");
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
pub async fn set_dev_addr(dev_eui: EUI64, dev_addr: DevAddr) -> Result<Device, Error> {
|
||||
let d: Device = diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set(device::dev_addr.eq(&dev_addr))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))?;
|
||||
info!(dev_eui = %dev_eui, dev_addr = %dev_addr, "Updated DevAddr");
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
// In case the current_ts has been updated during the last device get and calling this update
|
||||
// function, this will return a NotFound error. The purpose of this error is to catch concurrent
|
||||
// scheduling, e.g. Class-A downlink and Class-B/C downlink. In such case we want to terminate one
|
||||
// of the downlinks.
|
||||
pub async fn set_scheduler_run_after(
|
||||
dev_eui: &EUI64,
|
||||
new_ts: Option<DateTime<Utc>>,
|
||||
) -> Result<Device, Error> {
|
||||
diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set(device::scheduler_run_after.eq(&new_ts))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))
|
||||
}
|
||||
|
||||
pub async fn set_last_seen_dr(dev_eui: &EUI64, dr: u8) -> Result<Device, Error> {
|
||||
let d: Device = diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set((
|
||||
device::last_seen_at.eq(Utc::now()),
|
||||
device::dr.eq(dr as i16),
|
||||
))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))?;
|
||||
info!(dev_eui = %dev_eui, dr = dr, "Data-rate updated");
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
pub async fn set_status(
|
||||
dev_eui: &EUI64,
|
||||
margin: i32,
|
||||
external_power_source: bool,
|
||||
battery_level: Option<BigDecimal>,
|
||||
) -> Result<Device, Error> {
|
||||
let d: Device = diesel::update(device::dsl::device.find(&dev_eui))
|
||||
.set((
|
||||
device::margin.eq(Some(margin)),
|
||||
device::external_power_source.eq(external_power_source),
|
||||
device::battery_level.eq(battery_level),
|
||||
))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))?;
|
||||
info!(dev_eui = %dev_eui, "Device status updated");
|
||||
info!(dev_eui = %dev_eui, "Device partially updated");
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
@ -530,12 +732,36 @@ pub async fn get_with_class_b_c_queue_items(limit: usize) -> Result<Vec<Device>>
|
||||
.context("Get with Class B/C queue-items transaction")
|
||||
}
|
||||
|
||||
// GetFullFCntUp returns the full 32bit frame-counter, given the fCntUp which
|
||||
// has been truncated to the last 16 LSB.
|
||||
// Notes:
|
||||
// * After a succesful validation of the FCntUp and the MIC, don't forget
|
||||
// to synchronize the device FCntUp with the packet FCnt.
|
||||
// * In case of a frame-counter rollover, the returned values will be less
|
||||
// than the given DeviceSession FCntUp. This must be validated outside this
|
||||
// function!
|
||||
// * In case of a re-transmission, the returned frame-counter equals
|
||||
// DeviceSession.FCntUp - 1, as the FCntUp value holds the next expected
|
||||
// frame-counter, not the FCntUp which was last seen.
|
||||
fn get_full_f_cnt_up(next_expected_full_fcnt: u32, truncated_f_cnt: u32) -> u32 {
|
||||
// Handle re-transmission.
|
||||
if truncated_f_cnt == (((next_expected_full_fcnt % (1 << 16)) as u16).wrapping_sub(1)) as u32 {
|
||||
return next_expected_full_fcnt - 1;
|
||||
}
|
||||
|
||||
let gap = ((truncated_f_cnt as u16).wrapping_sub((next_expected_full_fcnt % (1 << 16)) as u16))
|
||||
as u32;
|
||||
|
||||
next_expected_full_fcnt.wrapping_add(gap)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::storage;
|
||||
use crate::storage::device_queue;
|
||||
use crate::test;
|
||||
use lrwn::AES128Key;
|
||||
|
||||
struct FilterTest<'a> {
|
||||
filters: Filters,
|
||||
@ -696,13 +922,37 @@ pub mod test {
|
||||
assert_eq!(0, res.len());
|
||||
|
||||
// device in Class-B.
|
||||
let d = set_enabled_class(&d.dev_eui, DeviceClass::B).await.unwrap();
|
||||
let d = partial_update(
|
||||
d.dev_eui,
|
||||
&DeviceChangeset {
|
||||
enabled_class: Some(DeviceClass::B),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let res = get_with_class_b_c_queue_items(10).await.unwrap();
|
||||
let d = set_scheduler_run_after(&d.dev_eui, None).await.unwrap();
|
||||
let d = partial_update(
|
||||
d.dev_eui,
|
||||
&DeviceChangeset {
|
||||
scheduler_run_after: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(1, res.len());
|
||||
|
||||
// device in Class-C
|
||||
let d = set_enabled_class(&d.dev_eui, DeviceClass::C).await.unwrap();
|
||||
let d = partial_update(
|
||||
d.dev_eui,
|
||||
&DeviceChangeset {
|
||||
enabled_class: Some(DeviceClass::C),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let res = get_with_class_b_c_queue_items(10).await.unwrap();
|
||||
assert_eq!(1, res.len());
|
||||
|
||||
@ -712,7 +962,15 @@ pub mod test {
|
||||
assert_eq!(0, res.len());
|
||||
|
||||
// device in class C / downlink is pending.
|
||||
let _ = set_scheduler_run_after(&d.dev_eui, None).await.unwrap();
|
||||
let _ = partial_update(
|
||||
d.dev_eui,
|
||||
&DeviceChangeset {
|
||||
scheduler_run_after: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
qi.is_pending = true;
|
||||
qi.timeout_after = Some(Utc::now() + Duration::seconds(10));
|
||||
qi = device_queue::update_item(qi).await.unwrap();
|
||||
@ -726,4 +984,397 @@ pub mod test {
|
||||
let res = get_with_class_b_c_queue_items(10).await.unwrap();
|
||||
assert_eq!(1, res.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_full_f_cnt_up() {
|
||||
// server, device, expected
|
||||
let tests = vec![
|
||||
(1, 1, 1), // frame-counter is as expected
|
||||
(1 << 16, 0, 1 << 16), // frame-counter is as expected
|
||||
((1 << 16) + 1, 1, (1 << 16) + 1), // frame-counter is as expected
|
||||
(0, 1, 1), // one frame packet-loss
|
||||
((1 << 16) + 1, 2, (1 << 16) + 2), // one frame packet-loss
|
||||
(2, 1, 1), // re-transmission of previous frame
|
||||
((1 << 16) + 1, 0, (1 << 16)), // re-transmission of previous frame
|
||||
((1 << 16), (1 << 16) - 1, (1 << 16) - 1), // re-transmission of previous frame
|
||||
(u32::MAX, 0, 0), // 32bit frame-counter rollover
|
||||
];
|
||||
|
||||
for (i, tst) in tests.iter().enumerate() {
|
||||
let out = get_full_f_cnt_up(tst.0, tst.1);
|
||||
assert_eq!(tst.2, out, "Test: {}, expected: {}, got: {}", i, tst.2, out);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_device_session() {
|
||||
let _guard = test::prepare().await;
|
||||
|
||||
let t = storage::tenant::create(storage::tenant::Tenant {
|
||||
name: "test-tenant".into(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dp = storage::device_profile::create(storage::device_profile::DeviceProfile {
|
||||
name: "test-dp".into(),
|
||||
tenant_id: t.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let app = storage::application::create(storage::application::Application {
|
||||
name: "test-app".into(),
|
||||
tenant_id: t.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut devices = vec![
|
||||
Device {
|
||||
application_id: app.id,
|
||||
device_profile_id: dp.id,
|
||||
name: "0101010101010101".into(),
|
||||
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]),
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01,
|
||||
],
|
||||
f_cnt_up: 100,
|
||||
skip_f_cnt_check: true,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
Device {
|
||||
application_id: app.id,
|
||||
device_profile_id: dp.id,
|
||||
name: "0202020202020202".into(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]),
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02,
|
||||
],
|
||||
f_cnt_up: 200,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
Device {
|
||||
application_id: app.id,
|
||||
device_profile_id: dp.id,
|
||||
name: "0303030303030303".into(),
|
||||
dev_eui: EUI64::from_be_bytes([3, 3, 3, 3, 3, 3, 3, 3]),
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
secondary_dev_addr: Some(DevAddr::from_be_bytes([4, 3, 2, 1])),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03,
|
||||
],
|
||||
f_cnt_up: 300,
|
||||
pending_rejoin_device_session: Some(Box::new(internal::DeviceSession {
|
||||
dev_addr: vec![0x04, 0x03, 0x02, 0x01],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04,
|
||||
],
|
||||
f_cnt_up: 0,
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
Device {
|
||||
application_id: app.id,
|
||||
device_profile_id: dp.id,
|
||||
name: "0505050505050505".into(),
|
||||
dev_eui: EUI64::from_be_bytes([5, 5, 5, 5, 5, 5, 5, 5]),
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05,
|
||||
],
|
||||
f_cnt_up: (1 << 16) + 1,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
|
||||
for d in &mut devices {
|
||||
*d = create(d.clone()).await.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Test {
|
||||
name: String,
|
||||
dev_addr: DevAddr,
|
||||
s_nwk_s_int_key: AES128Key,
|
||||
f_nwk_s_int_key: AES128Key,
|
||||
f_cnt: u32,
|
||||
expected_retransmission: bool,
|
||||
expected_reset: bool,
|
||||
expected_dev_eui: EUI64,
|
||||
expected_fcnt_up: u32,
|
||||
expected_error: Option<String>,
|
||||
}
|
||||
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "matching dev_eui 0101010101010101".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[0].get_device_session().unwrap().f_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[0].get_device_session().unwrap().s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
f_cnt: devices[0].get_device_session().unwrap().f_cnt_up,
|
||||
expected_retransmission: false,
|
||||
expected_reset: false,
|
||||
expected_fcnt_up: devices[0].get_device_session().unwrap().f_cnt_up,
|
||||
expected_dev_eui: devices[0].dev_eui,
|
||||
expected_error: None,
|
||||
},
|
||||
Test {
|
||||
name: "matching dev_eui 0202020202020202".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[1].get_device_session().unwrap().f_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[1].get_device_session().unwrap().s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
f_cnt: devices[1].get_device_session().unwrap().f_cnt_up,
|
||||
expected_retransmission: false,
|
||||
expected_reset: false,
|
||||
expected_fcnt_up: devices[1].get_device_session().unwrap().f_cnt_up,
|
||||
expected_dev_eui: devices[1].dev_eui,
|
||||
expected_error: None,
|
||||
},
|
||||
Test {
|
||||
name: "matching dev_eui 0101010101010101 with frame-counter reset".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[0].get_device_session().unwrap().f_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[0].get_device_session().unwrap().s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
f_cnt: 0,
|
||||
expected_retransmission: false,
|
||||
expected_reset: false,
|
||||
expected_fcnt_up: 0,
|
||||
expected_dev_eui: devices[0].dev_eui,
|
||||
expected_error: None,
|
||||
},
|
||||
Test {
|
||||
name: "matching dev_eui 0202020202020202 with invalid frame-counter".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[1].get_device_session().unwrap().f_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[1].get_device_session().unwrap().s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
f_cnt: 0,
|
||||
expected_reset: true,
|
||||
expected_dev_eui: devices[1].dev_eui,
|
||||
..Default::default()
|
||||
},
|
||||
Test {
|
||||
name: "invalid DevAddr".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x01, 0x01, 0x01]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[0].get_device_session().unwrap().f_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(
|
||||
&devices[0].get_device_session().unwrap().s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap(),
|
||||
f_cnt: devices[0].get_device_session().unwrap().f_cnt_up,
|
||||
expected_error: Some("Object does not exist (id: 01010101)".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
Test {
|
||||
name: "invalid nwk_s_key".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_bytes([
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
]),
|
||||
s_nwk_s_int_key: AES128Key::from_bytes([
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
]),
|
||||
f_cnt: devices[0].get_device_session().unwrap().f_cnt_up,
|
||||
expected_error: Some("Invalid MIC".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
Test {
|
||||
name: "matching pending rejoin device-session".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x04, 0x03, 0x02, 0x01]),
|
||||
f_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04,
|
||||
]),
|
||||
s_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04,
|
||||
]),
|
||||
f_cnt: 0,
|
||||
expected_dev_eui: devices[2].dev_eui,
|
||||
expected_fcnt_up: 0,
|
||||
expected_retransmission: false,
|
||||
expected_error: None,
|
||||
expected_reset: false,
|
||||
},
|
||||
Test {
|
||||
name: "frame-counter rollover (16lsb)".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
]),
|
||||
s_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
]),
|
||||
f_cnt: (1 << 16) + 11,
|
||||
expected_dev_eui: devices[3].dev_eui,
|
||||
expected_fcnt_up: (1 << 16) + 11,
|
||||
expected_retransmission: false,
|
||||
expected_error: None,
|
||||
expected_reset: false,
|
||||
},
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
println!("> {}", tst.name);
|
||||
let mut phy = lrwn::PhyPayload {
|
||||
mhdr: lrwn::MHDR {
|
||||
m_type: lrwn::MType::UnconfirmedDataUp,
|
||||
major: lrwn::Major::LoRaWANR1,
|
||||
},
|
||||
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
|
||||
fhdr: lrwn::FHDR {
|
||||
devaddr: tst.dev_addr,
|
||||
f_ctrl: lrwn::FCtrl::default(),
|
||||
f_cnt: tst.f_cnt,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
mic: None,
|
||||
};
|
||||
|
||||
phy.set_uplink_data_mic(
|
||||
lrwn::MACVersion::LoRaWAN1_0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&tst.f_nwk_s_int_key,
|
||||
&tst.s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Truncate to 16LSB (as it would be transmitted over the air).
|
||||
if let lrwn::Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = tst.f_cnt % (1 << 16);
|
||||
}
|
||||
|
||||
let d = get_for_phypayload_and_incr_f_cnt_up(false, &mut phy, 0, 0).await;
|
||||
if tst.expected_error.is_some() {
|
||||
assert_eq!(true, d.is_err());
|
||||
assert_eq!(
|
||||
tst.expected_error.as_ref().unwrap(),
|
||||
&d.err().unwrap().to_string()
|
||||
);
|
||||
if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
assert_eq!(tst.f_cnt, pl.fhdr.f_cnt);
|
||||
}
|
||||
} else {
|
||||
let d = d.unwrap();
|
||||
|
||||
// Validate that the f_cnt of the PhyPayload was set to the full frame-counter.
|
||||
if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
assert_eq!(tst.expected_fcnt_up, pl.fhdr.f_cnt);
|
||||
}
|
||||
|
||||
if let ValidationStatus::Ok(full_f_cnt, d) = d {
|
||||
assert_eq!(false, tst.expected_retransmission);
|
||||
assert_eq!(tst.expected_dev_eui, d.dev_eui,);
|
||||
assert_eq!(tst.expected_fcnt_up, full_f_cnt);
|
||||
} else if let ValidationStatus::Retransmission(full_f_cnt, d) = d {
|
||||
assert_eq!(true, tst.expected_retransmission);
|
||||
assert_eq!(tst.expected_dev_eui, d.dev_eui,);
|
||||
assert_eq!(tst.expected_fcnt_up, full_f_cnt);
|
||||
} else if let ValidationStatus::Reset(_, d) = d {
|
||||
assert_eq!(true, tst.expected_reset);
|
||||
assert_eq!(tst.expected_dev_eui, d.dev_eui,);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +1,14 @@
|
||||
use std::collections::HashSet;
|
||||
use std::io::Cursor;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use prost::Message;
|
||||
use tracing::{error, info, trace};
|
||||
|
||||
use super::error::Error;
|
||||
use super::{get_async_redis_conn, redis_key};
|
||||
use crate::api::helpers::FromProto;
|
||||
use crate::config;
|
||||
use crate::helpers::errors::PrintFullError;
|
||||
use chirpstack_api::internal;
|
||||
use lrwn::{AES128Key, DevAddr, Payload, PhyPayload, EUI64};
|
||||
use lrwn::EUI64;
|
||||
|
||||
pub enum ValidationStatus {
|
||||
Ok(u32, internal::DeviceSession),
|
||||
Retransmission(u32, internal::DeviceSession),
|
||||
Reset(u32, internal::DeviceSession),
|
||||
}
|
||||
|
||||
pub async fn save(ds: &internal::DeviceSession) -> Result<()> {
|
||||
let eui = EUI64::from_slice(&ds.dev_eui)?;
|
||||
let addr = DevAddr::from_slice(&ds.dev_addr)?;
|
||||
|
||||
let conf = config::get();
|
||||
let addr_key = redis_key(format!("devaddr:{{{}}}", addr));
|
||||
let ds_key = redis_key(format!("device:{{{}}}:ds", eui));
|
||||
let b = ds.encode_to_vec();
|
||||
let ttl = conf.network.device_session_ttl.as_millis() as usize;
|
||||
|
||||
// Atomic add and pexpire.
|
||||
redis::pipe()
|
||||
.atomic()
|
||||
.cmd("SADD")
|
||||
.arg(&addr_key)
|
||||
.arg(&eui.to_be_bytes())
|
||||
.ignore()
|
||||
.cmd("PEXPIRE")
|
||||
.arg(&addr_key)
|
||||
.arg(ttl)
|
||||
.ignore()
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
// In case there is a pending rejoin session, make sure that the new
|
||||
// DevAddr also resolves to the device-session.
|
||||
if let Some(pending_ds) = &ds.pending_rejoin_device_session {
|
||||
let pending_addr = DevAddr::from_slice(&pending_ds.dev_addr)?;
|
||||
let pending_addr_key = redis_key(format!("devaddr:{{{}}}", pending_addr));
|
||||
|
||||
redis::pipe()
|
||||
.atomic()
|
||||
.cmd("SADD")
|
||||
.arg(&pending_addr_key)
|
||||
.arg(&eui.to_be_bytes())
|
||||
.ignore()
|
||||
.cmd("PEXPIRE")
|
||||
.arg(&pending_addr_key)
|
||||
.arg(ttl)
|
||||
.ignore()
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
}
|
||||
|
||||
redis::cmd("PSETEX")
|
||||
.arg(ds_key)
|
||||
.arg(ttl)
|
||||
.arg(b)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
info!(dev_eui = %eui, dev_addr = %addr, "Device-session saved");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get(dev_eui: &EUI64) -> Result<chirpstack_api::internal::DeviceSession, Error> {
|
||||
pub async fn get(dev_eui: &EUI64) -> Result<internal::DeviceSession, Error> {
|
||||
let key = redis_key(format!("device:{{{}}}:ds", dev_eui));
|
||||
|
||||
let v: Vec<u8> = redis::cmd("GET")
|
||||
@ -85,687 +19,7 @@ pub async fn get(dev_eui: &EUI64) -> Result<chirpstack_api::internal::DeviceSess
|
||||
if v.is_empty() {
|
||||
return Err(Error::NotFound(dev_eui.to_string()));
|
||||
}
|
||||
let ds = chirpstack_api::internal::DeviceSession::decode(&mut Cursor::new(v))
|
||||
.context("Decode device-session")?;
|
||||
let ds =
|
||||
internal::DeviceSession::decode(&mut Cursor::new(v)).context("Decode device-session")?;
|
||||
Ok(ds)
|
||||
}
|
||||
|
||||
pub async fn delete(dev_eui: &EUI64) -> Result<()> {
|
||||
let key = redis_key(format!("device:{{{}}}:ds", dev_eui));
|
||||
|
||||
redis::cmd("DEL")
|
||||
.arg(&key)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
info!(dev_eui = %dev_eui, "Device-session deleted");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Return the device-session matching the given PhyPayload. This will fetch all device-session
|
||||
// associated with the used DevAddr and based on f_cont and mic, decides which one to use.
|
||||
// This function will increment the uplink frame-counter and will immediately update the
|
||||
// device-session in the database, to make sure that in case this function is called multiple
|
||||
// times, at most one will be valid.
|
||||
// On Ok response, the PhyPayload f_cnt will be set to the full 32bit frame-counter based on the
|
||||
// device-session context.
|
||||
pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
||||
relayed: bool,
|
||||
phy: &mut PhyPayload,
|
||||
tx_dr: u8,
|
||||
tx_ch: u8,
|
||||
) -> Result<ValidationStatus, Error> {
|
||||
let mut _dev_addr = DevAddr::from_be_bytes([0x00, 0x00, 0x00, 0x00]);
|
||||
let mut _f_cnt_orig = 0;
|
||||
|
||||
// Get the dev_addr and original f_cnt.
|
||||
if let Payload::MACPayload(pl) = &phy.payload {
|
||||
_dev_addr = pl.fhdr.devaddr;
|
||||
_f_cnt_orig = pl.fhdr.f_cnt;
|
||||
} else {
|
||||
return Err(Error::InvalidPayload("MacPayload".to_string()));
|
||||
}
|
||||
|
||||
let device_sessions = get_for_dev_addr(_dev_addr)
|
||||
.await
|
||||
.context("Get device-sessions for DevAddr")?;
|
||||
if device_sessions.is_empty() {
|
||||
return Err(Error::NotFound(_dev_addr.to_string()));
|
||||
}
|
||||
|
||||
for mut ds in device_sessions {
|
||||
// Get the full 32bit frame-counter.
|
||||
let full_f_cnt = get_full_f_cnt_up(ds.f_cnt_up, _f_cnt_orig);
|
||||
let f_nwk_s_int_key = AES128Key::from_slice(&ds.f_nwk_s_int_key)?;
|
||||
let s_nwk_s_int_key = AES128Key::from_slice(&ds.s_nwk_s_int_key)?;
|
||||
|
||||
// Check both the full frame-counter and the received frame-counter
|
||||
// truncated to the 16LSB.
|
||||
// The latter is needed in case of a frame-counter reset as the
|
||||
// GetFullFCntUp will think the 16LSB has rolled over and will
|
||||
// increment the 16MSB bit.
|
||||
let mut mic_ok = false;
|
||||
for f_cnt in &[full_f_cnt, _f_cnt_orig] {
|
||||
// Set the full f_cnt.
|
||||
if let Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = *f_cnt;
|
||||
}
|
||||
|
||||
mic_ok = phy
|
||||
.validate_uplink_data_mic(
|
||||
ds.mac_version().from_proto(),
|
||||
ds.conf_f_cnt,
|
||||
tx_dr,
|
||||
tx_ch,
|
||||
&f_nwk_s_int_key,
|
||||
&s_nwk_s_int_key,
|
||||
)
|
||||
.context("Validate MIC")?;
|
||||
|
||||
if mic_ok {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if mic_ok {
|
||||
let full_f_cnt = if let Payload::MACPayload(pl) = &phy.payload {
|
||||
pl.fhdr.f_cnt
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if let Some(relay) = &ds.relay {
|
||||
if !relayed && relay.ed_relay_only {
|
||||
info!(
|
||||
dev_eui = hex::encode(ds.dev_eui),
|
||||
"Only communication through relay is allowed"
|
||||
);
|
||||
return Err(Error::NotFound(_dev_addr.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
if full_f_cnt >= ds.f_cnt_up {
|
||||
// Make sure that in case of concurrent calls for the same uplink only one will
|
||||
// pass. Either the concurrent call would read the incremented uplink frame-counter
|
||||
// or it is unable to aquire the lock.
|
||||
|
||||
let lock_key = redis_key(format!(
|
||||
"device:{{{}}}:ds:lock:{}",
|
||||
hex::encode(&ds.dev_eui),
|
||||
full_f_cnt,
|
||||
));
|
||||
let set: bool = redis::cmd("SET")
|
||||
.arg(&lock_key)
|
||||
.arg("lock")
|
||||
.arg("EX")
|
||||
.arg(1_usize)
|
||||
.arg("NX")
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
if !set {
|
||||
return Ok(ValidationStatus::Retransmission(full_f_cnt, ds));
|
||||
}
|
||||
|
||||
// We immediately save the device-session to make sure that concurrent calls for
|
||||
// the same uplink will fail on the frame-counter validation.
|
||||
let ds_f_cnt_up = ds.f_cnt_up;
|
||||
ds.f_cnt_up = full_f_cnt + 1;
|
||||
save(&ds).await?;
|
||||
ds.f_cnt_up = ds_f_cnt_up;
|
||||
|
||||
return Ok(ValidationStatus::Ok(full_f_cnt, ds));
|
||||
} else if ds.skip_f_cnt_check {
|
||||
// re-transmission or frame-counter reset
|
||||
ds.f_cnt_up = 0;
|
||||
return Ok(ValidationStatus::Ok(full_f_cnt, ds));
|
||||
} else if full_f_cnt == (ds.f_cnt_up - 1) {
|
||||
// re-transmission, the frame-counter did not increment
|
||||
return Ok(ValidationStatus::Retransmission(full_f_cnt, ds));
|
||||
} else {
|
||||
return Ok(ValidationStatus::Reset(full_f_cnt, ds));
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the original f_cnt.
|
||||
if let Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = _f_cnt_orig;
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidMIC)
|
||||
}
|
||||
|
||||
// Simmilar to get_for_phypayload_and_incr_f_cnt_up, but only retrieves the device-session for the
|
||||
// given PhyPayload. As it does not return the ValidationStatus, it only returns the DeviceSession
|
||||
// in case of a valid frame-counter.
|
||||
// On Ok response, the PhyPayload f_cnt will be set to the full 32bit frame-counter based on the
|
||||
// device-session context.
|
||||
pub async fn get_for_phypayload(
|
||||
phy: &mut PhyPayload,
|
||||
tx_dr: u8,
|
||||
tx_ch: u8,
|
||||
) -> Result<internal::DeviceSession, Error> {
|
||||
// Get the dev_addr and original f_cnt.
|
||||
let (dev_addr, f_cnt_orig) = if let Payload::MACPayload(pl) = &phy.payload {
|
||||
(pl.fhdr.devaddr, pl.fhdr.f_cnt)
|
||||
} else {
|
||||
return Err(Error::InvalidPayload("MacPayload".to_string()));
|
||||
};
|
||||
|
||||
let device_sessions = get_for_dev_addr(dev_addr)
|
||||
.await
|
||||
.context("Get device-sessions for DevAddr")?;
|
||||
if device_sessions.is_empty() {
|
||||
return Err(Error::NotFound(dev_addr.to_string()));
|
||||
}
|
||||
|
||||
for ds in device_sessions {
|
||||
// Get the full 32bit frame-counter.
|
||||
let full_f_cnt = get_full_f_cnt_up(ds.f_cnt_up, f_cnt_orig);
|
||||
let f_nwk_s_int_key = AES128Key::from_slice(&ds.f_nwk_s_int_key)?;
|
||||
let s_nwk_s_int_key = AES128Key::from_slice(&ds.s_nwk_s_int_key)?;
|
||||
|
||||
// Set the full f_cnt
|
||||
if let Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = full_f_cnt;
|
||||
}
|
||||
|
||||
let mic_ok = phy
|
||||
.validate_uplink_data_mic(
|
||||
ds.mac_version().from_proto(),
|
||||
ds.conf_f_cnt,
|
||||
tx_dr,
|
||||
tx_ch,
|
||||
&f_nwk_s_int_key,
|
||||
&s_nwk_s_int_key,
|
||||
)
|
||||
.context("Validate MIC")?;
|
||||
|
||||
if mic_ok && full_f_cnt >= ds.f_cnt_up {
|
||||
return Ok(ds);
|
||||
}
|
||||
|
||||
// Restore the original f_cnt.
|
||||
if let Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = f_cnt_orig;
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidMIC)
|
||||
}
|
||||
|
||||
async fn get_dev_euis_for_dev_addr(dev_addr: DevAddr) -> Result<Vec<EUI64>> {
|
||||
let key = redis_key(format!("devaddr:{{{}}}", dev_addr));
|
||||
|
||||
let dev_euis: HashSet<Vec<u8>> = redis::cmd("SMEMBERS")
|
||||
.arg(key)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await
|
||||
.context("Get DevEUIs for DevAddr")?;
|
||||
|
||||
let mut out = Vec::new();
|
||||
for dev_eui in &dev_euis {
|
||||
out.push(EUI64::from_slice(dev_eui)?);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
async fn remove_dev_eui_from_dev_addr_set(dev_addr: DevAddr, dev_eui: EUI64) -> Result<()> {
|
||||
let key = redis_key(format!("devaddr:{{{}}}", dev_addr));
|
||||
|
||||
redis::cmd("SREM")
|
||||
.arg(key)
|
||||
.arg(&dev_eui.to_be_bytes())
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_for_dev_addr(dev_addr: DevAddr) -> Result<Vec<internal::DeviceSession>> {
|
||||
trace!(dev_addr = %dev_addr, "Getting device-session for DevAddr");
|
||||
let dev_euis = get_dev_euis_for_dev_addr(dev_addr).await?;
|
||||
|
||||
let mut out = Vec::new();
|
||||
for dev_eui in dev_euis {
|
||||
let ds = match get(&dev_eui).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
if let Error::NotFound(_) = e {
|
||||
if let Err(e) = remove_dev_eui_from_dev_addr_set(dev_addr, dev_eui).await {
|
||||
error!(dev_addr = %dev_addr, dev_eui = %dev_eui, error = %e.full(), "Remove DevEUI from DevAddr->DevEUI set error");
|
||||
}
|
||||
} else {
|
||||
error!(dev_addr = %dev_addr, dev_eui = %dev_eui, error = %e.full(), "Get device-session for DevEUI error");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let ds_dev_addr = DevAddr::from_slice(&ds.dev_addr)?;
|
||||
|
||||
// When a pending rejoin device-session context is set and it has
|
||||
// the given devAddr, add it to the items list.
|
||||
if let Some(pending_ds) = &ds.pending_rejoin_device_session {
|
||||
let pending_dev_addr = DevAddr::from_slice(&pending_ds.dev_addr)?;
|
||||
if pending_dev_addr == dev_addr {
|
||||
out.push(*pending_ds.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// It is possible that the "main" device-session maps to a different
|
||||
// devAddr as the PendingRejoinDeviceSession is set (using the devAddr
|
||||
// that is used for the lookup).
|
||||
if ds_dev_addr == dev_addr {
|
||||
out.push(ds);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
// GetFullFCntUp returns the full 32bit frame-counter, given the fCntUp which
|
||||
// has been truncated to the last 16 LSB.
|
||||
// Notes:
|
||||
// * After a succesful validation of the FCntUp and the MIC, don't forget
|
||||
// to synchronize the device FCntUp with the packet FCnt.
|
||||
// * In case of a frame-counter rollover, the returned values will be less
|
||||
// than the given DeviceSession FCntUp. This must be validated outside this
|
||||
// function!
|
||||
// * In case of a re-transmission, the returned frame-counter equals
|
||||
// DeviceSession.FCntUp - 1, as the FCntUp value holds the next expected
|
||||
// frame-counter, not the FCntUp which was last seen.
|
||||
fn get_full_f_cnt_up(next_expected_full_fcnt: u32, truncated_f_cnt: u32) -> u32 {
|
||||
// Handle re-transmission.
|
||||
if truncated_f_cnt == (((next_expected_full_fcnt % (1 << 16)) as u16).wrapping_sub(1)) as u32 {
|
||||
return next_expected_full_fcnt - 1;
|
||||
}
|
||||
|
||||
let gap = ((truncated_f_cnt as u16).wrapping_sub((next_expected_full_fcnt % (1 << 16)) as u16))
|
||||
as u32;
|
||||
|
||||
next_expected_full_fcnt.wrapping_add(gap)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::test;
|
||||
|
||||
#[test]
|
||||
fn test_get_full_f_cnt_up() {
|
||||
// server, device, expected
|
||||
let tests = vec![
|
||||
(1, 1, 1), // frame-counter is as expected
|
||||
(1 << 16, 0, 1 << 16), // frame-counter is as expected
|
||||
((1 << 16) + 1, 1, (1 << 16) + 1), // frame-counter is as expected
|
||||
(0, 1, 1), // one frame packet-loss
|
||||
((1 << 16) + 1, 2, (1 << 16) + 2), // one frame packet-loss
|
||||
(2, 1, 1), // re-transmission of previous frame
|
||||
((1 << 16) + 1, 0, (1 << 16)), // re-transmission of previous frame
|
||||
((1 << 16), (1 << 16) - 1, (1 << 16) - 1), // re-transmission of previous frame
|
||||
(u32::MAX, 0, 0), // 32bit frame-counter rollover
|
||||
];
|
||||
|
||||
for (i, tst) in tests.iter().enumerate() {
|
||||
let out = get_full_f_cnt_up(tst.0, tst.1);
|
||||
assert_eq!(tst.2, out, "Test: {}, expected: {}, got: {}", i, tst.2, out);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_device_session() {
|
||||
let _guard = test::prepare().await;
|
||||
|
||||
let device_sessions = vec![
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
dev_eui: vec![0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01,
|
||||
],
|
||||
f_cnt_up: 100,
|
||||
skip_f_cnt_check: true,
|
||||
..Default::default()
|
||||
},
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
dev_eui: vec![0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02,
|
||||
],
|
||||
f_cnt_up: 200,
|
||||
..Default::default()
|
||||
},
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
dev_eui: vec![0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03,
|
||||
],
|
||||
f_cnt_up: 300,
|
||||
pending_rejoin_device_session: Some(Box::new(internal::DeviceSession {
|
||||
dev_addr: vec![0x04, 0x03, 0x02, 0x01],
|
||||
dev_eui: vec![0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04,
|
||||
],
|
||||
f_cnt_up: 0,
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![0x01, 0x02, 0x03, 0x04],
|
||||
dev_eui: vec![0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05],
|
||||
s_nwk_s_int_key: vec![
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
],
|
||||
f_nwk_s_int_key: vec![
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
],
|
||||
nwk_s_enc_key: vec![
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
],
|
||||
f_cnt_up: (1 << 16) + 1,
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
|
||||
for ds in &device_sessions {
|
||||
save(ds).await.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Test {
|
||||
name: String,
|
||||
dev_addr: DevAddr,
|
||||
s_nwk_s_int_key: AES128Key,
|
||||
f_nwk_s_int_key: AES128Key,
|
||||
f_cnt: u32,
|
||||
expected_retransmission: bool,
|
||||
expected_reset: bool,
|
||||
expected_dev_eui: EUI64,
|
||||
expected_fcnt_up: u32,
|
||||
expected_error: Option<String>,
|
||||
}
|
||||
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "matching dev_eui 0101010101010101".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(&device_sessions[0].f_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(&device_sessions[0].s_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
f_cnt: device_sessions[0].f_cnt_up,
|
||||
expected_retransmission: false,
|
||||
expected_reset: false,
|
||||
expected_fcnt_up: device_sessions[0].f_cnt_up,
|
||||
expected_dev_eui: EUI64::from_slice(&device_sessions[0].dev_eui).unwrap(),
|
||||
expected_error: None,
|
||||
},
|
||||
Test {
|
||||
name: "matching dev_eui 0202020202020202".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(&device_sessions[1].f_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(&device_sessions[1].s_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
f_cnt: device_sessions[1].f_cnt_up,
|
||||
expected_retransmission: false,
|
||||
expected_reset: false,
|
||||
expected_fcnt_up: device_sessions[1].f_cnt_up,
|
||||
expected_dev_eui: EUI64::from_slice(&device_sessions[1].dev_eui).unwrap(),
|
||||
expected_error: None,
|
||||
},
|
||||
Test {
|
||||
name: "matching dev_eui 0101010101010101 with frame-counter reset".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(&device_sessions[0].f_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(&device_sessions[0].s_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
f_cnt: 0,
|
||||
expected_retransmission: false,
|
||||
expected_reset: false,
|
||||
expected_fcnt_up: 0,
|
||||
expected_dev_eui: EUI64::from_slice(&device_sessions[0].dev_eui).unwrap(),
|
||||
expected_error: None,
|
||||
},
|
||||
Test {
|
||||
name: "matching dev_eui 0202020202020202 with invalid frame-counter".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(&device_sessions[1].f_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(&device_sessions[1].s_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
f_cnt: 0,
|
||||
expected_reset: true,
|
||||
expected_dev_eui: EUI64::from_slice(&device_sessions[1].dev_eui).unwrap(),
|
||||
..Default::default()
|
||||
},
|
||||
Test {
|
||||
name: "invalid DevAddr".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x01, 0x01, 0x01]),
|
||||
f_nwk_s_int_key: AES128Key::from_slice(&device_sessions[0].f_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
s_nwk_s_int_key: AES128Key::from_slice(&device_sessions[0].s_nwk_s_int_key)
|
||||
.unwrap(),
|
||||
f_cnt: device_sessions[0].f_cnt_up,
|
||||
expected_error: Some("Object does not exist (id: 01010101)".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
Test {
|
||||
name: "invalid nwk_s_key".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_bytes([
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
]),
|
||||
s_nwk_s_int_key: AES128Key::from_bytes([
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
]),
|
||||
f_cnt: device_sessions[0].f_cnt_up,
|
||||
expected_error: Some("Invalid MIC".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
Test {
|
||||
name: "matching pending rejoin device-session".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x04, 0x03, 0x02, 0x01]),
|
||||
f_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04,
|
||||
]),
|
||||
s_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04,
|
||||
]),
|
||||
f_cnt: 0,
|
||||
expected_dev_eui: EUI64::from_slice(&device_sessions[2].dev_eui).unwrap(),
|
||||
expected_fcnt_up: 0,
|
||||
expected_retransmission: false,
|
||||
expected_error: None,
|
||||
expected_reset: false,
|
||||
},
|
||||
Test {
|
||||
name: "frame-counter rollover (16lsb)".to_string(),
|
||||
dev_addr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
|
||||
f_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
]),
|
||||
s_nwk_s_int_key: AES128Key::from_bytes([
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05,
|
||||
]),
|
||||
f_cnt: (1 << 16) + 11,
|
||||
expected_dev_eui: EUI64::from_slice(&device_sessions[3].dev_eui).unwrap(),
|
||||
expected_fcnt_up: (1 << 16) + 11,
|
||||
expected_retransmission: false,
|
||||
expected_error: None,
|
||||
expected_reset: false,
|
||||
},
|
||||
];
|
||||
|
||||
for tst in &tests {
|
||||
println!("> {}", tst.name);
|
||||
let mut phy = lrwn::PhyPayload {
|
||||
mhdr: lrwn::MHDR {
|
||||
m_type: lrwn::MType::UnconfirmedDataUp,
|
||||
major: lrwn::Major::LoRaWANR1,
|
||||
},
|
||||
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
|
||||
fhdr: lrwn::FHDR {
|
||||
devaddr: tst.dev_addr,
|
||||
f_ctrl: lrwn::FCtrl::default(),
|
||||
f_cnt: tst.f_cnt,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
mic: None,
|
||||
};
|
||||
|
||||
phy.set_uplink_data_mic(
|
||||
lrwn::MACVersion::LoRaWAN1_0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&tst.f_nwk_s_int_key,
|
||||
&tst.s_nwk_s_int_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Truncate to 16LSB (as it would be transmitted over the air).
|
||||
if let lrwn::Payload::MACPayload(pl) = &mut phy.payload {
|
||||
pl.fhdr.f_cnt = tst.f_cnt % (1 << 16);
|
||||
}
|
||||
|
||||
let ds_res = get_for_phypayload_and_incr_f_cnt_up(false, &mut phy, 0, 0).await;
|
||||
if tst.expected_error.is_some() {
|
||||
assert_eq!(true, ds_res.is_err());
|
||||
assert_eq!(
|
||||
tst.expected_error.as_ref().unwrap(),
|
||||
&ds_res.err().unwrap().to_string()
|
||||
);
|
||||
if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
assert_eq!(tst.f_cnt, pl.fhdr.f_cnt);
|
||||
}
|
||||
} else {
|
||||
let ds = ds_res.unwrap();
|
||||
|
||||
// Validate that the f_cnt of the PhyPayload was set to the full frame-counter.
|
||||
if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||
assert_eq!(tst.expected_fcnt_up, pl.fhdr.f_cnt);
|
||||
}
|
||||
|
||||
if let ValidationStatus::Ok(full_f_cnt, ds) = ds {
|
||||
assert_eq!(false, tst.expected_retransmission);
|
||||
assert_eq!(
|
||||
tst.expected_dev_eui,
|
||||
EUI64::from_slice(&ds.dev_eui).unwrap()
|
||||
);
|
||||
assert_eq!(tst.expected_fcnt_up, full_f_cnt);
|
||||
} else if let ValidationStatus::Retransmission(full_f_cnt, ds) = ds {
|
||||
assert_eq!(true, tst.expected_retransmission);
|
||||
assert_eq!(
|
||||
tst.expected_dev_eui,
|
||||
EUI64::from_slice(&ds.dev_eui).unwrap()
|
||||
);
|
||||
assert_eq!(tst.expected_fcnt_up, full_f_cnt);
|
||||
} else if let ValidationStatus::Reset(_, ds) = ds {
|
||||
assert_eq!(true, tst.expected_reset);
|
||||
assert_eq!(
|
||||
tst.expected_dev_eui,
|
||||
EUI64::from_slice(&ds.dev_eui).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_for_dev_addr() {
|
||||
let _guard = test::prepare().await;
|
||||
|
||||
let dev_eui_1 = EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]);
|
||||
let dev_eui_2 = EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]);
|
||||
let dev_addr = DevAddr::from_be_bytes([1, 2, 3, 4]);
|
||||
|
||||
let ds_1 = internal::DeviceSession {
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
dev_eui: dev_eui_1.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let ds_2 = internal::DeviceSession {
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
dev_eui: dev_eui_2.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
save(&ds_1).await.unwrap();
|
||||
save(&ds_2).await.unwrap();
|
||||
|
||||
let dss = get_for_dev_addr(dev_addr).await.unwrap();
|
||||
assert_eq!(2, dss.len());
|
||||
|
||||
let dev_euis = get_dev_euis_for_dev_addr(dev_addr).await.unwrap();
|
||||
assert_eq!(2, dev_euis.len());
|
||||
|
||||
// At this point there is still a 'dangling' pointer from DevAddr->DevEUI.
|
||||
delete(&dev_eui_2).await.unwrap();
|
||||
let dev_euis = get_dev_euis_for_dev_addr(dev_addr).await.unwrap();
|
||||
assert_eq!(2, dev_euis.len());
|
||||
|
||||
// This should only return one device-session.
|
||||
let dss = get_for_dev_addr(dev_addr).await.unwrap();
|
||||
assert_eq!(1, dss.len());
|
||||
assert_eq!(dev_eui_1.to_vec(), dss[0].dev_eui);
|
||||
|
||||
// 'dangling' DevAddr->DevEUI pointers have been cleaned up.
|
||||
let dev_euis = get_dev_euis_for_dev_addr(dev_addr).await.unwrap();
|
||||
assert_eq!(1, dev_euis.len());
|
||||
assert_eq!(dev_eui_1, dev_euis[0]);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ diesel::table! {
|
||||
tags -> Jsonb,
|
||||
variables -> Jsonb,
|
||||
join_eui -> Bytea,
|
||||
secondary_dev_addr -> Nullable<Bytea>,
|
||||
device_session -> Nullable<Bytea>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ use crate::gateway::backend::mock as gateway_mock;
|
||||
use crate::integration::mock;
|
||||
use crate::storage::{
|
||||
device::{self, DeviceClass},
|
||||
device_queue, device_session, downlink_frame, get_async_redis_conn, redis_key,
|
||||
device_queue, downlink_frame, get_async_redis_conn, redis_key,
|
||||
};
|
||||
use chirpstack_api::{gw, integration as integration_pb, internal, stream};
|
||||
use lrwn::EUI64;
|
||||
@ -27,7 +27,8 @@ pub fn f_cnt_up(dev_eui: EUI64, f_cnt: u32) -> Validator {
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(f_cnt, ds.f_cnt_up);
|
||||
})
|
||||
})
|
||||
@ -37,7 +38,8 @@ pub fn n_f_cnt_down(dev_eui: EUI64, f_cnt: u32) -> Validator {
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(f_cnt, ds.n_f_cnt_down);
|
||||
})
|
||||
})
|
||||
@ -47,7 +49,8 @@ pub fn a_f_cnt_down(dev_eui: EUI64, f_cnt: u32) -> Validator {
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(f_cnt, ds.a_f_cnt_down);
|
||||
})
|
||||
})
|
||||
@ -57,7 +60,8 @@ pub fn tx_power_index(dev_eui: EUI64, tx_power: u32) -> Validator {
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(tx_power, ds.tx_power_index);
|
||||
})
|
||||
})
|
||||
@ -67,7 +71,8 @@ pub fn nb_trans(dev_eui: EUI64, nb_trans: u32) -> Validator {
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(nb_trans, ds.nb_trans);
|
||||
})
|
||||
})
|
||||
@ -78,7 +83,8 @@ pub fn enabled_uplink_channel_indices(dev_eui: EUI64, channels: Vec<u32>) -> Val
|
||||
let dev_eui = dev_eui.clone();
|
||||
let channels = channels.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(channels, ds.enabled_uplink_channel_indices);
|
||||
})
|
||||
})
|
||||
@ -88,7 +94,8 @@ pub fn dr(dev_eui: EUI64, dr: u32) -> Validator {
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(dr, ds.dr);
|
||||
})
|
||||
})
|
||||
@ -98,7 +105,8 @@ pub fn mac_command_error_count(dev_eui: EUI64, cid: lrwn::CID, count: u32) -> Va
|
||||
Box::new(move || {
|
||||
let dev_eui = dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(
|
||||
count,
|
||||
ds.mac_command_error_count
|
||||
@ -115,7 +123,8 @@ pub fn uplink_adr_history(dev_eui: EUI64, uh: Vec<internal::UplinkAdrHistory>) -
|
||||
let dev_eui = dev_eui.clone();
|
||||
let uh = uh.clone();
|
||||
Box::pin(async move {
|
||||
let ds = device_session::get(&dev_eui).await.unwrap();
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds = d.get_device_session().unwrap();
|
||||
assert_eq!(uh, ds.uplink_adr_history);
|
||||
})
|
||||
})
|
||||
@ -240,8 +249,9 @@ pub fn device_session(dev_eui: EUI64, ds: internal::DeviceSession) -> Validator
|
||||
Box::new(move || {
|
||||
let ds = ds.clone();
|
||||
Box::pin(async move {
|
||||
let ds_get = device_session::get(&dev_eui).await.unwrap();
|
||||
assert_eq!(ds, ds_get);
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
let ds_get = d.get_device_session().unwrap();
|
||||
assert_eq!(&ds, ds_get);
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -249,8 +259,8 @@ pub fn device_session(dev_eui: EUI64, ds: internal::DeviceSession) -> Validator
|
||||
pub fn no_device_session(dev_eui: EUI64) -> Validator {
|
||||
Box::new(move || {
|
||||
Box::pin(async move {
|
||||
let res = device_session::get(&dev_eui).await;
|
||||
assert_eq!(true, res.is_err());
|
||||
let d = device::get(&dev_eui).await.unwrap();
|
||||
assert!(d.device_session.is_none());
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use crate::gateway::backend as gateway_backend;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_profile, device_queue, device_session, gateway, tenant,
|
||||
device_profile, device_queue, gateway, tenant,
|
||||
};
|
||||
use crate::{config, test, uplink};
|
||||
use chirpstack_api::{common, gw, internal};
|
||||
@ -219,17 +219,41 @@ async fn test_sns_uplink() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut dev_addr = lrwn::DevAddr::from_be_bytes([0, 0, 0, 0]);
|
||||
dev_addr.set_dev_addr_prefix(lrwn::NetID::from_str("000505").unwrap().dev_addr_prefix());
|
||||
|
||||
let dev = device::create(device::Device {
|
||||
name: "device".into(),
|
||||
application_id: app.id.clone(),
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::B,
|
||||
dev_addr: Some(dev_addr),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
app_s_key: Some(common::KeyEnvelope {
|
||||
kek_label: "".into(),
|
||||
aes_key: vec![16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||
}),
|
||||
f_cnt_up: 8,
|
||||
n_f_cnt_down: 5,
|
||||
enabled_uplink_channel_indices: vec![0, 1, 2],
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let ds = dev.get_device_session().unwrap();
|
||||
|
||||
device_queue::enqueue_item(device_queue::DeviceQueueItem {
|
||||
dev_eui: dev.dev_eui,
|
||||
f_port: 10,
|
||||
@ -239,31 +263,6 @@ async fn test_sns_uplink() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut dev_addr = lrwn::DevAddr::from_be_bytes([0, 0, 0, 0]);
|
||||
dev_addr.set_dev_addr_prefix(lrwn::NetID::from_str("000505").unwrap().dev_addr_prefix());
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
app_s_key: Some(common::KeyEnvelope {
|
||||
kek_label: "".into(),
|
||||
aes_key: vec![16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||
}),
|
||||
f_cnt_up: 8,
|
||||
n_f_cnt_down: 5,
|
||||
enabled_uplink_channel_indices: vec![0, 1, 2],
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
};
|
||||
device_session::save(&ds).await.unwrap();
|
||||
|
||||
let mut data_phy = lrwn::PhyPayload {
|
||||
mhdr: lrwn::MHDR {
|
||||
m_type: lrwn::MType::UnconfirmedDataUp,
|
||||
@ -346,7 +345,7 @@ async fn test_sns_uplink() {
|
||||
},
|
||||
phy_payload: hex::decode("600000000a8005000a54972baa8b983cd1").unwrap(),
|
||||
dl_meta_data: Some(backend::DLMetaData {
|
||||
dev_eui: ds.dev_eui.clone(),
|
||||
dev_eui: dev.dev_eui.to_vec(),
|
||||
dl_freq_1: Some(868.1),
|
||||
dl_freq_2: Some(869.525),
|
||||
rx_delay_1: Some(1),
|
||||
@ -403,7 +402,7 @@ async fn test_sns_uplink() {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
dev_eui: ds.dev_eui.clone(),
|
||||
dev_eui: dev.dev_eui.to_vec(),
|
||||
nwk_s_key: Some(backend::KeyEnvelope {
|
||||
kek_label: "".to_string(),
|
||||
aes_key: ds.nwk_s_enc_key.clone(),
|
||||
@ -466,41 +465,40 @@ async fn test_sns_roaming_not_allowed() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _dev = device::create(device::Device {
|
||||
let mut dev_addr = lrwn::DevAddr::from_be_bytes([0, 0, 0, 0]);
|
||||
dev_addr.set_dev_addr_prefix(lrwn::NetID::from_str("000505").unwrap().dev_addr_prefix());
|
||||
|
||||
let dev = device::create(device::Device {
|
||||
name: "device".into(),
|
||||
application_id: app.id.clone(),
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::B,
|
||||
dev_addr: Some(dev_addr),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
app_s_key: Some(common::KeyEnvelope {
|
||||
kek_label: "".into(),
|
||||
aes_key: vec![16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||
}),
|
||||
f_cnt_up: 8,
|
||||
n_f_cnt_down: 5,
|
||||
enabled_uplink_channel_indices: vec![0, 1, 2],
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut dev_addr = lrwn::DevAddr::from_be_bytes([0, 0, 0, 0]);
|
||||
dev_addr.set_dev_addr_prefix(lrwn::NetID::from_str("000505").unwrap().dev_addr_prefix());
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: dev_addr.to_vec(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
app_s_key: Some(common::KeyEnvelope {
|
||||
kek_label: "".into(),
|
||||
aes_key: vec![16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||
}),
|
||||
f_cnt_up: 8,
|
||||
n_f_cnt_down: 5,
|
||||
enabled_uplink_channel_indices: vec![0, 1, 2],
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
};
|
||||
device_session::save(&ds).await.unwrap();
|
||||
let ds = dev.get_device_session().unwrap();
|
||||
|
||||
let mut data_phy = lrwn::PhyPayload {
|
||||
mhdr: lrwn::MHDR {
|
||||
|
@ -8,16 +8,17 @@ use super::assert;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_profile, device_queue, device_session, gateway, mac_command, reset_redis, tenant,
|
||||
device_profile, device_queue, gateway, mac_command, reset_redis, tenant,
|
||||
};
|
||||
use crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink};
|
||||
use chirpstack_api::{common, gw, integration as integration_pb, internal, stream};
|
||||
use lrwn::{AES128Key, EUI64};
|
||||
use lrwn::{AES128Key, DevAddr, EUI64};
|
||||
|
||||
type Function = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
dev_eui: EUI64,
|
||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||
before_func: Option<Function>,
|
||||
after_func: Option<Function>,
|
||||
@ -93,11 +94,28 @@ async fn test_gateway_filtering() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::B,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
f_cnt_up: 7,
|
||||
n_f_cnt_down: 5,
|
||||
enabled_uplink_channel_indices: vec![0, 1, 2],
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let ds = dev.get_device_session().unwrap();
|
||||
|
||||
let mut rx_info_a = gw::UplinkRxInfo {
|
||||
gateway_id: gw_a.gateway_id.to_string(),
|
||||
location: Some(Default::default()),
|
||||
@ -128,26 +146,10 @@ async fn test_gateway_filtering() {
|
||||
};
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
f_cnt_up: 7,
|
||||
n_f_cnt_down: 5,
|
||||
enabled_uplink_channel_indices: vec![0, 1, 2],
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "private gateway of same tenant".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -174,6 +176,7 @@ async fn test_gateway_filtering() {
|
||||
},
|
||||
Test {
|
||||
name: "private gateway other tenant".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -196,7 +199,7 @@ async fn test_gateway_filtering() {
|
||||
}),
|
||||
mic: Some([48, 94, 26, 239]),
|
||||
},
|
||||
assert: vec![assert::f_cnt_up(dev.dev_eui.clone(), 7)],
|
||||
assert: vec![assert::f_cnt_up(dev.dev_eui, 7)],
|
||||
},
|
||||
];
|
||||
|
||||
@ -257,6 +260,7 @@ async fn test_region_config_id_filtering() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -293,9 +297,7 @@ async fn test_region_config_id_filtering() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -312,6 +314,7 @@ async fn test_region_config_id_filtering() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "matching config id".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -338,6 +341,7 @@ async fn test_region_config_id_filtering() {
|
||||
},
|
||||
Test {
|
||||
name: "non-matching configuration id".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -410,12 +414,13 @@ async fn test_lorawan_10_errors() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _dev = device::create(device::Device {
|
||||
let dev = device::create(device::Device {
|
||||
name: "device".into(),
|
||||
application_id: app.id.clone(),
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -440,9 +445,7 @@ async fn test_lorawan_10_errors() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -459,6 +462,7 @@ async fn test_lorawan_10_errors() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "invalid frame-counter (did not increment)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -491,6 +495,7 @@ async fn test_lorawan_10_errors() {
|
||||
},
|
||||
Test {
|
||||
name: "invalid frame-counter (reset)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -522,6 +527,7 @@ async fn test_lorawan_10_errors() {
|
||||
},
|
||||
Test {
|
||||
name: "invalid mic".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -605,12 +611,13 @@ async fn test_lorawan_11_errors() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _dev = device::create(device::Device {
|
||||
let dev = device::create(device::Device {
|
||||
name: "device".into(),
|
||||
application_id: app.id.clone(),
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -641,9 +648,7 @@ async fn test_lorawan_11_errors() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_dr, 3).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -660,6 +665,7 @@ async fn test_lorawan_11_errors() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "invalid frequency (MIC)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -686,6 +692,7 @@ async fn test_lorawan_11_errors() {
|
||||
},
|
||||
Test {
|
||||
name: "invalid frequency (MIC)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -765,6 +772,7 @@ async fn test_lorawan_10_skip_f_cnt() {
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
skip_fcnt_check: true,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -789,9 +797,7 @@ async fn test_lorawan_10_skip_f_cnt() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -809,6 +815,7 @@ async fn test_lorawan_10_skip_f_cnt() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "frame-counter is invalid but not 0".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -857,6 +864,7 @@ async fn test_lorawan_10_skip_f_cnt() {
|
||||
},
|
||||
Test {
|
||||
name: "frame-counter is invalid and 0".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -958,6 +966,7 @@ async fn test_lorawan_10_device_disabled() {
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
is_disabled: true,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -982,9 +991,7 @@ async fn test_lorawan_10_device_disabled() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -1000,6 +1007,7 @@ async fn test_lorawan_10_device_disabled() {
|
||||
|
||||
let tests = vec![Test {
|
||||
name: "uplink ignored".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1081,6 +1089,7 @@ async fn test_lorawan_10_uplink() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -1111,9 +1120,7 @@ async fn test_lorawan_10_uplink() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 10).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -1134,6 +1141,7 @@ async fn test_lorawan_10_uplink() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "unconfirmed uplink with payload".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1184,6 +1192,7 @@ async fn test_lorawan_10_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink with payload using LR-FHSS dr".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
Box::pin(async move {
|
||||
@ -1255,6 +1264,7 @@ async fn test_lorawan_10_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink with payload + ACK".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -1333,6 +1343,7 @@ async fn test_lorawan_10_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink without payload (just FPort)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1383,6 +1394,7 @@ async fn test_lorawan_10_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "confirmed uplink with payload".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1494,6 +1506,7 @@ async fn test_lorawan_10_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "confirmed uplink without payload".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1604,21 +1617,34 @@ async fn test_lorawan_10_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "uplink of class-c device updates scheduler_run_after".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui;
|
||||
Box::pin(async move {
|
||||
device::set_enabled_class(&dev_eui, device::DeviceClass::C)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
enabled_class: Some(device::DeviceClass::C),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
})),
|
||||
after_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui;
|
||||
Box::pin(async move {
|
||||
device::set_enabled_class(&dev_eui, device::DeviceClass::A)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
enabled_class: Some(device::DeviceClass::A),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
})),
|
||||
device_session: Some(ds.clone()),
|
||||
@ -1700,6 +1726,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -1724,9 +1751,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds_sess_key_id = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -1743,9 +1768,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
||||
};
|
||||
|
||||
let ds_app_s_key = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -1766,6 +1789,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "end-to-end encryption with session key id".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1816,6 +1840,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
||||
},
|
||||
Test {
|
||||
name: "end-to-end encryption with AppSKey".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -1869,6 +1894,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
||||
},
|
||||
Test {
|
||||
name: "end-to-end encryption using AppSkey + encrypted downlink".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -2028,6 +2054,7 @@ async fn test_lorawan_11_uplink() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -2058,9 +2085,7 @@ async fn test_lorawan_11_uplink() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan110.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -2082,6 +2107,7 @@ async fn test_lorawan_11_uplink() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "unconfirmed uplink with payload".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -2132,6 +2158,7 @@ async fn test_lorawan_11_uplink() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink with payload + ACK".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -2266,6 +2293,7 @@ async fn test_lorawan_10_rx_delay() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -2296,9 +2324,7 @@ async fn test_lorawan_10_rx_delay() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -2318,6 +2344,7 @@ async fn test_lorawan_10_rx_delay() {
|
||||
|
||||
let tests = vec![Test {
|
||||
name: "confirmed uplink without payload (rx_delay = 3)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -2479,6 +2506,7 @@ async fn test_lorawan_10_mac_commands() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -2509,9 +2537,7 @@ async fn test_lorawan_10_mac_commands() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -2532,6 +2558,7 @@ async fn test_lorawan_10_mac_commands() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "unconfirmed uplink + device-status request downlink (FOpts)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dp_id = dp.id.clone();
|
||||
@ -2624,6 +2651,7 @@ async fn test_lorawan_10_mac_commands() {
|
||||
Test {
|
||||
name: "unconfirmed uplink + device-status request downlink (FOpts) + downlink payload"
|
||||
.into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -2721,6 +2749,7 @@ async fn test_lorawan_10_mac_commands() {
|
||||
},
|
||||
Test {
|
||||
name: "RxTimingSetupAns is answered with an empty downlink".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -2848,6 +2877,7 @@ async fn test_lorawan_11_mac_commands() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -2872,9 +2902,7 @@ async fn test_lorawan_11_mac_commands() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan110.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -2928,6 +2956,7 @@ async fn test_lorawan_11_mac_commands() {
|
||||
|
||||
let tests = vec![Test {
|
||||
name: "uplink mac-command (encrypted fopts)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -3042,6 +3071,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -3066,9 +3096,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -3089,6 +3117,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "unconfirmed uplink + one unconfirmed downlink payload in queue".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3166,6 +3195,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink + two unconfirmed downlinks payload in queue".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![
|
||||
device_queue::DeviceQueueItem {
|
||||
id: Uuid::new_v4(),
|
||||
@ -3256,6 +3286,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink + one confirmed downlink payload in queue".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3334,6 +3365,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink data + downlink payload which exceeds the max payload size (for dr 0)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3370,6 +3402,7 @@ async fn test_lorawan_10_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink data + one unconfirmed downlink payload in queue (exactly max size for dr 0) + one mac command".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3515,6 +3548,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -3539,9 +3573,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan110.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -3563,6 +3595,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "unconfirmed uplink + one unconfirmed downlink payload in queue".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3640,6 +3673,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink + two unconfirmed downlinks payload in queue".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![
|
||||
device_queue::DeviceQueueItem {
|
||||
id: Uuid::new_v4(),
|
||||
@ -3730,6 +3764,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink + one confirmed downlink payload in queue".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3808,6 +3843,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink data + downlink payload which exceeds the max payload size (for dr 0)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3844,6 +3880,7 @@ async fn test_lorawan_11_device_queue() {
|
||||
},
|
||||
Test {
|
||||
name: "unconfirmed uplink data + one unconfirmed downlink payload in queue (exactly max size for dr 0) + one mac command".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -3992,6 +4029,7 @@ async fn test_lorawan_10_adr() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4016,9 +4054,7 @@ async fn test_lorawan_10_adr() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -4057,6 +4093,7 @@ async fn test_lorawan_10_adr() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "adr triggered".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4160,6 +4197,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "device has adr disabled".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4193,6 +4231,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "acknowledgement of pending adr request".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
@ -4260,6 +4299,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "negative acknowledgement of pending adr request".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
@ -4327,6 +4367,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "adr ack requested".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4403,6 +4444,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "channel re-configuration triggered".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4502,6 +4544,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "new channel re-configuration ack-ed".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
@ -4567,6 +4610,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "new channel re-configuration not ack-ed".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
@ -4634,6 +4678,7 @@ async fn test_lorawan_10_adr() {
|
||||
},
|
||||
Test {
|
||||
name: "channel re-configuration and adr triggered".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4738,6 +4783,7 @@ async fn test_lorawan_10_adr() {
|
||||
// adr backoff triggered
|
||||
Test {
|
||||
name: "adr backoff triggered".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4835,6 +4881,7 @@ async fn test_lorawan_10_device_status_request() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4859,9 +4906,7 @@ async fn test_lorawan_10_device_status_request() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -4887,6 +4932,7 @@ async fn test_lorawan_10_device_status_request() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "must request device-status".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4963,6 +5009,7 @@ async fn test_lorawan_10_device_status_request() {
|
||||
},
|
||||
Test {
|
||||
name: "interval has not yet expired".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -4993,6 +5040,7 @@ async fn test_lorawan_10_device_status_request() {
|
||||
// reporting device-status
|
||||
Test {
|
||||
name: "reporting device-status".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
@ -5098,6 +5146,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -5128,9 +5177,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan110.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -5155,6 +5202,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
|
||||
run_test(&Test {
|
||||
name: "unconfirmed uplink with payload (rx1)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -5224,6 +5272,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
|
||||
run_test(&Test {
|
||||
name: "unconfirmed uplink with payload (rx2)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -5293,6 +5342,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
|
||||
run_test(&Test {
|
||||
name: "unconfirmed uplink with payload (rx1 + rx2)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -5391,6 +5441,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
|
||||
run_test(&Test {
|
||||
name: "unconfirmed uplink with payload (rx1, payload exceeds rx2 limit)".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -5497,7 +5548,6 @@ async fn test_lorawan_11_receive_window_selection() {
|
||||
|
||||
async fn run_test(t: &Test) {
|
||||
println!("> {}", t.name);
|
||||
|
||||
reset_redis().await.unwrap();
|
||||
|
||||
integration::set_mock().await;
|
||||
@ -5506,12 +5556,16 @@ async fn run_test(t: &Test) {
|
||||
integration::mock::reset().await;
|
||||
gateway_backend::mock::reset().await;
|
||||
|
||||
if let Some(ds) = &t.device_session {
|
||||
let _ = device_session::save(&ds).await.unwrap();
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
||||
}
|
||||
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||
device::partial_update(
|
||||
t.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(t.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(f) = &t.before_func {
|
||||
f().await;
|
||||
|
@ -5,7 +5,7 @@ use crate::gpstime::ToGpsTime;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_gateway, device_profile, device_queue, device_session, gateway, reset_redis, tenant,
|
||||
device_gateway, device_profile, device_queue, gateway, reset_redis, tenant,
|
||||
};
|
||||
use crate::{
|
||||
config, downlink, downlink::classb, gateway::backend as gateway_backend, integration, test,
|
||||
@ -16,6 +16,7 @@ use lrwn::{DevAddr, EUI64};
|
||||
|
||||
struct UplinkTest {
|
||||
name: String,
|
||||
dev_eui: EUI64,
|
||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
tx_info: gw::UplinkTxInfo,
|
||||
@ -26,6 +27,7 @@ struct UplinkTest {
|
||||
|
||||
struct DownlinkTest {
|
||||
name: String,
|
||||
dev_eui: EUI64,
|
||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
||||
@ -80,6 +82,7 @@ async fn test_uplink() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -103,9 +106,7 @@ async fn test_uplink() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -130,6 +131,7 @@ async fn test_uplink() {
|
||||
// trigger beacon locked
|
||||
run_uplink_test(&UplinkTest {
|
||||
name: "trigger beacon locked".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
device_session: Some(ds.clone()),
|
||||
tx_info: tx_info.clone(),
|
||||
@ -164,6 +166,7 @@ async fn test_uplink() {
|
||||
// trigger beacon unlocked
|
||||
run_uplink_test(&UplinkTest {
|
||||
name: "trigger beacon locked".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
device_session: Some(ds.clone()),
|
||||
tx_info: tx_info.clone(),
|
||||
@ -244,15 +247,14 @@ async fn test_downlink_scheduler() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::B,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -291,6 +293,7 @@ async fn test_downlink_scheduler() {
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "class-b downlink".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -342,6 +345,7 @@ async fn test_downlink_scheduler() {
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "scheduler_run_after has not yet expired".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -356,12 +360,19 @@ async fn test_downlink_scheduler() {
|
||||
.await;
|
||||
|
||||
// remove the schedule run after
|
||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
scheduler_run_after: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "class-b downlink with more data".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![
|
||||
device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
@ -434,12 +445,16 @@ async fn run_uplink_test(t: &UplinkTest) {
|
||||
integration::mock::reset().await;
|
||||
gateway_backend::mock::reset().await;
|
||||
|
||||
if let Some(ds) = &t.device_session {
|
||||
let _ = device_session::save(&ds).await.unwrap();
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
||||
}
|
||||
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||
device::partial_update(
|
||||
t.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(t.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for qi in &t.device_queue_items {
|
||||
let _ = device_queue::enqueue_item(qi.clone()).await.unwrap();
|
||||
@ -471,13 +486,16 @@ async fn run_scheduler_test(t: &DownlinkTest) {
|
||||
|
||||
integration::mock::reset().await;
|
||||
gateway_backend::mock::reset().await;
|
||||
|
||||
if let Some(ds) = &t.device_session {
|
||||
let _ = device_session::save(&ds).await.unwrap();
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
||||
}
|
||||
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||
device::partial_update(
|
||||
t.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(t.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(rx_info) = &t.device_gateway_rx_info {
|
||||
let _ = device_gateway::save_rx_info(rx_info).await.unwrap();
|
||||
|
@ -4,14 +4,15 @@ use super::assert;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_gateway, device_profile, device_queue, device_session, gateway, reset_redis, tenant,
|
||||
device_gateway, device_profile, device_queue, gateway, reset_redis, tenant,
|
||||
};
|
||||
use crate::{downlink, gateway::backend as gateway_backend, integration, test};
|
||||
use chirpstack_api::{common, gw, internal};
|
||||
use lrwn::EUI64;
|
||||
use lrwn::{DevAddr, EUI64};
|
||||
|
||||
struct DownlinkTest {
|
||||
name: String,
|
||||
dev_eui: EUI64,
|
||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
||||
@ -71,6 +72,7 @@ async fn test_downlink_scheduler() {
|
||||
device_profile_id: dp.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||
enabled_class: DeviceClass::C,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -86,10 +88,8 @@ async fn test_downlink_scheduler() {
|
||||
};
|
||||
|
||||
let ds = internal::DeviceSession {
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -112,6 +112,7 @@ async fn test_downlink_scheduler() {
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "device has not yet sent an uplink".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -126,12 +127,19 @@ async fn test_downlink_scheduler() {
|
||||
.await;
|
||||
|
||||
// remove the schedule run after
|
||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
scheduler_run_after: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "unconfirmed data".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -178,6 +186,7 @@ async fn test_downlink_scheduler() {
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "scheduler_run_after has not yet expired".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -192,12 +201,19 @@ async fn test_downlink_scheduler() {
|
||||
.await;
|
||||
|
||||
// remove the schedule run after
|
||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
scheduler_run_after: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "unconfirmed data".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -246,12 +262,19 @@ async fn test_downlink_scheduler() {
|
||||
.await;
|
||||
|
||||
// remove the schedule run after
|
||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
scheduler_run_after: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
run_scheduler_test(&DownlinkTest {
|
||||
name: "unconfirmed data".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||
id: Uuid::nil(),
|
||||
dev_eui: dev.dev_eui.clone(),
|
||||
@ -276,13 +299,16 @@ async fn run_scheduler_test(t: &DownlinkTest) {
|
||||
|
||||
integration::mock::reset().await;
|
||||
gateway_backend::mock::reset().await;
|
||||
|
||||
if let Some(ds) = &t.device_session {
|
||||
let _ = device_session::save(&ds).await.unwrap();
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
||||
}
|
||||
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||
device::partial_update(
|
||||
t.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(t.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(rx_info) = &t.device_gateway_rx_info {
|
||||
let _ = device_gateway::save_rx_info(rx_info).await.unwrap();
|
||||
|
@ -156,8 +156,6 @@ async fn test_js() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan103.into(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8],
|
||||
@ -225,8 +223,6 @@ async fn test_js() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan103.into(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8],
|
||||
@ -297,8 +293,6 @@ async fn test_js() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan103.into(),
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8],
|
||||
|
@ -8,7 +8,7 @@ use super::assert;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_keys, device_profile, gateway, reset_redis, tenant,
|
||||
device_keys, device_profile, gateway, tenant,
|
||||
};
|
||||
use crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink};
|
||||
use chirpstack_api::{common, gw, internal, stream};
|
||||
@ -19,6 +19,7 @@ type Function = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
dev_eui: EUI64,
|
||||
before_func: Option<Function>,
|
||||
after_func: Option<Function>,
|
||||
tx_info: gw::UplinkTxInfo,
|
||||
@ -153,6 +154,7 @@ async fn test_gateway_filtering() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "private gateway of same tenant".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -168,8 +170,6 @@ async fn test_gateway_filtering() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
f_nwk_s_int_key: vec![
|
||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
||||
@ -198,6 +198,7 @@ async fn test_gateway_filtering() {
|
||||
},
|
||||
Test {
|
||||
name: "private gateway other tenant".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -375,6 +376,7 @@ async fn test_lorawan_10() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "dev-nonce already used".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
rx_info: rx_info.clone(),
|
||||
@ -387,6 +389,7 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -407,8 +410,6 @@ async fn test_lorawan_10() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
f_nwk_s_int_key: vec![
|
||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
||||
@ -576,6 +577,7 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted + skip fcnt check".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -602,8 +604,6 @@ async fn test_lorawan_10() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
f_nwk_s_int_key: vec![
|
||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
||||
@ -633,6 +633,7 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted + cflist".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -649,8 +650,6 @@ async fn test_lorawan_10() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
f_nwk_s_int_key: vec![
|
||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
||||
@ -786,6 +785,7 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted + class-b supported".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
let dp_id = dp.id.clone();
|
||||
@ -813,6 +813,7 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted + class-c supported".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
let dp_id = dp.id.clone();
|
||||
@ -840,6 +841,7 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "device disabled".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -997,6 +999,7 @@ async fn test_lorawan_11() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "dev-nonce already used".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
rx_info: rx_info.clone(),
|
||||
@ -1009,6 +1012,7 @@ async fn test_lorawan_11() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
Box::pin(async move {
|
||||
@ -1025,8 +1029,6 @@ async fn test_lorawan_11() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan110.into(),
|
||||
f_nwk_s_int_key: vec![
|
||||
98, 222, 198, 158, 98, 155, 205, 235, 143, 171, 203, 19, 221, 9, 1, 231,
|
||||
@ -1189,6 +1191,7 @@ async fn test_lorawan_11() {
|
||||
},
|
||||
Test {
|
||||
name: "join-request accepted + class-c supported".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui.clone();
|
||||
let dp_id = dp.id.clone();
|
||||
@ -1224,8 +1227,6 @@ async fn test_lorawan_11() {
|
||||
async fn run_test(t: &Test) {
|
||||
println!("> {}", t.name);
|
||||
|
||||
reset_redis().await.unwrap();
|
||||
|
||||
let mut conf: config::Configuration = (*config::get()).clone();
|
||||
for f in &t.extra_uplink_channels {
|
||||
conf.regions[0]
|
||||
@ -1246,6 +1247,17 @@ async fn run_test(t: &Test) {
|
||||
integration::mock::reset().await;
|
||||
gateway_backend::mock::reset().await;
|
||||
|
||||
device::partial_update(
|
||||
t.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
dev_addr: Some(None),
|
||||
device_session: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(f) = &t.before_func {
|
||||
f().await;
|
||||
}
|
||||
|
@ -6,14 +6,16 @@ use super::assert;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_profile, device_queue, device_session, gateway, reset_redis, tenant,
|
||||
device_profile, device_queue, gateway, reset_redis, tenant,
|
||||
};
|
||||
use crate::{gateway::backend as gateway_backend, integration, test, uplink};
|
||||
use chirpstack_api::{common, gw, integration as integration_pb, internal};
|
||||
use lrwn::{AES128Key, EUI64};
|
||||
use lrwn::{AES128Key, DevAddr, EUI64};
|
||||
|
||||
struct Test {
|
||||
name: String,
|
||||
dev_eui_relay: EUI64,
|
||||
dev_eui_relay_ed: EUI64,
|
||||
device_queue_items_relay_ed: Vec<device_queue::DeviceQueueItem>,
|
||||
device_session_relay: Option<internal::DeviceSession>,
|
||||
device_session_relay_ed: Option<internal::DeviceSession>,
|
||||
@ -85,6 +87,7 @@ async fn test_lorawan_10() {
|
||||
device_profile_id: dp_relay.id,
|
||||
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([1, 1, 1, 1])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -96,6 +99,7 @@ async fn test_lorawan_10() {
|
||||
device_profile_id: dp_relay_ed.id,
|
||||
dev_eui: EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([2, 2, 2, 2])),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -120,7 +124,6 @@ async fn test_lorawan_10() {
|
||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 5).unwrap();
|
||||
|
||||
let ds_relay = internal::DeviceSession {
|
||||
dev_eui: dev_relay.dev_eui.to_vec(),
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
dev_addr: vec![1, 1, 1, 1],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -140,7 +143,6 @@ async fn test_lorawan_10() {
|
||||
};
|
||||
|
||||
let ds_relay_ed = internal::DeviceSession {
|
||||
dev_eui: dev_relay_ed.dev_eui.to_vec(),
|
||||
mac_version: common::MacVersion::Lorawan104.into(),
|
||||
dev_addr: vec![2, 2, 2, 2],
|
||||
f_nwk_s_int_key: vec![2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
@ -439,6 +441,8 @@ async fn test_lorawan_10() {
|
||||
let tests = vec![
|
||||
Test {
|
||||
name: "relayed unconfirmed uplink".into(),
|
||||
dev_eui_relay: dev_relay.dev_eui,
|
||||
dev_eui_relay_ed: dev_relay_ed.dev_eui,
|
||||
device_queue_items_relay_ed: vec![],
|
||||
device_session_relay: Some(ds_relay.clone()),
|
||||
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
||||
@ -503,6 +507,8 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "relayed confirmed uplink".into(),
|
||||
dev_eui_relay: dev_relay.dev_eui,
|
||||
dev_eui_relay_ed: dev_relay_ed.dev_eui,
|
||||
device_queue_items_relay_ed: vec![],
|
||||
device_session_relay: Some(ds_relay.clone()),
|
||||
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
||||
@ -627,6 +633,8 @@ async fn test_lorawan_10() {
|
||||
},
|
||||
Test {
|
||||
name: "relayed unconfirmed uplink + adr_ack_req".into(),
|
||||
dev_eui_relay: dev_relay.dev_eui,
|
||||
dev_eui_relay_ed: dev_relay_ed.dev_eui,
|
||||
device_queue_items_relay_ed: vec![],
|
||||
device_session_relay: Some(ds_relay.clone()),
|
||||
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
||||
@ -765,20 +773,30 @@ async fn run_test(t: &Test) {
|
||||
|
||||
integration::mock::reset().await;
|
||||
gateway_backend::mock::reset().await;
|
||||
|
||||
if let Some(ds) = &t.device_session_relay {
|
||||
let _ = device_session::save(&ds).await.unwrap();
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
||||
}
|
||||
|
||||
if let Some(ds) = &t.device_session_relay_ed {
|
||||
let _ = device_session::save(&ds).await.unwrap();
|
||||
|
||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
||||
}
|
||||
device_queue::flush_for_dev_eui(&t.dev_eui_relay)
|
||||
.await
|
||||
.unwrap();
|
||||
device_queue::flush_for_dev_eui(&t.dev_eui_relay_ed)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
t.dev_eui_relay,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(t.device_session_relay.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
device::partial_update(
|
||||
t.dev_eui_relay_ed,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(t.device_session_relay_ed.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for qi in &t.device_queue_items_relay_ed {
|
||||
let _ = device_queue::enqueue_item(qi.clone()).await.unwrap();
|
||||
|
@ -6,11 +6,11 @@ use super::assert;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_keys, device_profile, device_session, gateway, tenant,
|
||||
device_keys, device_profile, gateway, tenant,
|
||||
};
|
||||
use crate::{gateway::backend as gateway_backend, integration, test, uplink};
|
||||
use chirpstack_api::{common, gw, internal};
|
||||
use lrwn::{AES128Key, EUI64};
|
||||
use lrwn::{AES128Key, DevAddr, EUI64};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_lorawan_10() {
|
||||
@ -96,26 +96,26 @@ async fn test_lorawan_10() {
|
||||
device_profile_id: dp_relay.id.clone(),
|
||||
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 2]),
|
||||
enabled_class: DeviceClass::A,
|
||||
dev_addr: Some(DevAddr::from_be_bytes([4, 3, 2, 1])),
|
||||
device_session: Some(internal::DeviceSession {
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
dev_addr: vec![4, 3, 2, 1],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
f_cnt_up: 10,
|
||||
n_f_cnt_down: 5,
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let ds_relay = internal::DeviceSession {
|
||||
dev_eui: dev_relay.dev_eui.to_vec(),
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
dev_addr: vec![4, 3, 2, 1],
|
||||
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
f_cnt_up: 10,
|
||||
n_f_cnt_down: 5,
|
||||
rx1_delay: 1,
|
||||
rx2_frequency: 869525000,
|
||||
region_config_id: "eu868".into(),
|
||||
..Default::default()
|
||||
};
|
||||
device_session::save(&ds_relay).await.unwrap();
|
||||
let ds_relay = dev_relay.get_device_session().unwrap();
|
||||
|
||||
let mut rx_info = gw::UplinkRxInfo {
|
||||
gateway_id: gw.gateway_id.to_string(),
|
||||
@ -268,8 +268,6 @@ async fn test_lorawan_10() {
|
||||
dev.dev_eui.clone(),
|
||||
internal::DeviceSession {
|
||||
dev_addr: vec![1, 2, 3, 4],
|
||||
dev_eui: vec![1, 1, 1, 1, 1, 1, 1, 1],
|
||||
join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
mac_version: common::MacVersion::Lorawan102.into(),
|
||||
f_nwk_s_int_key: vec![
|
||||
146, 184, 94, 251, 180, 89, 48, 96, 236, 112, 106, 181, 94, 25, 215, 162,
|
||||
|
@ -17,7 +17,7 @@ use crate::storage::error::Error as StorageError;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_gateway, device_profile, device_queue, device_session, fields,
|
||||
device_gateway, device_profile, device_queue, fields,
|
||||
helpers::get_all_device_data,
|
||||
metrics, tenant,
|
||||
};
|
||||
@ -40,7 +40,6 @@ pub struct Data {
|
||||
retransmission: bool,
|
||||
f_cnt_up_full: u32,
|
||||
tenant: Option<tenant::Tenant>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
device: Option<device::Device>,
|
||||
device_profile: Option<device_profile::DeviceProfile>,
|
||||
application: Option<application::Application>,
|
||||
@ -50,6 +49,7 @@ pub struct Data {
|
||||
must_send_downlink: bool,
|
||||
downlink_mac_commands: Vec<lrwn::MACCommandSet>,
|
||||
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
||||
device_changeset: device::DeviceChangeset,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
@ -99,7 +99,6 @@ impl Data {
|
||||
reset: false,
|
||||
retransmission: false,
|
||||
tenant: None,
|
||||
device_session: None,
|
||||
device: None,
|
||||
device_profile: None,
|
||||
application: None,
|
||||
@ -109,10 +108,11 @@ impl Data {
|
||||
must_send_downlink: false,
|
||||
downlink_mac_commands: Vec::new(),
|
||||
device_gateway_rx_info: None,
|
||||
device_changeset: Default::default(),
|
||||
};
|
||||
|
||||
ctx.handle_passive_roaming_device().await?;
|
||||
ctx.get_device_session().await?;
|
||||
ctx.get_device_for_phy_payload().await?;
|
||||
ctx.get_device_data().await?;
|
||||
ctx.check_roaming_allowed()?;
|
||||
|
||||
@ -120,17 +120,16 @@ impl Data {
|
||||
let span = tracing::Span::current();
|
||||
span.record("dev_eui", ctx.device.as_ref().unwrap().dev_eui.to_string());
|
||||
|
||||
ctx.abort_on_device_is_disabled().await?;
|
||||
ctx.set_device_info()?;
|
||||
ctx.set_device_gateway_rx_info()?;
|
||||
ctx.handle_retransmission_reset().await?;
|
||||
ctx.set_scheduler_run_after().await?;
|
||||
if !ctx._is_roaming() {
|
||||
// In case of roaming we do not know the gateways and therefore it must not be
|
||||
// filtered.
|
||||
ctx.filter_rx_info_by_tenant().await?;
|
||||
ctx.filter_rx_info_by_region_config_id()?;
|
||||
}
|
||||
ctx.set_device_info()?;
|
||||
ctx.set_device_gateway_rx_info()?;
|
||||
ctx.handle_retransmission_reset().await?;
|
||||
ctx.set_scheduler_run_after().await?;
|
||||
ctx.decrypt_f_opts_mac_commands()?;
|
||||
ctx.decrypt_frm_payload()?;
|
||||
ctx.log_uplink_frame_set().await?;
|
||||
@ -148,7 +147,7 @@ impl Data {
|
||||
ctx.detect_and_save_measurements().await?;
|
||||
ctx.sync_uplink_f_cnt()?;
|
||||
ctx.set_region_config_id()?;
|
||||
ctx.save_device_session().await?;
|
||||
ctx.update_device().await?;
|
||||
ctx.handle_uplink_ack().await?;
|
||||
ctx.save_metrics().await?;
|
||||
|
||||
@ -175,7 +174,6 @@ impl Data {
|
||||
reset: false,
|
||||
retransmission: false,
|
||||
tenant: None,
|
||||
device_session: None,
|
||||
device: None,
|
||||
device_profile: None,
|
||||
application: None,
|
||||
@ -184,11 +182,11 @@ impl Data {
|
||||
uplink_event: None,
|
||||
must_send_downlink: false,
|
||||
downlink_mac_commands: Vec::new(),
|
||||
device_changeset: Default::default(),
|
||||
};
|
||||
|
||||
ctx.get_device_session_relayed().await?;
|
||||
ctx.get_device_for_phy_payload_relayed().await?;
|
||||
ctx.get_device_data().await?;
|
||||
ctx.abort_on_device_is_disabled().await?;
|
||||
ctx.set_device_info()?;
|
||||
ctx.set_relay_rx_info()?;
|
||||
ctx.handle_retransmission_reset().await?;
|
||||
@ -204,7 +202,7 @@ impl Data {
|
||||
ctx.detect_and_save_measurements().await?;
|
||||
ctx.sync_uplink_f_cnt()?;
|
||||
ctx.set_region_config_id()?;
|
||||
ctx.save_device_session().await?;
|
||||
ctx.update_device().await?;
|
||||
ctx.handle_uplink_ack().await?;
|
||||
ctx.save_metrics_relayed().await?;
|
||||
ctx.start_downlink_data_flow_relayed().await?;
|
||||
@ -230,8 +228,8 @@ impl Data {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_session(&mut self) -> Result<(), Error> {
|
||||
trace!("Getting device-session for dev_addr");
|
||||
async fn get_device_for_phy_payload(&mut self) -> Result<(), Error> {
|
||||
trace!("Getting device for PhyPayload");
|
||||
|
||||
let dev_addr = if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||
pl.fhdr.devaddr
|
||||
@ -239,7 +237,7 @@ impl Data {
|
||||
return Err(Error::AnyhowError(anyhow!("No MacPayload in PhyPayload")));
|
||||
};
|
||||
|
||||
match device_session::get_for_phypayload_and_incr_f_cnt_up(
|
||||
match device::get_for_phypayload_and_incr_f_cnt_up(
|
||||
false,
|
||||
&mut self.phy_payload,
|
||||
self.uplink_frame_set.dr,
|
||||
@ -248,18 +246,18 @@ impl Data {
|
||||
.await
|
||||
{
|
||||
Ok(v) => match v {
|
||||
device_session::ValidationStatus::Ok(f_cnt, ds) => {
|
||||
self.device_session = Some(ds);
|
||||
device::ValidationStatus::Ok(f_cnt, d) => {
|
||||
self.device = Some(d);
|
||||
self.f_cnt_up_full = f_cnt;
|
||||
}
|
||||
device_session::ValidationStatus::Retransmission(f_cnt, ds) => {
|
||||
device::ValidationStatus::Retransmission(f_cnt, d) => {
|
||||
self.retransmission = true;
|
||||
self.device_session = Some(ds);
|
||||
self.device = Some(d);
|
||||
self.f_cnt_up_full = f_cnt;
|
||||
}
|
||||
device_session::ValidationStatus::Reset(f_cnt, ds) => {
|
||||
device::ValidationStatus::Reset(f_cnt, d) => {
|
||||
self.reset = true;
|
||||
self.device_session = Some(ds);
|
||||
self.device = Some(d);
|
||||
self.f_cnt_up_full = f_cnt;
|
||||
}
|
||||
},
|
||||
@ -289,8 +287,8 @@ impl Data {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_device_session_relayed(&mut self) -> Result<(), Error> {
|
||||
trace!("Getting device-session for dev_addr (relayed)");
|
||||
async fn get_device_for_phy_payload_relayed(&mut self) -> Result<(), Error> {
|
||||
trace!("Getting device for PhyPayload (relayed)");
|
||||
|
||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||
|
||||
@ -307,27 +305,22 @@ impl Data {
|
||||
dr,
|
||||
)? as u8;
|
||||
|
||||
match device_session::get_for_phypayload_and_incr_f_cnt_up(
|
||||
true,
|
||||
&mut self.phy_payload,
|
||||
dr,
|
||||
ch,
|
||||
)
|
||||
.await
|
||||
match device::get_for_phypayload_and_incr_f_cnt_up(true, &mut self.phy_payload, dr, ch)
|
||||
.await
|
||||
{
|
||||
Ok(v) => match v {
|
||||
device_session::ValidationStatus::Ok(f_cnt, ds) => {
|
||||
self.device_session = Some(ds);
|
||||
device::ValidationStatus::Ok(f_cnt, d) => {
|
||||
self.device = Some(d);
|
||||
self.f_cnt_up_full = f_cnt;
|
||||
}
|
||||
device_session::ValidationStatus::Retransmission(f_cnt, ds) => {
|
||||
device::ValidationStatus::Retransmission(f_cnt, d) => {
|
||||
self.retransmission = true;
|
||||
self.device_session = Some(ds);
|
||||
self.device = Some(d);
|
||||
self.f_cnt_up_full = f_cnt;
|
||||
}
|
||||
device_session::ValidationStatus::Reset(f_cnt, ds) => {
|
||||
device::ValidationStatus::Reset(f_cnt, d) => {
|
||||
self.reset = true;
|
||||
self.device_session = Some(ds);
|
||||
self.device = Some(d);
|
||||
self.f_cnt_up_full = f_cnt;
|
||||
}
|
||||
},
|
||||
@ -353,8 +346,9 @@ impl Data {
|
||||
|
||||
async fn get_device_data(&mut self) -> Result<()> {
|
||||
trace!("Getting device data");
|
||||
let dev_eui = lrwn::EUI64::from_slice(&self.device_session.as_ref().unwrap().dev_eui)?;
|
||||
let (dev, app, t, dp) = get_all_device_data(dev_eui).await?;
|
||||
|
||||
let dev_eui = self.device.as_ref().unwrap().dev_eui;
|
||||
let (_, app, t, dp) = get_all_device_data(dev_eui).await?;
|
||||
|
||||
if dp.region != self.uplink_frame_set.region_common_name {
|
||||
return Err(anyhow!("Invalid device-profile region"));
|
||||
@ -363,7 +357,6 @@ impl Data {
|
||||
self.tenant = Some(t);
|
||||
self.application = Some(app);
|
||||
self.device_profile = Some(dp);
|
||||
self.device = Some(dev);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -425,9 +418,10 @@ impl Data {
|
||||
|
||||
fn set_device_gateway_rx_info(&mut self) -> Result<()> {
|
||||
trace!("Setting gateway rx-info for device");
|
||||
let d = self.device.as_ref().unwrap();
|
||||
|
||||
self.device_gateway_rx_info = Some(internal::DeviceGatewayRxInfo {
|
||||
dev_eui: self.device_session.as_ref().unwrap().dev_eui.clone(),
|
||||
dev_eui: d.dev_eui.to_vec(),
|
||||
dr: self.uplink_frame_set.dr as u32,
|
||||
items: self
|
||||
.uplink_frame_set
|
||||
@ -469,24 +463,6 @@ impl Data {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn abort_on_device_is_disabled(&self) -> Result<(), Error> {
|
||||
let device = self.device.as_ref().unwrap();
|
||||
|
||||
if device.is_disabled {
|
||||
// Restore the device-session in case the device is disabled.
|
||||
// This is because during the fcnt validation, we immediately store the
|
||||
// device-session with incremented fcnt to avoid race conditions.
|
||||
device_session::save(self.device_session.as_ref().unwrap())
|
||||
.await
|
||||
.context("Savel device-session")?;
|
||||
|
||||
info!(dev_eui = %device.dev_eui, "Device is disabled, aborting flow");
|
||||
return Err(Error::Abort);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_retransmission_reset(&self) -> Result<(), Error> {
|
||||
trace!("Handle retransmission and reset");
|
||||
let dev = self.device.as_ref().unwrap();
|
||||
@ -555,8 +531,14 @@ impl Data {
|
||||
if dev.scheduler_run_after.is_none()
|
||||
|| scheduler_run_after > dev.scheduler_run_after.unwrap()
|
||||
{
|
||||
*dev = device::set_scheduler_run_after(&dev.dev_eui, Some(scheduler_run_after))
|
||||
.await?;
|
||||
*dev = device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
scheduler_run_after: Some(Some(scheduler_run_after)),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -575,9 +557,15 @@ impl Data {
|
||||
// Restore the device-session in case of an error (no gateways available).
|
||||
// This is because during the fcnt validation, we immediately store the
|
||||
// device-session with incremented fcnt to avoid race conditions.
|
||||
device_session::save(self.device_session.as_ref().unwrap())
|
||||
.await
|
||||
.context("Save device-session")?;
|
||||
let d = self.device.as_ref().unwrap();
|
||||
device::partial_update(
|
||||
d.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(d.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Err(v)
|
||||
}
|
||||
@ -597,7 +585,13 @@ impl Data {
|
||||
|
||||
fn decrypt_f_opts_mac_commands(&mut self) -> Result<()> {
|
||||
trace!("Decrypting mac-commands");
|
||||
let ds = self.device_session.as_ref().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
if ds.mac_version().to_string().starts_with("1.0") {
|
||||
if let Err(e) = self.phy_payload.decode_f_opts_to_mac_commands() {
|
||||
// This avoids failing in case of a corrupted mac-command in the frm_payload.
|
||||
@ -616,7 +610,13 @@ impl Data {
|
||||
|
||||
fn decrypt_frm_payload(&mut self) -> Result<()> {
|
||||
trace!("Decrypting FRMPayload");
|
||||
let ds = self.device_session.as_ref().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let mut f_port = 0;
|
||||
|
||||
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||
@ -656,7 +656,13 @@ impl Data {
|
||||
|
||||
fn set_adr(&mut self) -> Result<()> {
|
||||
trace!("Set ADR flag in device-session");
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||
ds.adr = pl.fhdr.f_ctrl.adr;
|
||||
}
|
||||
@ -666,9 +672,13 @@ impl Data {
|
||||
async fn set_uplink_data_rate(&mut self) -> Result<()> {
|
||||
trace!("Set uplink data-rate and reset tx-power on change");
|
||||
let device = self.device.as_mut().unwrap();
|
||||
*device = device::set_last_seen_dr(&device.dev_eui, self.uplink_frame_set.dr).await?;
|
||||
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
self.device_changeset.last_seen_at = Some(Some(Utc::now()));
|
||||
if device.dr.is_none() || self.uplink_frame_set.dr as i16 != device.dr.unwrap_or_default() {
|
||||
self.device_changeset.dr = Some(Some(self.uplink_frame_set.dr.into()));
|
||||
}
|
||||
|
||||
let ds = device.get_device_session_mut()?;
|
||||
// The node changed its data-rate. Possibly the node did also reset its
|
||||
// tx-power to max power. Because of this, we need to reset the tx-power
|
||||
// and the uplink history at the network-server side too.
|
||||
@ -677,6 +687,7 @@ impl Data {
|
||||
ds.uplink_adr_history = Vec::new();
|
||||
}
|
||||
ds.dr = self.uplink_frame_set.dr as u32;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -684,9 +695,13 @@ impl Data {
|
||||
trace!("Set relayed uplink data-rate and reset tx-power on change");
|
||||
let device = self.device.as_mut().unwrap();
|
||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||
*device = device::set_last_seen_dr(&device.dev_eui, self.uplink_frame_set.dr).await?;
|
||||
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
self.device_changeset.last_seen_at = Some(Some(Utc::now()));
|
||||
if device.dr.is_none() || self.uplink_frame_set.dr as i16 != device.dr.unwrap_or_default() {
|
||||
self.device_changeset.dr = Some(Some(self.uplink_frame_set.dr.into()));
|
||||
}
|
||||
|
||||
let ds = device.get_device_session_mut()?;
|
||||
// The node changed its data-rate. Possibly the node did also reset its
|
||||
// tx-power to max power. Because of this, we need to reset the tx-power
|
||||
// and the uplink history at the network-server side too.
|
||||
@ -717,7 +732,7 @@ impl Data {
|
||||
|
||||
// Update if the enabled class has changed.
|
||||
if dev.enabled_class != enabled_class {
|
||||
*dev = device::set_enabled_class(&dev.dev_eui, enabled_class).await?;
|
||||
self.device_changeset.enabled_class = Some(enabled_class);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -770,11 +785,12 @@ impl Data {
|
||||
// device did not reset these).
|
||||
fn reset_channels_on_adr_ack_req(&mut self) -> Result<()> {
|
||||
trace!("Reset channels on adr ack req");
|
||||
let d = self.device.as_mut().unwrap();
|
||||
|
||||
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||
if pl.fhdr.f_ctrl.adr_ack_req {
|
||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let ds = d.device_session.as_mut().unwrap();
|
||||
|
||||
// We reset the device-session enabled_uplink_channel_indices and
|
||||
// extra_uplink_channels. On the downlink path, the mac-command handling will
|
||||
@ -804,8 +820,7 @@ impl Data {
|
||||
self.tenant.as_ref().unwrap(),
|
||||
self.application.as_ref().unwrap(),
|
||||
self.device_profile.as_ref().unwrap(),
|
||||
self.device.as_ref().unwrap(),
|
||||
self.device_session.as_mut().unwrap(),
|
||||
self.device.as_mut().unwrap(),
|
||||
)
|
||||
.await
|
||||
.context("Handle uplink mac-commands")?;
|
||||
@ -822,8 +837,7 @@ impl Data {
|
||||
self.tenant.as_ref().unwrap(),
|
||||
self.application.as_ref().unwrap(),
|
||||
self.device_profile.as_ref().unwrap(),
|
||||
self.device.as_ref().unwrap(),
|
||||
self.device_session.as_mut().unwrap(),
|
||||
self.device.as_mut().unwrap(),
|
||||
)
|
||||
.await
|
||||
.context("Handle uplink mac-commands")?;
|
||||
@ -847,7 +861,13 @@ impl Data {
|
||||
}
|
||||
|
||||
fn append_meta_data_to_uplink_history(&mut self) -> Result<()> {
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
|
||||
// ignore re-transmissions we don't know the source of the
|
||||
// re-transmission (it might be a replay-attack)
|
||||
@ -892,7 +912,13 @@ impl Data {
|
||||
fn append_meta_data_to_uplink_history_relayed(&mut self) -> Result<()> {
|
||||
trace!("Apping meta-data of relayed uplink to upink history");
|
||||
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||
|
||||
// ignore re-transmissions we don't know the source of the
|
||||
@ -929,7 +955,7 @@ impl Data {
|
||||
let app = self.application.as_ref().unwrap();
|
||||
let dp = self.device_profile.as_ref().unwrap();
|
||||
let dev = self.device.as_ref().unwrap();
|
||||
let ds = self.device_session.as_ref().unwrap();
|
||||
let ds = dev.get_device_session()?;
|
||||
let mac = if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||
pl
|
||||
} else {
|
||||
@ -1092,7 +1118,8 @@ impl Data {
|
||||
// required.
|
||||
fn sync_uplink_f_cnt(&mut self) -> Result<()> {
|
||||
trace!("Syncing uplink frame-counter");
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
ds.f_cnt_up = self.f_cnt_up_full + 1;
|
||||
Ok(())
|
||||
}
|
||||
@ -1102,16 +1129,19 @@ impl Data {
|
||||
// value is not set initially.
|
||||
fn set_region_config_id(&mut self) -> Result<()> {
|
||||
trace!("Setting region_config_id to device-session");
|
||||
let ds = self.device_session.as_mut().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
let ds = d.get_device_session_mut()?;
|
||||
ds.region_config_id = self.uplink_frame_set.region_config_id.clone();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_device_session(&self) -> Result<()> {
|
||||
trace!("Saving device-session");
|
||||
device_session::save(self.device_session.as_ref().unwrap())
|
||||
.await
|
||||
.context("Save device-session")?;
|
||||
async fn update_device(&mut self) -> Result<()> {
|
||||
trace!("Updating device");
|
||||
|
||||
let d = self.device.as_mut().unwrap();
|
||||
self.device_changeset.device_session = Some(d.device_session.clone());
|
||||
|
||||
*d = device::partial_update(d.dev_eui, &self.device_changeset).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1265,7 +1295,6 @@ impl Data {
|
||||
self.application.as_ref().cloned().unwrap(),
|
||||
self.device_profile.as_ref().cloned().unwrap(),
|
||||
self.device.as_ref().cloned().unwrap(),
|
||||
self.device_session.as_ref().cloned().unwrap(),
|
||||
pl.fhdr.f_ctrl.adr_ack_req || self.must_send_downlink,
|
||||
self.phy_payload.mhdr.m_type == lrwn::MType::ConfirmedDataUp,
|
||||
self.downlink_mac_commands.clone(),
|
||||
@ -1291,7 +1320,6 @@ impl Data {
|
||||
self.application.as_ref().cloned().unwrap(),
|
||||
self.device_profile.as_ref().cloned().unwrap(),
|
||||
self.device.as_ref().cloned().unwrap(),
|
||||
self.device_session.as_ref().cloned().unwrap(),
|
||||
pl.fhdr.f_ctrl.adr_ack_req || self.must_send_downlink,
|
||||
self.phy_payload.mhdr.m_type == lrwn::MType::ConfirmedDataUp,
|
||||
self.downlink_mac_commands.clone(),
|
||||
@ -1314,7 +1342,6 @@ impl Data {
|
||||
req: pl.clone(),
|
||||
device: self.device.as_ref().unwrap().clone(),
|
||||
device_profile: self.device_profile.as_ref().unwrap().clone(),
|
||||
device_session: self.device_session.as_ref().unwrap().clone(),
|
||||
must_ack: self.phy_payload.mhdr.m_type
|
||||
== lrwn::MType::ConfirmedDataUp,
|
||||
must_send_downlink: relay_pl.fhdr.f_ctrl.adr_ack_req,
|
||||
@ -1329,7 +1356,6 @@ impl Data {
|
||||
req: pl.clone(),
|
||||
device: self.device.as_ref().unwrap().clone(),
|
||||
device_profile: self.device_profile.as_ref().unwrap().clone(),
|
||||
device_session: self.device_session.as_ref().unwrap().clone(),
|
||||
must_ack: self.phy_payload.mhdr.m_type
|
||||
== lrwn::MType::ConfirmedDataUp,
|
||||
must_send_downlink: relay_pl.fhdr.f_ctrl.adr_ack_req,
|
||||
@ -1369,7 +1395,13 @@ impl Data {
|
||||
}
|
||||
|
||||
fn _is_end_to_end_encrypted(&self) -> bool {
|
||||
let ds = self.device_session.as_ref().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
if !ds.js_session_key_id.is_empty() {
|
||||
return true;
|
||||
|
@ -6,8 +6,8 @@ use chrono::{DateTime, Local, Utc};
|
||||
use tracing::{error, info, span, trace, warn, Instrument, Level};
|
||||
|
||||
use lrwn::{
|
||||
keys, AES128Key, CFList, DLSettings, DevAddr, JoinAcceptPayload, JoinRequestPayload, JoinType,
|
||||
MType, Major, Payload, PhyPayload, MHDR,
|
||||
keys, AES128Key, CFList, DLSettings, JoinAcceptPayload, JoinRequestPayload, JoinType, MType,
|
||||
Major, Payload, PhyPayload, MHDR,
|
||||
};
|
||||
|
||||
use super::error::Error;
|
||||
@ -20,7 +20,6 @@ use super::{
|
||||
use crate::api::{backend::get_async_receiver, helpers::ToProto};
|
||||
use crate::backend::{joinserver, keywrap, roaming};
|
||||
use crate::helpers::errors::PrintFullError;
|
||||
use crate::storage::device_session;
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
@ -40,12 +39,10 @@ pub struct JoinRequest {
|
||||
join_request: Option<JoinRequestPayload>,
|
||||
join_accept: Option<PhyPayload>,
|
||||
device: Option<device::Device>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
application: Option<application::Application>,
|
||||
tenant: Option<tenant::Tenant>,
|
||||
device_profile: Option<device_profile::DeviceProfile>,
|
||||
device_keys: Option<device_keys::DeviceKeys>,
|
||||
dev_addr: Option<DevAddr>,
|
||||
device_info: Option<integration_pb::DeviceInfo>,
|
||||
relay_rx_info: Option<integration_pb::UplinkRelayRxInfo>,
|
||||
f_nwk_s_int_key: Option<AES128Key>,
|
||||
@ -96,12 +93,10 @@ impl JoinRequest {
|
||||
js_client: None,
|
||||
join_request: None,
|
||||
device: None,
|
||||
device_session: None,
|
||||
application: None,
|
||||
tenant: None,
|
||||
device_profile: None,
|
||||
device_keys: None,
|
||||
dev_addr: None,
|
||||
join_accept: None,
|
||||
device_info: None,
|
||||
relay_rx_info: None,
|
||||
@ -130,7 +125,7 @@ impl JoinRequest {
|
||||
ctx.abort_on_relay_only_comm()?;
|
||||
ctx.log_uplink_frame_set().await?;
|
||||
ctx.abort_on_otaa_is_disabled()?;
|
||||
ctx.get_random_dev_addr()?;
|
||||
ctx.set_random_dev_addr()?;
|
||||
if ctx.js_client.is_some() {
|
||||
// Using join-server
|
||||
ctx.get_join_accept_from_js().await?;
|
||||
@ -141,11 +136,10 @@ impl JoinRequest {
|
||||
ctx.construct_join_accept_and_set_keys()?;
|
||||
}
|
||||
ctx.log_uplink_meta().await?;
|
||||
ctx.create_device_session().await?;
|
||||
ctx.set_device_session().await?;
|
||||
ctx.flush_device_queue().await?;
|
||||
ctx.set_device_mode().await?;
|
||||
ctx.set_dev_addr().await?;
|
||||
ctx.set_join_eui().await?;
|
||||
ctx.update_device().await?;
|
||||
ctx.start_downlink_join_accept_flow().await?;
|
||||
ctx.send_join_event().await?;
|
||||
|
||||
@ -159,12 +153,10 @@ impl JoinRequest {
|
||||
js_client: None,
|
||||
join_request: None,
|
||||
device: None,
|
||||
device_session: None,
|
||||
application: None,
|
||||
tenant: None,
|
||||
device_profile: None,
|
||||
device_keys: None,
|
||||
dev_addr: None,
|
||||
join_accept: None,
|
||||
device_info: None,
|
||||
relay_rx_info: None,
|
||||
@ -183,7 +175,7 @@ impl JoinRequest {
|
||||
ctx.abort_on_device_is_disabled()?;
|
||||
ctx.abort_on_otaa_is_disabled()?;
|
||||
ctx.abort_on_relay_only_comm()?;
|
||||
ctx.get_random_dev_addr()?;
|
||||
ctx.set_random_dev_addr()?;
|
||||
if ctx.js_client.is_some() {
|
||||
// Using join-server
|
||||
ctx.get_join_accept_from_js().await?;
|
||||
@ -193,11 +185,10 @@ impl JoinRequest {
|
||||
ctx.validate_dev_nonce_and_get_device_keys().await?;
|
||||
ctx.construct_join_accept_and_set_keys()?;
|
||||
}
|
||||
ctx.create_device_session().await?;
|
||||
ctx.set_device_session().await?;
|
||||
ctx.flush_device_queue().await?;
|
||||
ctx.set_device_mode().await?;
|
||||
ctx.set_dev_addr().await?;
|
||||
ctx.set_join_eui().await?;
|
||||
ctx.update_device().await?;
|
||||
ctx.start_downlink_join_accept_flow_relayed().await?;
|
||||
ctx.send_join_event().await?;
|
||||
|
||||
@ -514,8 +505,10 @@ impl JoinRequest {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_random_dev_addr(&mut self) -> Result<()> {
|
||||
self.dev_addr = Some(get_random_dev_addr());
|
||||
fn set_random_dev_addr(&mut self) -> Result<()> {
|
||||
trace!("Setting random DevAddr");
|
||||
let d = self.device.as_mut().unwrap();
|
||||
d.dev_addr = Some(get_random_dev_addr());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -550,7 +543,7 @@ impl JoinRequest {
|
||||
mac_version: dp.mac_version.to_string(),
|
||||
phy_payload: phy_b,
|
||||
dev_eui: dev.dev_eui.to_vec(),
|
||||
dev_addr: self.dev_addr.unwrap().to_vec(),
|
||||
dev_addr: dev.dev_addr.unwrap().to_vec(),
|
||||
dl_settings: dl_settings.to_le_bytes()?.to_vec(),
|
||||
rx_delay: region_network.rx1_delay,
|
||||
cf_list: match region_conf.get_cf_list(dp.mac_version) {
|
||||
@ -619,6 +612,7 @@ impl JoinRequest {
|
||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||
let join_request = self.join_request.as_ref().unwrap();
|
||||
|
||||
let d = self.device.as_ref().unwrap();
|
||||
let dk = self.device_keys.as_mut().unwrap();
|
||||
|
||||
let join_nonce = dk.join_nonce - 1; // this was incremented on validation
|
||||
@ -643,7 +637,7 @@ impl JoinRequest {
|
||||
payload: Payload::JoinAccept(JoinAcceptPayload {
|
||||
join_nonce: join_nonce as u32,
|
||||
home_netid: conf.network.net_id,
|
||||
devaddr: self.dev_addr.unwrap(),
|
||||
devaddr: d.dev_addr.unwrap(),
|
||||
dl_settings: DLSettings {
|
||||
opt_neg,
|
||||
rx2_dr: region_network.rx2_dr,
|
||||
@ -768,21 +762,18 @@ impl JoinRequest {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_device_session(&mut self) -> Result<()> {
|
||||
trace!("Creating device-session");
|
||||
async fn set_device_session(&mut self) -> Result<()> {
|
||||
trace!("Setting device-session");
|
||||
|
||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||
let region_network = config::get_region_network(&self.uplink_frame_set.region_config_id)?;
|
||||
|
||||
let device = self.device.as_ref().unwrap();
|
||||
let device = self.device.as_mut().unwrap();
|
||||
let device_profile = self.device_profile.as_ref().unwrap();
|
||||
let join_request = self.join_request.as_ref().unwrap();
|
||||
|
||||
let mut ds = internal::DeviceSession {
|
||||
region_config_id: self.uplink_frame_set.region_config_id.clone(),
|
||||
dev_eui: device.dev_eui.to_be_bytes().to_vec(),
|
||||
dev_addr: self.dev_addr.unwrap().to_be_bytes().to_vec(),
|
||||
join_eui: join_request.join_eui.to_be_bytes().to_vec(),
|
||||
dev_addr: device.dev_addr.unwrap().to_be_bytes().to_vec(),
|
||||
f_nwk_s_int_key: self.f_nwk_s_int_key.as_ref().unwrap().to_vec(),
|
||||
s_nwk_s_int_key: self.s_nwk_s_int_key.as_ref().unwrap().to_vec(),
|
||||
nwk_s_enc_key: self.nwk_s_enc_key.as_ref().unwrap().to_vec(),
|
||||
@ -847,11 +838,7 @@ impl JoinRequest {
|
||||
None => {}
|
||||
}
|
||||
|
||||
device_session::save(&ds)
|
||||
.await
|
||||
.context("Saving device-session failed")?;
|
||||
|
||||
self.device_session = Some(ds);
|
||||
device.device_session = Some(ds);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -870,30 +857,36 @@ impl JoinRequest {
|
||||
|
||||
async fn set_device_mode(&mut self) -> Result<()> {
|
||||
let dp = self.device_profile.as_ref().unwrap();
|
||||
let device = self.device.as_mut().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
|
||||
// LoRaWAN 1.1 devices send a mac-command when changing to Class-C.
|
||||
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
|
||||
*device = device::set_enabled_class(&device.dev_eui, DeviceClass::C).await?;
|
||||
d.enabled_class = DeviceClass::C;
|
||||
} else {
|
||||
*device = device::set_enabled_class(&device.dev_eui, DeviceClass::A).await?;
|
||||
d.enabled_class = DeviceClass::A;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_dev_addr(&mut self) -> Result<()> {
|
||||
trace!("Setting DevAddr");
|
||||
let dev = self.device.as_mut().unwrap();
|
||||
*dev = device::set_dev_addr(dev.dev_eui, self.dev_addr.unwrap()).await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn update_device(&mut self) -> Result<()> {
|
||||
trace!("Updating device");
|
||||
|
||||
async fn set_join_eui(&mut self) -> Result<()> {
|
||||
trace!("Setting JoinEUI");
|
||||
let dev = self.device.as_mut().unwrap();
|
||||
let req = self.join_request.as_ref().unwrap();
|
||||
let d = self.device.as_mut().unwrap();
|
||||
|
||||
*dev = device::set_join_eui(dev.dev_eui, req.join_eui).await?;
|
||||
*d = device::partial_update(
|
||||
d.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
enabled_class: Some(d.enabled_class),
|
||||
dev_addr: Some(d.dev_addr),
|
||||
secondary_dev_addr: Some(None),
|
||||
join_eui: Some(req.join_eui),
|
||||
device_session: Some(d.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -904,7 +897,6 @@ impl JoinRequest {
|
||||
&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(),
|
||||
)
|
||||
.await?;
|
||||
@ -918,7 +910,6 @@ impl JoinRequest {
|
||||
&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(),
|
||||
)
|
||||
.await?;
|
||||
@ -939,7 +930,7 @@ impl JoinRequest {
|
||||
time: Some(ts.into()),
|
||||
device_info: self.device_info.clone(),
|
||||
relay_rx_info: self.relay_rx_info.clone(),
|
||||
dev_addr: self.dev_addr.as_ref().unwrap().to_string(),
|
||||
dev_addr: dev.dev_addr.unwrap().to_string(),
|
||||
join_server_context: if !self.js_session_key_id.is_empty() {
|
||||
Some(common::JoinServerContext {
|
||||
app_s_key: None,
|
||||
|
@ -10,7 +10,7 @@ use crate::backend::{joinserver, keywrap, roaming};
|
||||
use crate::storage::{
|
||||
application,
|
||||
device::{self, DeviceClass},
|
||||
device_keys, device_profile, device_queue, device_session,
|
||||
device_keys, device_profile, device_queue,
|
||||
error::Error as StorageError,
|
||||
helpers::get_all_device_data,
|
||||
metrics, tenant,
|
||||
@ -28,7 +28,6 @@ pub struct JoinRequest {
|
||||
join_request: Option<lrwn::JoinRequestPayload>,
|
||||
join_accept: Option<lrwn::PhyPayload>,
|
||||
device: Option<device::Device>,
|
||||
device_session: Option<internal::DeviceSession>,
|
||||
js_client: Option<Arc<backend::Client>>,
|
||||
application: Option<application::Application>,
|
||||
tenant: Option<tenant::Tenant>,
|
||||
@ -66,7 +65,6 @@ impl JoinRequest {
|
||||
join_request: None,
|
||||
join_accept: None,
|
||||
device: None,
|
||||
device_session: None,
|
||||
js_client: None,
|
||||
application: None,
|
||||
tenant: None,
|
||||
@ -99,10 +97,9 @@ impl JoinRequest {
|
||||
ctx.construct_join_accept_and_set_keys()?;
|
||||
}
|
||||
ctx.log_uplink_meta().await?;
|
||||
ctx.create_device_session().await?;
|
||||
ctx.set_device_session().await?;
|
||||
ctx.flush_device_queue().await?;
|
||||
ctx.set_device_mode().await?;
|
||||
ctx.set_join_eui().await?;
|
||||
ctx.update_device().await?;
|
||||
ctx.send_join_event().await?;
|
||||
ctx.set_pr_start_ans_payload()?;
|
||||
|
||||
@ -562,21 +559,18 @@ impl JoinRequest {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_device_session(&mut self) -> Result<()> {
|
||||
trace!("Creating device-session");
|
||||
async fn set_device_session(&mut self) -> Result<()> {
|
||||
trace!("Setting device-session");
|
||||
|
||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||
let region_network = config::get_region_network(&self.uplink_frame_set.region_config_id)?;
|
||||
|
||||
let device = self.device.as_ref().unwrap();
|
||||
let device = self.device.as_mut().unwrap();
|
||||
let device_profile = self.device_profile.as_ref().unwrap();
|
||||
let join_request = self.join_request.as_ref().unwrap();
|
||||
|
||||
let mut ds = internal::DeviceSession {
|
||||
region_config_id: self.uplink_frame_set.region_config_id.clone(),
|
||||
dev_eui: device.dev_eui.to_be_bytes().to_vec(),
|
||||
dev_addr: self.dev_addr.unwrap().to_be_bytes().to_vec(),
|
||||
join_eui: join_request.join_eui.to_be_bytes().to_vec(),
|
||||
f_nwk_s_int_key: self.f_nwk_s_int_key.as_ref().unwrap().to_vec(),
|
||||
s_nwk_s_int_key: self.s_nwk_s_int_key.as_ref().unwrap().to_vec(),
|
||||
nwk_s_enc_key: self.nwk_s_enc_key.as_ref().unwrap().to_vec(),
|
||||
@ -627,11 +621,7 @@ impl JoinRequest {
|
||||
}
|
||||
}
|
||||
|
||||
device_session::save(&ds)
|
||||
.await
|
||||
.context("Saving device-session failed")?;
|
||||
|
||||
self.device_session = Some(ds);
|
||||
device.device_session = Some(ds);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -648,25 +638,37 @@ impl JoinRequest {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_device_mode(&mut self) -> Result<()> {
|
||||
async fn update_device(&mut self) -> Result<()> {
|
||||
trace!("Updating device");
|
||||
let dp = self.device_profile.as_ref().unwrap();
|
||||
let device = self.device.as_mut().unwrap();
|
||||
let ds = self
|
||||
.device
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.device_session
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
// LoRaWAN 1.1 devices send a mac-command when changing to Class-C.
|
||||
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
|
||||
*device = device::set_enabled_class(&device.dev_eui, DeviceClass::C).await?;
|
||||
} else {
|
||||
*device = device::set_enabled_class(&device.dev_eui, DeviceClass::A).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_join_eui(&mut self) -> Result<()> {
|
||||
trace!("Setting JoinEUI");
|
||||
let dev = self.device.as_mut().unwrap();
|
||||
let req = self.join_request.as_ref().unwrap();
|
||||
|
||||
*dev = device::set_join_eui(dev.dev_eui, req.join_eui).await?;
|
||||
self.device = Some(
|
||||
device::partial_update(
|
||||
self.device.as_ref().unwrap().dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(Some(ds.clone())),
|
||||
join_eui: Some(self.join_request.as_ref().unwrap().join_eui),
|
||||
dev_addr: Some(Some(self.dev_addr.unwrap())),
|
||||
secondary_dev_addr: Some(None),
|
||||
enabled_class: Some(
|
||||
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
|
||||
DeviceClass::C
|
||||
} else {
|
||||
DeviceClass::A
|
||||
},
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -714,7 +716,9 @@ impl JoinRequest {
|
||||
|
||||
fn set_pr_start_ans_payload(&mut self) -> Result<()> {
|
||||
trace!("Setting PRStartAnsPayload");
|
||||
let ds = self.device_session.as_ref().unwrap();
|
||||
let d = self.device.as_ref().unwrap();
|
||||
let ds = d.get_device_session()?;
|
||||
|
||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||
|
||||
let sender_id = NetID::from_slice(&self.pr_start_req.base.sender_id)?;
|
||||
@ -753,8 +757,8 @@ impl JoinRequest {
|
||||
.base
|
||||
.to_base_payload_result(backend::ResultCode::Success, ""),
|
||||
phy_payload: self.join_accept.as_ref().unwrap().to_vec()?,
|
||||
dev_eui: ds.dev_eui.clone(),
|
||||
dev_addr: ds.dev_addr.clone(),
|
||||
dev_eui: d.dev_eui.to_vec(),
|
||||
dev_addr: d.get_dev_addr()?.to_vec(),
|
||||
lifetime: if pr_lifetime.is_zero() {
|
||||
None
|
||||
} else {
|
||||
@ -764,7 +768,7 @@ impl JoinRequest {
|
||||
nwk_s_key,
|
||||
f_cnt_up: Some(0),
|
||||
dl_meta_data: Some(backend::DLMetaData {
|
||||
dev_eui: ds.dev_eui.clone(),
|
||||
dev_eui: d.dev_eui.to_vec(),
|
||||
dl_freq_1: Some(rx1_freq as f64 / 1_000_000.0),
|
||||
dl_freq_2: Some(rx2_freq as f64 / 1_000_000.0),
|
||||
rx_delay_1: Some(rx1_delay.as_secs() as usize),
|
||||
|
@ -21,7 +21,7 @@ use crate::storage::{
|
||||
device, device_profile, error::Error as StorageError, gateway, get_async_redis_conn, redis_key,
|
||||
};
|
||||
use crate::stream;
|
||||
use chirpstack_api::{common, gw, internal, stream as stream_pb};
|
||||
use chirpstack_api::{common, gw, stream as stream_pb};
|
||||
use lrwn::region::CommonName;
|
||||
use lrwn::{ForwardUplinkReq, MType, PhyPayload, EUI64};
|
||||
|
||||
@ -75,7 +75,6 @@ pub struct RelayContext {
|
||||
pub req: ForwardUplinkReq,
|
||||
pub device: device::Device,
|
||||
pub device_profile: device_profile::DeviceProfile,
|
||||
pub device_session: internal::DeviceSession,
|
||||
pub must_ack: bool,
|
||||
pub must_send_downlink: bool,
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ pkgs.mkShell {
|
||||
pkgs.perl
|
||||
pkgs.cmake
|
||||
pkgs.clang
|
||||
pkgs.postgresql # needed to build the diesel cli utility
|
||||
];
|
||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/${pkgs.llvmPackages.libclang.version}/include";
|
||||
|
Reference in New Issue
Block a user